NoSQL中排行第一的数据库——MongoDB

前言

本文为站长学习MongoDB的笔记,记录了学习资料上的一些知识点,以及少量个人的观点。由于知识点记录得不是很全面,不建议选择本文为入门资料。

我眼中的MongoDB

我对MongoDB的认知主要有以下几点

  • 直观的看,MongoDB在存储方式上与SQL数据库(在本段中简称SQL)有很大的不同,它是以JSON格式的文档来进行存储的,每个文档对应SQL中的行,每个集合又对应SQL中的表。
  • 在数据类型方面,MongoDB与SQL不同,没有严格的数据类型限制,就好像Python和Java语言的数据类型一样。
  • 与SQL数据库不同,MongoDB没有复杂的关系限制,MongoDB本身不支持外键管理,也不支持join表连接。
  • MongoDB的亮点在于分布式存储,也就是副本集和分片
  • 实在是太容易安装部署了,至少在单机上是这样的,我相信在部署分布式上也比SQL数据库方便。
  • 总体给我的感觉是很自由

参考资料

《MongoDB权威指南第二版》

主要参考资料,有一些描述不详细的地方标上了此书的对应页码。pdf链接: https://pan.baidu.com/s/1kVvDeFd 密码: m4is  建议购买正版阅读。

《MongoDB实战第二版》
后面结合了Ruby进行讲解,就不怎么看了。在外部语言驱动与MongoDB的交互方面,只尝试了使用Python连接。

《MongoDB手册》
官方文档,绝对的权威。连接: https://docs.mongodb.com/master/

简介

MongoDB是为快速开发互联网Web应用而设计的数据库系统。其数据模型和持久化策略就是为了构建高 读/写 吞吐量和高自动灾备伸缩性系统。

MongoDB的数据存储在文档里而不是行里。MongoDB的文档格式基于JSON

键-值存储数据库牺牲丰富的查询功能来换取更简单的伸缩模型。

索引

MongoDB中的索引使用了B-数数据结构
结果集在原集合中所占比例越大,索引的速度就越慢。因为使用索引会进行两次查找,一次是查找索引条目,一次是根据索引指针去查找相应文档。
一般返回集合内30%的文档时,就应该思考是否使用索引了。
创建索引应该查看日志,根据查询的实际情况决定在哪个键上创建索引。
使用

运行db.users.ensureIndex({"username" : 1}) 创建索引

唯一索引

唯一索引可以确保集合中的每一个文档的指定键都有唯一的值,
创建唯一索引:
db.users.ensureIndex({"username" : 1},{"unique" : ture})
超出8KB大小的键不会受到唯一索引的约束。
dropDups会强制建立唯一索引,第一个会被保留,之后包含相同字段的文档会被删除。(注意:删除的是整个文档,而不仅仅是该字段)对于比较重要的数据,千万不要使用“dropDups”

稀疏索引

唯一索引会将null看作值,所以无法将多个缺少唯一索引中的键插入到集合中。
而稀疏索引可以解决这个问题,可以将unique和sparse选项组合在一起使用,允许了多个缺少唯一索引中的键插入到集合中,又保证了存在的字段 是唯一存在的;

 

MongoDB把集合分别存储在不同的数据库中。与传统的SQL数据库不同,MongoDB的数据库只区分集合的命名空间。要查询MongoDB数据库,需要知道存储文档数据的数据库和集合的名字。

增删改查

CRUD(增删改查)范例:
https://docs.mongodb.com/master/crud/

批量插入的限制是16MB

慢查询排查

对于速度较慢的查询可以使用explain()来查看具体原因,对于任意查询,都可以在最后添加一个explain()调用(与调用sort()或者limit()一样,不过explain()必须放在最后) mogodb权威指南P99

注意:
db.users.update({username:"dyc"},{$set:{country:"China"}})

db.users.update({username:"dyc"},{{country:"China"}})
不同。
$set:表示更新文档的country字段,不加“$set”表示用大括号里的内容替换掉整个文档(仅保留_id值)
比如文档
{ "_id" : ObjectId("596eccba4975f09f1bfbb728"), "username" : "dyc" }
使用后者会将name字段删除:
{ "_id" : ObjectId("596eccba4975f09f1bfbb728"), "country" : "haut" }
加了$set:的话,仅仅是在name字段后新增一个字段:
{ "_id" : ObjectId("596eccba4975f09f1bfbb728"), "username" : "dyc", "country" : "haut"}

盖子集合(capped collection)

指有上限的集合,好像盖了盖子一样。
与标准集合不同,因为有固定的大小,意味着一旦盖子集合达到最大上限,后续的插入将会覆盖最先插入的文档数据。
设计目的:当只有最近的数据有价值时,避免造成空间浪费。
创建名为user_actions的盖子集合,大小为16kb:db.createCollection("user_actions",{capped:true,size:16384})
同时限定最大文档数量:db.createCollection("user_actions",{capped:true,size:16384,max:200})

盖子集合不允许正常集合的所有操作。例如,不允许从盖子集合里删除单个文档,也不能增加文档大小。

生存时间集合TTL(time-to-live)

在特定时间后废弃文档数据,通过特殊的索引实现。

首先得有个存放创建时间的time_field字段
db.reviews.insert({
time_field:new Date()
})

创建TTL索引,文档在一小时后删除:
db.reviews.createIndex({time_field:1},{expireAfterSeconds:3600})
该命令会在time_fild字段上创建索引。这个字段会定期检查time_field的值,与当前时间值比较。

注意

不能在已存在索引的字段简历TTL索引。不能在盖子集合里使用TTL索引,因为盖子集合不允许删除单个文档。

范式化和反范式化

范式化就是将数据分散到多个不同的集合,不同集合之间可以互相引用数据。但是MongoDB没有提供连接(join)工具,所以在不同集合之间执行连接查询需要进行多次查询。
反范式化与范式化相反,就是将每个文档所需的数据都嵌入在文档内部。更改信息耗时长,查询信息耗时短。

在实际使用中,需要考虑 更新信息 和 读取信息 哪个更频繁。

优化数据操作

mysql权威指南P162

如果要优化应用程序,首先

  • 必须对读写性能进行评估,以便找到性能瓶颈
  • 对读取操作的优化通常包括正确使用索引,尽可能将所需信息放在单个文档中返回。
  • 对写入操作的优化通常包括减少索引数量以及尽可能提高更新效率。

副本集

在MongoDB中可以创建副本集,可以应对主服务器的宕机、断电等突发状况;亦可以在需要进行高风险时,先在从服务器上进行操作,无误后将其设为主服务器。

MongoDB的复制功能是使用操作日志oplog实现的。

 

可复制集的亮点是可以支持自动化灾备:如果主节点失败,则集群中会选择一个从节点,并自动提升位主节点。当之前的主节点回归时,它会继续作为从节点。

以下教程是在本机的三个端口创建三个副本集(在生产环境中当然不会这么干,但是可以让我们熟悉相关的知识)
命令行输入 mongo --nodb 启动mongoshell但不连接到任何mongod
创建三个服务器的副本集
replicaSet=newReplSetTest({“nodes” : 3})
创建副本集之后,就可以使用复制功能了。

//启动三个mongod进程
replicaSet.starrtSet()

//配置复制功能
replicaSet.initiate()

这三个进程启动后分别运行在31000、31001、31002端口。这三个进程都会把各自的日志输入到当前的shell中,这会让人很混乱。
我们可以打开一个新的shell,运行以下代码连接31000端口的mongod
conn = new Mongo("localhost:31000")
进入到数据库‘test’中
primaryDB=conn1.getDB("test")

在连接到主节点的连接上执行primaryDB.isMaster()命令,可以看到副本集状态

因为备份节点会落后于主节点,可能会导致幻读(两次读取的纪录数量不一致)。
出于保护的目的,默认情况下会拒绝读取请求。

可以通过设置允许当前连接读取:conn2.setSlavaOk()

不能对备份节点执行写操作,备份节点只能够通过复制功能写入数据,不接受客户端的写入请求。

monggodb有“自动故障转移”功能,如果主节点挂了,其中一个备份节点会自动选举位主节点。这时候就能向 被选举位主节点 的节点发送写入请求了。

最后可以执行replicaSet.stopSet()来关闭副本集

为了避免数据造成毁灭性的破坏,可以使用slaveDelay设置一个延迟的备份节点,单位是秒。从延迟备份节点中回复详细操作参考MongoDB权威操作指南P223

在副本集中还有有趣的选举机制,详见MongoDB权威指南P183

 

分片

分片是指将数据拆分,将其分散到不同机器下的过程。
当单台服务器没有足够的内存或者cpu核心处理庞大的负载压力,单个磁盘或者阵列存储庞大的数据集不太实际时,解决方案就是在多台服务器上分散存储这些数据。这在MongoDB里就叫做分片。

