MongoDB
一丶介绍
简介
MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数据库系统。在高负载的情况下,添加更多的节点,可以保证服务器性能。MongoDB 旨在为WEB应用提供可扩展的高性能数据存储解决方案。MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。
API:https://docs.mongodb.com/manual/reference/command/shardCollection/
主要特点
- MongoDB 是一个面向文档存储的数据库,操作起来比较简单和容易。
- 你可以在MongoDB记录中设置任何属性的索引 (如:FirstName="Sameer",Address="8Road")来实现更快的排序。
- 如果负载的增加(需要更多的存储空间和更强的处理能力) ,它可以分布在计算机网络中的其他节点上这就是所谓的分片。
- Mongo支持丰富的查询表达式。查询指令使用JSON形式的标记,可轻易查询文档中内嵌的对象及数组。
- MongoDb 使用update()命令可以实现替换完成的文档(数据)或者一些指定的数据字
- Mongodb中的Map/reduce主要是用来对数据进行批量处理和聚合操作
- Map和Reduce。Map函数调用emit(key,value)遍历集合中所有的记录,将key与value传给Reduce函数进行处理。
- Map函数和Reduce函数是使用Javascript编写的,并可以通过db.runCommand或mapreduce命令来执行MapReduce操作。
- GridFS是MongoDB中的一个内置功能,可以用于存放大量小文件。
- MongoDB允许在服务端执行脚本,可以用Javascript编写某个函数,直接在服务端执行,也可以把函数的定义存储在服务端,下次直接调用即可。
- MongoDB支持各种编程语言:RUBY,PYTHON,JAVA,C++,PHP,C#等多种语言。
与Redis进行比较
二丶基本操作
创建数据库
MongoDB 中默认的数据库为 test,如果你没有创建新的数据库,集合将存放在 test 数据库中。
删除数据库
创建集合
语法:db.createCollection(name, options)
name:要创建集合的名称
options:可选参数,指定有关内存的大小及索引的选项
字段 | 类型 | 描述 |
capped | 布尔 | (可选)如果为 true,则创建固定集合。固定集合是指有着固定大小的集合,当达到最大值时,它会自动覆盖最早的文档。当该值为 true 时,必须指定 size 参数。 |
autoIndexId | 布尔 | (可选)如为 true,自动在 _id 字段创建索引。默认为 false。 |
Size | 数值 | (可选)为固定集合指定一个最大值(以字节计)。 如果 capped 为 true,也需要指定该字段。 |
max | 数值 | (可选)指定固定集合中包含文档的最大数量。 |
在插入文档时,MongoDB 首先检查固定集合的 size 字段,然后检查 max 字段。
删除集合
语法:db.collection.drop()
插入文档
语法:db.COLLECTION_NAME.insert(document)
MongoDB 使用 insert() 或 save() 方法向集合中插入文档
插入文档你也可以使用 db.col.save(document) 命令。如果不指定 _id 字段 save() 方法类似于 insert() 方法。如果指定 _id 字段,则会更新该 _id 的数据。
更新文档
① update()
语法:db.collection.update(
<query>,
<update>,
{
upsert: <boolean>,
multi: <boolean>,
writeConcern: <document>
})
query | update的查询条件,类似sql update查询内where后面的。 |
update | update的对象和一些更新的操作符(如$,$inc...)等,也可以理解为sql update查询内set后面的 |
upsert | 可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入。 |
multi | 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。 |
writeConcern | 可选,抛出异常的级别。 |
② save()
语法:db.collection.save(
<document>,
{
writeConcern: <document>
})
document | 文档数据。 |
writeConcern | 可选,抛出异常的级别。 |
save() 方法通过传入的文档来替换已有文档。
删除文档
语法:db.collection.remove(
<query>,
<justOne>)
query | (可选)删除的文档的条件。 |
justOne | (可选)如果设为 true 或 1,则只删除一个文档,如果不设置该参数,或使用默认值 false,则删除所有匹配条件的文档。 |
writeConcern | (可选)抛出异常的级别。 |
查询文档
条件操作符
语法:db.collection.find(query, projection)
query | 可选,使用查询操作符指定查询条件 |
projection | 可选,使用投影操作符指定返回的键。查询时返回文档中所有键值, 只需省略该参数即可(默认省略)。 |
等于 | {<key>:<value>} |
小于 | {<key>:{$lt:<value>}} |
小于或等于 | {<key>:{$lte:<value>}} |
大于 | {<key>:{$gt:<value>}} |
大于或等于 | {<key>:{$gte:<value>}} |
不等于 | {<key>:{$ne:<value>}} |
MongoDB 的 find() 方法可以传入多个键(key),每个键(key)以逗号隔开,即SQL 的 AND 条件。
or查询
$type操作符
$type操作符是基于BSON类型来检索集合中匹配的数据类型,并返回结果。
limit(),skip()
语法:db.COLLECTION_NAME.find().limit(NUMBER).skip(NUMBER)
limit():取多少条
skip():从哪一条开始取(从0开始)
sort()
语法:db.COLLECTION_NAME.find().sort({KEY:1})
sort() 方法可以通过参数指定排序的字段,并使用 1 和 -1 来指定排序的方式,其中 1 为升序排列,而 -1 是用于降序排列。
MongoDB 聚合
语法:db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION)
主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果。有点类似sql语句中的 count(*)。
$sum | 计算总和 | db.collection.aggregate([{$group :{_id : "$by_user", num_tutorial : {$sum : "$likes"}}}]) |
$avg | 平均值 | db.collection.aggregate([{$group : {_id : "$by_user", num_tutorial : {$avg : "$likes"}}}]) |
$min | 取最小值 |
|
$max | 取最大值 |
|
$push |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
三丶创建索引
语法:db.collection.createIndex(keys, options)
嵌套文档以及文档中数组建立索引:test:{
num:1
info:{
city:"cd",
....
},
lisUser:[
{
name:"jack",
age:19
}
]
}
假如要在city和age字段建立索引
db.collection.createIndex(test.city,"");
db.collection.createIndex(test.age, -1);
建立组合索引:
db.getCollection("test").createIndex({
num: -1,
test.city."",
test.age. -1
}, {
name: "num_city_age"
});
遵循最左前缀原则
Key 值为你要创建的索引字段,1 为指定按升序创建索引,如果你想按降序来创建索引指定为 -1。
①:如果要查询的内容都是最近的,那建立索引就用倒序,如果要通盘查询那就用正序。
②:比如说一个数据集合查询占的比较多就用索引,如果查询少而是插入数据比较多就不用建立索引。因为:当没有索引的时候,插入数据的时候MongoDB会在内存中分配出一块空间,用来存放数据。当有索引的时候在插入数据之后还会给自动添加一个索引,浪费了时间。
③:不是所有数据都要建立索引,要在恰当并且需要的地方建立才是最好的。
④:大数量的排序工作时可以考虑创建索引。
1、查看集合索引
db.col.getIndexes()
2、查看集合索引大小
db.col.totalIndexSize()
3、删除集合所有索引
db.col.dropIndexes()
4、删除集合指定索引
db.col.dropIndex("索引名称")
注:
MongoDB中索引是大小写敏感的。
当更新对象是,只有在索引上的这些key发生变化时才会更新。着极大地提高了性能。当对象增长了或者必须移动时,所有的索引必须更新,这回很慢 。
索引信息会保存在system.indexes 集合中,运行 db.system.indexes.find() 能够看到这些示例数据。
索引的字段的大小有最大限制,目前接近800 bytes. 可在大于这个值的字段上建立索引是可以的,但是该字段不会被索引,这种限制在以后的版本中可能被移除。
索引的性能
索引使得可以通过关键字段获取数据,能够使得快速查询和更新数据。
但是,必须注意的是,索引也会在插入和删除的时候增加一些系统的负担。往集合中插入数据的时候,索引的字段必须加入到B-Tree中去,因此,索引适合建立在读远多于写的数据集上,对于写入频繁的集合,在某些情况下,索引反而有副作用。不过大多数集合都是读频繁的集合,所以集合在大多数情况下是有用的
使用sort()而不需要索引
如果数据集合比较小(通常小于4M),使用sort()而不需要建立索引就能够返回数据。在这种情况下,做好联合使用limit()和sort()。
使用explain()解析
后面来补.....
四丶MongoDB复制集
① 简介
一组Mongodb复制集,就是一组mongod进程,这些进程维护同一个数据集合。复制集提供了数据冗余和高等级的可靠性,这是生产部署的基础。
② 目的
保证数据在生产部署时的冗余和可靠性,通过在不同的机器上保存副本来保证数据的不会因为单点损坏而丢失。能够随时应对数据丢失、机器损坏带来的风险。
换一句话来说,还能提高读取能力,用户的读取服务器和写入服务器在不同的地方,而且,由不同的服务器为不同的用户提供服务,提高整个系统的负载。
③ 原理
主节点记录在其上的所有操作oplog,从节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作,从而保证从节点的数据与主节点一致。
主服务器很重要,包含了所有的改变操作(写)的日志。但是副本服务器集群包含有所有的主服务器数据,因此当主服务器挂掉了,就会在副本服务器上重新选取一个成为主服务器。
每个复制集还有一个仲裁者,仲裁者不存储数据,只是负责通过心跳包来确认集群中集合的数量,并在主服务器选举的时候作为仲裁决定结果。
基本的架构由3台服务器组成,一个三成员的复制集,由三个有数据,或者两个有数据,一个作为仲裁者。
一个主库;两个从库。
主库宕机时,这两个从库都可以被选为主库。
当主库宕机后,两个从库都会进行竞选,其中一个变为主库,当原主库恢复后,作为从库加入当前的复制集群即可。
两个正常的主从,及一台仲裁节点
一个从库,可以在选举中成为主库
一个aribiter节点,在选举中,只进行投票,不能成为主库
由于arbiter节点没有复制数据,因此这个架构中仅提供一个完整的数据副本。arbiter节点只需要更少的资源,代价是更有限的冗余和容错。
当主库宕机时,将会选择从库成为主,主库修复后,将其加入到现有的复制集群中即可。
主节点选举:(对于大多数那里概念有点混淆)
复制集通过replSetInitiate命令(或mongo shell的rs.initiate())进行初始化,初始化后各个成员间开始发送心跳消息,并发起Priamry选举操作,获得『大多数』成员投票支持的节点,会成为Primary,其余节点成为Secondary。
大多数的定义:
当复制集内存活成员数量不足大多数时,整个复制集将无法选举出Primary,复制集将无法提供写服务,处于只读状态。
当复制集节点为3个时,主节点允许发生故障且剩余两个节点由于满足大多数原则所以会自动选出一台新的主节点。如果新选出来的主节点又发送故障那么不好意思此时最后一个从节点并不会成为主节点。
当复制集节点为4个时,主节点允许发送故障且剩余三个节点由于满足大多数原则所以会自动选出一台新的主节点。如果新选出来的主节点又发送故障那么不好意思此时剩余两个节点由于不满足大多数原则所以此复制集就不会有主节点了。
通常建议将复制集成员数量设置为奇数,从上表可以看出3个节点和4个节点的复制集都只能容忍1个节点失效,从『服务可用性』的角度看,其效果是一样的。(但无疑4个节点能提供更可靠的数据存储)
④ windows环境配置复制集
三个复制集,一个primary,一个secondary,一个arbiter
在安装目录下的data下新建db,log,conf文件夹分别用来保存数据,日志,和配置文件。
在conf文件下新建rs1.conf,rs2.conf,rs3.conf
配置:
# 数据库文件存储位置 dbpath = D:\Develop\mongoDB\data\db\rs1 # log文件存储位置 logpath = D:\Develop\mongoDB\data\log\rs1\rs1.log # 使用追加的方式写日志 #logappend = true # 是否以守护进程方式运行 #fork = true # 端口号 port = 27018 # 是否启用认证 #auth = true # 集群的私钥的完整路径,只对于Replica Set 架构有效(noauth = true时不用配置此项) #keyFile = /usr/local/mongodb/mongodb-keyfile # diaglog选项 0=off 1=W 2=R 3=both 7=W+some reads #diaglog = 0 # 设置oplog的大小(MB) #oplogSize=2048 # 启用日志选项,MongoDB的数据操作将会写入到journal文件夹的文件里 journal=true # 设置副本集名称 replSet=rs0
|
然后在db下新建rs1,rs2,rs3文件夹用来存放数据
在bin目录下启动服务
mongod --config D:\Develop\mongoDB\data\conf\rs1.conf
其他也是同样...
进入服务rs1 mongo --27018
执行命令
五丶MongoDB分片
① 简介
分片(sharding)是MongoDB用来将大型集合分割到不同服务器(或者说一个集群)上所采用的方法。尽管分片起源于关系型数据库分区,但MongoDB分片完全又是另一回事。和MySQL分区方案相比,MongoDB的最大区别在于它几乎能自动完成所有事情,只要告诉MongoDB要分配数据,它就能自动维护数据在不同服务器之间的均衡。
② 目的
在Mongodb里面存在另一种集群,就是分片技术,可以满足MongoDB数据量大量增长的需求。
当MongoDB存储海量的数据时,一台机器可能不足以存储数据,也可能不足以提供可接受的读写吞吐量。这时,我们就可以通过在多台机器上分割数据,使得数据库系统能存储和处理更多的数据。
高数据量和吞吐量的数据库应用会对单机的性能造成较大压力,大的查询量会将单机的CPU耗尽,大的数据量对单机的存储压力较大,最终会耗尽系统的内存而将压力转移到磁盘IO上。
为了解决这些问题,有两个基本的方法: 垂直扩展和水平扩展。
垂直扩展:增加更多的CPU和存储资源来扩展容量。
水平扩展:将数据集分布在多个服务器上。水平扩展即分片。
③ 原理
架构:
组件 | 说明 | |
Config Server | 存储集群所有节点、分片数据路由信息。默认需要配置3个Config Server节点。 | |
Mongos |
| |
Mongod | 存储应用数据记录。一般有多个Mongod节点,达到数据分片目的。 |
chunk:
在一个shard server内部,MongoDB还是会把数据分为chunks,每个chunk代表这个shard server内部一部分数据。chunk的产生,会有以下两个用途:
Splitting:当一个chunk的大小超过配置中的chunk size时,MongoDB的后台进程会把这个chunk切分成更小的chunk,从而避免chunk过大的情况
Balancing:在MongoDB中,balancer是一个后台进程,负责chunk的迁移,从而均衡各个shard server的负载,系统初始1个chunk,chunk size默认值64M,生产库上选择适合业务的chunk size是最好的。ongoDB会自动拆分和迁移chunks。
分片集群的数据分布(shard节点)
(1)使用chunk来存储数据
(2)进群搭建完成之后,默认开启一个chunk,大小是64M,
(3)存储需求超过64M,chunk会进行分裂,如果单位时间存储需求很大,设置更大的chunk
(4)chunk会被自动均衡迁移。
chunksize的选择
适合业务的chunksize是最好的。
chunk的分裂和迁移非常消耗IO资源;chunk分裂的时机:在插入和更新,读数据不会分裂。
小的chunksize:数据均衡是迁移速度快,数据分布更均匀。数据分裂频繁,路由节点消耗更多资源。
大的chunksize:数据分裂少。数据块移动集中消耗IO资源。通常100-200M
chunk的分裂及迁移
随着数据的增长,其中的数据大小超过了配置的chunk size,默认是64M,则这个chunk就会分裂成两个。数据的增长会让chunk分裂得越来越多。
这时候,各个shard 上的chunk数量就会不平衡。这时候,mongos中的一个组件balancer 就会执行自动平衡。把chunk从chunk数量最多的shard节点挪动到数量最少的节点。
chunksize的分裂和迁移的影响:
MongoDB 默认的 chunkSize 为64MB,如无特殊需求,建议保持默认值;chunkSize 会直接影响到 chunk 分裂、迁移的行为。
chunkSize 越小,chunk 分裂及迁移越多,数据分布越均衡;反之,chunkSize 越大,chunk 分裂及迁移会更少,但可能导致数据分布不均。
chunkSize 太小,容易出现 jumbo chunk(即shardKey 的某个取值出现频率很高,这些文档只能放到一个 chunk 里,无法再分裂)而无法迁移;chunkSize 越大,则可能出现 chunk 内文档数太多(chunk 内文档数不能超过 250000 )而无法迁移。
chunk 自动分裂只会在数据写入时触发,所以如果将 chunkSize 改小,系统需要一定的时间来将 chunk 分裂到指定的大小。
chunk 只会分裂,不会合并,所以即使将 chunkSize 改大,现有的 chunk 数量不会减少,但 chunk 大小会随着写入不断增长,直到达到目标大小。
分片键:
MongoDB中数据的分片是、以集合为基本单位的,集合中的数据通过片键(Shard key)被分成多部分。其实片键就是在集合中选一个键,用该键的值作为数据拆分的依据。
片键必须是一个索引,通过sh.shardCollection加会自动创建索引(前提是此集合不存在的情况下)。
一个自增的片键对写入和数据均匀分布就不是很好,因为自增的片键总会在一个分片上写入,后续达到某个阀值可能会写到别的分片。但是按照片键查询会非常高效。
随机片键对数据的均匀分布效果很好。注意尽量避免在多个分片上进行查询。在所有分片上查询,mongos会对结果进行归并排序。
对集合进行分片时,你需要选择一个片键,片键是每条记录都必须包含的,且建立了索引的单个字段或复合字段,MongoDB按照片键将数据划分到不同的数据块中,并将数据块均衡地分布到所有分片中。
注意:
分片键是不可变。
分片键必须有索引。
分片键大小限制512bytes。
分片键用于路由查询。
MongoDB不接受已进行collection级分片的collection上插入无分片键的文档(也 不支持空值插入)
以范围为基础的分片:
Sharded Cluster支持将单个集合的数据分散存储在多shard上,用户可以指定根据集合内文档的某个字段即shard key来进行范围分片(range sharding)。
对于基于范围的分片,MongoDB按照片键的范围把数据分成不同部分。
假设有一个数字的片键:想象一个从负无穷到正无穷的直线,每一个片键的值都在直线上画了一个点。MongoDB把这条直线划分为更短的不重叠的片段,并称之为数据块,每个数据块包含了片键在一定范围内的数据。在使用片键做范围划分的系统中,拥有”相近”片键的文档很可能存储在同一个数据块中,因此也会存储在同一个分片中。
基于哈希的分片:
分片过程中利用哈希索引作为分片的单个键,且哈希分片的片键只能使用一个字段,而基于哈希片键最大的好处就是保证数据在各个节点分布基本均匀。
对于基于哈希的分片,MongoDB计算一个字段的哈希值,并用这个哈希值来创建数据块。在使用基于哈希分片的系统中,拥有”相近”片键的文档很可能不会存储在同一个数据块中,因此数据的分离性更好一些。
Hash分片与范围分片互补,能将文档随机的分散到各个chunk,充分的扩展写能力,弥补了范围分片的不足,但不能高效的服务范围查询,所有的范围查询要分发到后端所有的Shard才能找出满足条件的文档。
分片键的选择:
递增的sharding key
数据文件挪动小。(优势)
因为数据文件递增,所以会把insert的写IO永久放在最后一片上,造成最后一片的写热点。同时,随着最后一片的数据量增大,将不断的发生迁移至之前的片上。
④ windows环境搭建分片集群
2个分片里面分别三个复制集
配置路由服务器:
配置文件
配置分片:
激活数据库(sh.enanleSharding(“dbName”)):
配置集合分片键: