欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

MongoDB入门

程序员文章站 2022-05-08 14:54:13
...

mongo数据模型

文档与集合

文档是mongo的核心概念,本质是是一种BSON(Binary JSON)数据,BSON是一种类JSON的二进制数据,可以在为JSON基础上添加了一些新的数据类型,包括日期、Int32、Int64,常被作为数据存储和网络数据交换的格式,缺点是空间利用率并不理想(存在键名的冗余信息)。BSON由多组键值对组成,具有轻量性、可遍历性和高效性,其中可遍历性是mongo将其作为数据存储的主要原因

mongo和关系型数据库可以按照下图进行理解:

MongoDB入门

mongo文档的注意事项 

  1. 写操作的原子性限制在文档级别

  2. 单个文档的存储大小限制为16MB(防止读取大文档时对内存和网络造成压力)

  3. mongo会尽量保持插入文档时键值对的顺序,但是更新操作可能会改变字段的顺序,因为可能会导致文档内存的重新分配

文档键的命名

  1. _id是系统保留关键字,默认主键,集合中唯一,一旦生成不可更改

  2. 键名不能包含\0或者空字符,因为mongo依靠此判断键的结尾

  3. 键名不能以$开头,不能包含.

  4. 键名区分大小写

  5. _开头的键通常是保留的,并不推荐使用

集合

把一组相关文档放到一起就组成了集合。我们可以将集合类比成RDB中的表,文档则是一条条的记录。在mongo中集合也是*的,一个集合中的文档可以是不同的。mongo提供了一些特殊功能的集合:capped collection(固定集合),system.indexessystem.namespaces(这两个用于存储集合的元数据)。集合名字中的.表示子集合。

数据库

多个文档组成集合,多个集合组成数据库。一个mongo instance可以承载多个数据库,每个DB拥有不同的权限,不同的DB也可以分散在DB的各个目录(启动时需要directoryperdb指定),数据库名最终会变成文件系统中的文件,mongo中保留了一些数据库:adminlocal(本地数据库,不会被复制)、config(本地使用保存分片信息)。

命名空间

把数据库名添加到集合名前面,中间用点号连接,得到集合的全限定名就是namespace,例如:test_db.testcollection
需要说明的是,.还可以出现在集合名字中,例如:test_db.blog.poststest_db.blog.authors,我们可以将postsauthors看做是blog集合的子集合

mongo数据类型

mongo支持的数据类型非常丰富:
MongoDB入门
如下的命令db.collection.find({name:{$type:2}})将查找name字段为String类型的文档。由于mongoshell默认是javascript shell,所以我们插入的数据是double类型,如果需要插入int类型可以使用NumberInt(x).
ObjectId可以保证在分布式环境下同一集合唯一,由24个16进制字符组成,总共需要12字节存储空间:
MongoDB入门

> x = ObjectId()
ObjectId("562f741336aeb42b3fdef2da")
> x.getTimestamp()
ISODate("2015-10-27T12:54:43Z")
> x.valueOf()
562f741336aeb42b3fdef2da

mongo内嵌文档

文档可以作为键的值,这样的文档叫做内嵌文档。内嵌文档使得数据不用保存成扁平结构的键值对,从而使数据组织方式更加自然

mongo基本操作

关闭mongoDB

# 切换到admin,执行db.shutdownServer()
use admin
db.shutdownServer()

# 先查看mongo的pid,然后kill此进程
ps -ef | grep mongodb
kill 25414

mongo常用命令

show dbs          # 查看数据库
show collections  # 查看所有的集合
use test          # 切换数据库(需要的时候自己创建)
db.dropDatabase() # 删除当前使用的数据库

向mongo中写入数据

mongo中的collection类似于RDBMS中的表,写入数据的语法:

db.collection_name.insert(BSON)

插入的时候如果没有指定_id字段,则mongo会自动生成一个id,我们也可以自己指定_id,该字段默认创建索引。除了insert我们也可以使用save,当文档不存在时save函数调用insert执行插入,当文档存在时save函数执行update进行更新操作

我们可以使用js语法插入多条数据

for(var i = 0;i < 10;i++) db.test_collection.insert({x:i})

我们也可以使用bulk函数将多个数据的更新(增删改)操作放到一个待执行的列表中批量执行,bulk分为顺序(一个操作失败剩下的不会执行)执行的bulk和并行(随机性)执行的bulk。

# 初始化bulk
# 初始化bulk
var bulk = db.test_collection.initializeUnorderedBulkOp() # 并行bulk
#db.test_collection.initializeOrderedBulkOp()             # 顺序bulk

# 向bulk中添加数据更新操作
bulk.insert({name:"xiaomi",age:1});
bulk.insert({name:"huawei",age:2});
bulk.insert({name:"zte",age:3});

# 执行更新
var result = bulk.execute();
printjson(result);

# 测试执行结果
var cursor = db.test_collection.find({});
print(cursor.toArray());

在mongo中查找数据

查找数据使用find()命令

db.colletion_name.find() # 查询条件为空返回所有文档
db.collection.findOne()  # 返回第一条数据

我们可以对find操作产生的文档进行额外的统计

db.test_collection.find().count()      # 计数
db.test_collection.find().skip(3)      # 跳过前3条数据
db.test_collection.find().limit(5)     # 仅返回前5条数据
db.test_collection.find().sort({x:-1}) # 以x字段降序排列,如果指定1则表示升序排列

以上的操作可以连缀进行,例如:

db.test_collection.find().skip(3).limit(4).sort({x:1})

更新mongo数据

更新数据使用update,需要指明查询条件和更新条件,例如:

更新一个文档对象

db.test_collection.update({x:2},{x:'hello'})

注意:与RDBMS不同的是mongo默认更新一条数据,这样设计的初衷是防止不小心更新多条数据。如果需要更新多条数据参见更新多条数据

更新一个文档的部分字段

db.test_collection.insert({x:100,y:200})         # { "x" : 100, "y" : 200 }
db.test_collection.update({x:100},{$set:{y:-1}}) # 仅更新y字段,{ "x" : 100, "y" : -1 }

# 如果使用以下的语法将会完全替代原来的数据
db.test_collection.update({x:100},{y:-1})        # { "y" : -1 }

更新一个文档,如果文档不存在则创建之

只需要指定update的第三个参数为true即可。

db.test_collection.update({x:1111},{x:9999},true) # 数据{ x:1111 }原先并不存在

批量更新文档

实际上更新数据的update语句还有<span id="multi-update">第4个参数</span>表示更新多条数据。

for(var i = 0;i < 3;i++) db.test_collection.insert({x:888})  # 写入3个文档
db.test_collection.update({x:888},{$set:{x:999}},false,true) # 更新3个文档

更新修改器

更新修改器是原子的高效部分更新。

$inc 修改器

例如网站计数器的文档结构如下:

{
    "_id":ObjectId("5631a71da4d39f72ef3bbaa4"),
    "url":"www.url.com",
    "pageviews":52
}

当有人访问页面的时候可以用$inc修改器增加pageviews的值:

db.web_analytics.update({"url":"www.url.com"},{$inc:{"pageviews":1}})

$set修改器

设置一个键的值,若不存在自动创建,可以用于更新部分字段或者增加键值对。
MongoDB入门

 数组修改器

如果指定的键已经存在,$push会向已有的数组末尾追加一个元素,否则创建一个新的数组。我们也可以使用$addToSet来避免重复。
MongoDB入门

删除数据

数据的删除使用remove,与查找数据相比,删除数据有以下不同:

  • 必须传递参数

  • 默认删除所有匹配数据

db.test_collection.remove({x:999})
db.test_collection.remove({}) # 清空集合中的所有文档(保留集合本身和索引)

清除某个集合中(table)

db.test_collection.drop()
show tables                 # 看不到任何数据

切记:删除操作是永久性的,不可撤销,不可恢复。

索引

查看索引

db.test_collection.getIndexes() # 查看索引

MongoDB入门

创建索引

MongoDB入门

索引的分类

  • _id索引,默认插入数据的时候建立索引

  • 单键索引,该类型的索引需要使用ensureIndex创建

  • 多键索引,和单键索引不同的是值可以是多个记录,例如数组

  • 复合索引,查询条件不止一个。
    MongoDB入门

  • 过期索引(TTL),一段时间后过期,索引过期后相应的数据也被删除。比较适合存储一些一段时间后失效的数据,例如用户登录信息,用户日志。过期索引有以下的限制:

    • 过期索引指定字段的值必须是指定的时间类型,例如ISODate或ISODate数组,不能使用timestamp否则不能自动删除

    • 如果指定了ISODate数组,则按照最小的时间进行删除

    • 过期索引不能是复合索引,原因很简单:不能同时指定多个过期时间

    • 删除的时间是不精确的(删除进程由后台每60s执行一次,而且每次删除也需要时间)
      MongoDB入门

  • 全文索引(文本索引)。一个数据集中只能创建一个全文索引。对字符串或者字符串数组创建全文可搜索的索引,适用情况:

{ author:"", title:"", article:"" }
# 建立方法
db.arcicles.ensureIndex({key:"text"})
db.arcicles.ensureIndex({key_1:"text",key_2:"text"})
db.arcicles.ensureIndex({"$**":"text"})

# 使用全文索引进行查询
db.arcicles.find({$text:{$search:"coffee"}})
db.arcicles.find({$text:{$search:"aa bb cc"}})  # 包含aa或bb或cc
db.arcicles.find({$text:{$search:"aa bb -cc"}}) # 包含aa或bb 但不包含cc
db.arcicles.find({$text:{$search:"\"aa\" \"bb\" \"cc\""}}) # 同时包含aa、bb、cc的需要将字符串包裹起来

全文检索相似度查询$meta,写在查询条件后面可以返回结果的相似度,与sort一起使用可以达到很好的实用效果。我们可以模拟一个类似于百度的搜索

{score:{$meta:"textScore"}}

MongoDB入门

  • 全文索引的限制:

    • 每次查询只能使用一次$text

    • $text不能出现在$nor查询中

    • 查询中使用了$text,hint(可强制指定索引)将不再起作用

    • mongoDB全文检索不支持中文

  • 索引属性:
    MongoDB入门

地理位置索引

将一些点的位置存储在mongo中,创建索引后可以按照位置来找到其他点。地理位置索引有2个子分类:

  • 2D索引,平面

  • 2Dsphere索引,球面

2D索引

查找方式:

  • 查找距离某个点一定距离的点

  • 查找包含在某个区域内的点

2D索引的创建方式

db.collection.ensureIndex({"w":"2d"})

位置的表示方式是经纬度[经度,纬度],取值范围经度[-180,180],纬度[-90,90]

2D索引查询的方式

  • $near:查询距离某个点最近的点(默认返回100个,可以使用$maxDistance进行限制)

  • $geoWithin:查询某个形状内的点,mongo中形状的表现方式有:

    • $box。矩形,使用{$box:[[<x1>,<y1>],[<x2>,<y2>]]}表示。

    • $center。圆形,使用{$center:[[<x>,<y>],r]} 表示.

    • $polygon。多边形,使用{$polygon:[[<x1>,<y1>],[<x2>,<y2>],[<x3>,<y3>]]}表示。

# 创建2D索引
db.location_collection.ensureIndex({"w":"2d"})

# 插入数据
db.location_collection.insert({w:[1,1]})
db.location_collection.insert({w:[1,2]})
db.location_collection.insert({w:[3,2]})
db.location_collection.insert({w:[90,50]})

# 使用$near查询距离(1,1)最近的点
db.location_collection.find({w:{$near:[1,1]}}) # 返回4条数据

# 使用$maxDistance进行限制
db.location_collection.find({w:{$near:[1,1],$maxDistance:10}}) # 返回前3条数据

# 查询位于[0,0]-[3,3]矩形内的点
db.location_collection.find({w:{$geoWithin:{$box:[[0,0],[3,3]]}}}) # 返回前3条数据

# 查询圆内的点
db.location_collection.find({w:{$geoWithin:{$center:[[0,0],2]}}}) # 1条结果

# 位于多边形内的点
db.location_collection.find({w:{$geoWithin:{$polygon:[[0,0],[1,1],[2,5]]}}}) # 2条

geoNear查询

该查询可以看做是$near查询的进化版,geoNear查询使用runCommand命令

db.runCommand({
    geoNear:<collection>,
    near:[x,y],
    minDistance:(对2D索引无效),
    maxDistance:
    num:
    ...
})

MongoDB入门

2Dsphere索引

2Dsphere索引是球面地理位置索引,创建方式:

db.collection.ensureIndex({"w":"2dsphere"})

位置的表示方式不再是经度和纬度,而是一种名为GeoJSON的数据结构,GeoJSON用来描述一个点、一条直线、多边形等形状。格式如下:

{type:"",coordinates:[<coordinates>]}

2Dsphere索引的查询方式和2d索引的查询方式类似,支持$minDistance$maxDistance

索引的mongo性能分析

索引的利弊

  • 利:加快索引相关的查询

  • 弊:增加磁盘空间的消耗,降低写入性能

评判索引构建情况

  1. mongostat工具
    mongostat是查看mongo运行状态的程序,我们可以使用mongostat --help查看mongo的使用帮助,flushes表名将内存中的数据刷入硬盘(越高则性能越低);faults换页,越高性能越低;ids miss查询没有命中索引的比例;qr|qw读写队列,较高时性能明显下降。ids miss的提高会导致qr的提高导致明显的性能问题。

  2. profile集合
    profile集合是mongo的慢操作日志
    MongoDB入门
    当mongo的级别为0的时候不会记录任何操作,2则记录所有的操作。我们的操作就是被记录在system.profile集合中。
    MongoDB入门
    profile主要用于测试阶段,生产环境中并不推荐profile

  3. mongo日志
    在mongo的配置文件mongod.conf中可以配置verbose来记录日志的详细程度。

  4. explain分析
    在特定查询的时候可以指定explain()来查看特定查询的情况:
    MongoDB入门

mongo安全

mongo开启权限认证:

mongod.conf中配置auth = true开启权限认证。之后重启mongo,查看mongo日志,搜索auth发现已经有了鉴权启动的标志。

创建用户

mongo2.6之后使用createUser命令创建用户,之前的版本只有简化的addUsercreateUser接收的参数:

{
    user:"<name>",
    pwd:"<cleartext password>",
    customData:{<any information>},
    roles:[{role:"<role>",db:"<database>"}]
}

mongo角色

mongo的角色类型有很多,mongo内建的类型有:

  • 数据库角色:readreadWritedbAdmindbOwneruserAdmin

  • 集群角色:clusterAdminclusterManger...

  • 备份角色:backuprestore

  • 特殊权限:DBAdminAnyDatabaseReadAnyDatabase...

  • mongo内置的特殊角色:root--system
    MongoDB入门
    使用mongo -u test_user -ptest_user_admin命令登录mongo。
    MongoDB入门

我们可以使用createRole命令创建自定义角色。
MongoDB入门