简单来讲,分片可以用来:

  • 增加可用RAM
  • 增加可用磁盘空间
  • 减轻单台服务器的负载
  • 处理单个mongod无法承受的吞吐量

分片会增加额外的复杂性和开销,在许多情况下分片集群可能不是最佳解决方案。
所以我们应该对服务器进行监控,对服务器进行分析,提前做好何时分片以及如何分片的计划。
分片有两种情况:存储分布式和负载分布式。

MongoDB支持自动分片,可以使数据库架构对应用程序不可见。也可以简化系统管理。

MongoDB分片集群由以下组件组成:

  • 分片:每个分片包含分片数据的一部分。每个分片可以部署为副本集。
  • mongos:mongos作为查询路由器,提供客户端应用程序和分片集群之间的接口。
  • 配置服务器:相当于集群的大脑,存储集群的元数据(分片包含哪些数据的信息)和配置设置。从MongoDB 3.4开始,配置服务器必须部署为副本集(CSRS)

以下教程是在本机的三个端口创建三个分片(同样,在生产环境中当然不会这么干,但是可以让我们熟悉相关的知识)
命令行输入 mongo --nodb 启动mongoshell但不连接到任何mongod
建立集群:
cluster = new ShardingTest({"shards" : 3,"chuksize" : 1 })
该集群拥有3个分片(3个mongod进程)

三个分片分别运行在30000、30001、30002端口,而mongos会在30999启动。

集群会将日志输入到当前shell中,要想管理集群需要再打开连接mongos:
db=(new Mongo("localhost:30999)).getDB("test")
现在就连接到了集群的test数据库。
客户端不需要知道分片的任何信息,只要有分片存在,就可以向mongos发送请求,它会自动将请求转发到合适的分片上。

运行sh.status()可以看到集群的状态。
sh命令与rs命令很像,除了它是用于分片的。
主分片是为每个数据库随机选择的,所有的数据都会位于主分片上

mongos写入方面的交互与使用单机服务器完全一样

主分片与副本集中的主节点不同。

  • 主分片是指组成分片的整个副本集。
  • 而副本集中的主节点是指副本集中能够处理写请求的单台服务器

对test数据库内的集合分片:sh.enableSharding("test")

对集合分片时要选择一个片键,片键是集合中的一个键,MongoDB会根据这个键拆分数据。
只有被索引过的键才能作为片键。

选择片键应该仔细斟酌,MongoDB权威指南15章有详细介绍
暂时根据“name”对users集合分片:
sh.shardCollection("test.users", {"username" : 1})

在分片之前,可以认为集合是一个单一的数据块,从片键的最小值一直到片键的最大值都位于这个块。
分片依据片键范围将集合才分为多个数据块,再把数据块均衡的分布在不同分片上(不一定按块的顺序)。

通常来说,如果没有在查询中使用片键,mongos就不得不将查询发送到每个分片。

最后可以运行cluster.stop()关闭整个集群。

均衡器

均衡器负责数据的迁移,他会周期性地检查分片间是否存在不平衡,如果存在,则会开始块的迁移。虽然均衡器通常被看作是单一实体,但每个mongos有时也会扮演均衡器的角色。(使用mongos的客户端不会因为数据均衡受到影响)

分片管理

块的数量较多时,sh.status()命令会概述块的状态,而非打印出每个块的相关信息。如需查看所有的块,可使用sh.status(ture)命令。

刷新配置

mongos有时无法从配置服务器正确更新配置。如发现配置有误,mongos的配置过旧或无法找到应有的数据,可使用
db.adminCommand({"flushRouterConfig" : 1})
手动刷新所有缓存。如果仍未能解决问题,再次确认后可重启mongos或mongod进程,以便清楚所有可能的缓存。

监控

可以安装使用MMS对MongoDB进行监控,安装说明:https://mms.10gen.com

备份

对服务器进行备份有很多种方法:

  • 文件系统快照
  • 复制数据文件
  • 使用mongodump

对于分片集群,一般不可能“完美的”对其进行备份,我们更关注分块的备份,即单独备份配置服务器和副本集。

pymongo–MongoDB的Python驱动

pymongo官方文档:http://api.mongodb.com/python/3.4.0/api/pymongo/index.html
在monggodb的语法文档中也有各种语言的范例代码
https://docs.mongodb.com/master/tutorial/insert-documents/

 

 

您可能还喜欢...

发表评论

电子邮件地址不会被公开。 必填项已用*标注