Data Models
-
原子性仅保证在文档级别,对单个文档的操作是原子操作
-
GridFS:解决BSON文档16M大小限制, GridFS用fs.files 集合存储元数据,记录chunk与文件映射关系,用fs.chunks集合记录文件片段,fs.chunks有{ files_id: 1, n: 1 }复合唯一索引,记录属于哪个文件第几个片段
-
Keyword Search:比如字段topics : [ "whaling" , "allegory" , "revenge"]是一个列表,对其建一个multi-key index,createIndex( { topics: 1 } ),则可以快速检索
Administration
-
Capped Collections:固定大小,先进先被覆盖,多用于日志,或小量数据。缺陷:1.如果update其中某个文档,不能体积(占用空间)变大,否则失败2.不能删除,只能追加直到覆盖3.不能对固定大小集合分片
-
TTL collection(同redis过期key):TTL索引只能单字段,不支持联合索引以及列表等多值索引。
-
Locking Performance:主要关注locks.deadlockCount死锁数,globalLock.currentQueue.total排队等候锁数
-
Memory Use:mem.resident内存使用量,如果超过物理内存,将使用磁盘。mem.mapped是mongod进程内存使用量,如果大于物理内存,必有page fault(磁盘开销大)
-
Database Profiling:三个级别:0无记录,1记录慢查询,2记录所有日志。slowOpThresholdMs设置慢查询阀值,db.setProfilingLevel(0,20)参数0是级别,20是阀值。查看慢查询日志db.system.profile.find().limit(10).sort( { ts : -1 } ).pretty()
-
explain:同MySQL的explain,cursor.explain()返回执行计划,从而得知索引使用情况。
-
查询优化:类似关系型和数据库,1建合适索引提高检索效率2.limit返回结果的记录数3.Projections 只返回必须的字段4. hint() 强制使用某个索引5.inc计数加减比客户端计算加减结果update要高效5.指定timeout:db.collection.find( { 条件} ).maxTimeMS(30)
-
备份恢复:1文件系统快照2.自带工具dump,restore,import,export
CRUD
-
查询仅在一个集合内进行,结果默认无序,除非显式sort()
-
update默认只更新符合条件的第一个,multi=true更新所有符合条件的。默认没有符合条件的不更新,upsert=true,没有则insert
-
remove删除所有满足条件的文档
-
Write Concern
-
Unacknowledged({w:0}):mongod不保证写入成功,客户端也不在乎
-
Acknowledged({w:1}):mongod成功将写操作写入内存,客户端能感知写入内存的成败
-
Journaled({w:1, J:true}):mongod不仅将写操作应用于内存,还写入journal(磁盘日志),断电可恢复。客户端能感知写入内存以及磁盘的成败
-
对于replica set,默认只确认写操作在primary的结果,可以指定为至少写操作传递给几个slave(防止primary挂slave未及时同步),{ w: 3, wtimeout: 5000 }表示至少2个slave同步了该写操作或者超时5秒
-
-
isolated
类似redis事务muilt,isolated 可以保证多个文档被连续修改,不被其他操作穿插,但是遇到错误不回滚。要么全部成功,要么出错退出,出错之前的修改保留。缺陷:不支持shard
-
Bulk 批量写入:无序速度快,后一条插入不必等待前一条成功;有序必须一条一条成功往下执行,慢
var bulk = db.items.initializeUnorderedBulkOp(); var bulk = db.items.initializeUnorderedBulkOp(); bulk.insert( { _id: 1, item: "abc123", status: "A", soldQty: 5000 } ); bulk.insert( { _id: 2, item: "abc456", status: "A", soldQty: 150 } ); bulk.insert( { _id: 3, item: "abc789", status: "P", soldQty: 0 } ); bulk.execute( { w: "majority", wtimeout: 5000 } );
-
WiredTiger 存储引擎
文档级别细粒度锁,journal操作日志,zlib压缩减少磁盘消耗
-
tailable cursors
默认迭代到末尾,游标关闭;tailable cursors迭代到末尾,阻塞,直到新的文档被insert后移到新插入的文档上。类似 tail -f
tailable cursors不使用索引,效率不高;对于有索引集合db.collection.find( { indexedField: { $gt:
<lastvalue>
} } )获取最新文档,lastvalue表示最后迭代出的索引字段值
Aggregation
-
Early Filtering(尽早过滤)
$match, $limit, and $skip尽早减少进入流程的数据量,project投影减少不必要的字段
-
先match后sort减少sort工作量
-
先limit后skip.例如{ $skip: 10 },{ $limit: 5 }会被自动优化成{ $limit: 15 },{ $skip: 10 }尽早确定limit
-
合并优化:sort紧接着limit,那么limit将被合并到sort步骤一次完成(取top K)。limit紧接着limit,简单合并(取小者),一次到位。连续skip合并{ $skip: 5 },{ $skip: 2 }合并成{ $skip: 7 }。连续match做逻辑&运算一次match完毕。
-
缺陷:返回结果不能超过一个文档大小限制16M,此缺陷可以通过返回一个结果集的游标避免。整个pipeline各个阶段内存使用不得超过100M,此缺陷可以设置allowDiskUse避免
index
-
Single Field Indexes
shard cluster中如果shard key不是 id,务必确保id唯一性,否则出错
-
Compound Indexes
前缀规则:mongo只能使用复合索引的前缀或者整个符合索引。例如有复合索引{"a":1, "b":1, "c":1},前缀有a和a,b两个,查询a=1,查询a=1, b=1,查询a=1, b=1,c=1都能使用索引,查询a=1,c=1则只能使用a的索引
-
Multikey Indexes
复合索引中最多只能一个字段是多值字段(如数组)。多值索引不能当作shard key。哈希索引不能是多值字段。
集合中有如下文档:
{ _id: 1, item: "ABC", ratings: [ 2, 9 ] },
{ _id: 2, item: "XYZ", ratings: [ 4, 3 ] }
find( { ratings : { $elemMatch: { $gt: 3, $lt: 6 } } } )表示ratings字段至少有一个值同时满足大于3,小于6,那么不仅能使用ratings字段的索引,而且还能使用该索引的交集,两个条件单独看(-inf,6)和(3,inf),直接计算索引交集(3,6)再筛选文档
find( { ratings : { $gte: 3, $lte: 6 } } )表示ratings字段至少有一个值要么大于3,要么小于6,那么只能普通使用该索引,不能使用索引交集
-
Text Indexes
文本索引字段可以使字符串或者字符串数组。db.collection.createIndex( { field: "text" } )创建索引。一个集合最多只能有一个文本索引(可以一次索引多个字段,可以复合文本索引,但只能与简单类型组成复合索引)。weights属性用来给复合文本索引指定每个字段的权值。db.collection.createIndex( { "$**": "text" } )用来创建全文索引,所有字段的字符串值都会被索引到。使用$text才能使用文本索引。使用$text查询器时不能使用hint(.文本索引不支持排序。文本索引的时空成本都很高。
-
TTL Indexs
TTL索引是特殊single-field索引,用来过期后删除过期key,仅适用于date类型或date数组,且不能是_id字段.例如db.collection.createIndex( { "lastModifiedDate": 1 }, { expireAfterSeconds: 3600 } )指定expireAfterSeconds即可创建TTL索引。
如果索引字段是date数组,以日期最早的为准,即尽早过期策略。如果字段类型不符合要求,则永不过期。如果该文档缺失该索引字段,也永不过期。一个后台线程60秒为周期,周期性扫描过期文档并删除。对于副本集,扫描线程只工作在primary节点。
capped collection不支持TTL索引,因为capped collection不支持删除。TTL索引不得与其他索引共同建立在一个字段上。
-
Sparse Indexes
如果某文档改索引字段缺失,普通索引当作null处理,稀疏索引直接忽略此文档。
默认情况下,索引建立时,整个数据库被阻塞,不可读不可写。如果创建索引时指定{background: true},则不会阻塞数据库,但不能做数据库管理操作,比如删除集合。
-
Index Intersection
两个独立字段的索引(或者索引前缀)可以直接计算交集后迅速筛选文档。如{a:1}{b:1}两个索引对于查询find( { a: "aa", b: { $gt: 1 } } )就会使用索引交集,explain()中可以看到ANDSORTED或者ANDHASH阶段。
索引交集和联合索引的比较:如果有联合索引{a:1, b:1}则查询find({b:1})或者sort(b:-1)时联合索引无效,因为不满足前缀规则。但这个时候两个单独索引a,b就能被使用到。
索引交集和排序:必须find使用的索引字段和sort使用的索引字段有交集,才能用到索引交集。比如find({a:1}).sort({b:1})就无法用到ab的单独索引的索引交集,因为两个操作针对的字段毫无关联。
对于内嵌文档的联合索引,比如文档
{ _id: 1, item: "ABC", ratings: [ { score: { q1: 2, q2: 5 } }, { score: { q1: 8, q2: 4 } } ] }
创建索引createIndex( { "ratings.score.q1": 1, "ratings.score.q2": 1 } ),由于二者共享field path:ratings.score,所以查询时ratings.score必须在$elemMatch上才能用上索引边界的交集。
find( { ratings: { $elemMatch: { 'score.q1': 2, 'score.q2': 8 } } } )
不对find( { 'ratings.score': { $elemMatch: { 'q1': 2, 'q2': 8 } } } )
对,$elemMatch作用在共享path:ratings.score上 -
Replica Set建索引
逐个停止secondary, 以standalone模式启动,建索引,再停止,再以secondary身份启动,连接到副本集,更新到与primary同步。
前提是:oplog足够大,等secondary建完索引还能追上primary
对于主节点,要么后台建索引,要么rs.stepDown()促使主节点变为从节点,副本集重新选举主节点,然后对于该旧的主节点(现在变为从节点)采取上面相同办法
提示:mongo2.6+从节点也可以后台建索引,所以无论主从全部后台建索引,如createIndex( { a: 1 }, { background: true } )
db.collection.totalIndexSize()确保索引不超过内存大小。
replica set
-
3种角色:主,从,仲裁.仲裁节点不保留数据,仅仅参与选举,防止选举僵持。
从节点可以指定priority 0,仅仅保留数据,不会被选举成主节点。
hidden从节点:必须priority 0,特性:不会对client可见,hidden从节点保留数据,不会被选举成主节点,但是可能参与对其他的节点的投票。
延迟从节点:必须priority 0,必须 hidden,参与对其他节点的投票。用于防止主节点人工误操作(删除数据),从节点立即同步,导致副本集数据丢失。
-
部署策略
确保投票节点数目为奇数,如果是偶数,加入仲裁借点。
物理分布上避免两个数据中心拥有相等数目的节点,选取节点数目多的为主数据中心
最多50节点,7个投票节点,其他非投票节点vates:0
避免回滚:主节点接收写操作后,宕机,从节点未及时同步该写操作。接着选举出新的主节点后,原先主节点变为从节点,此时,原先的主节点就将该写操作回滚,放在数据目录的rollback目录下。可以设置write concern为majority
read preferences:写只能在主节点,读默认为主节点,可以设置"primaryPreferred", "secondary", "secondaryPreferred", "nearest"等。如果在从节点读取数据,可能读到脏数据。比如:一次写操作,还未扩散到majority,但已经有个别小部分从节点已经同步了写操作,那么在这些从节点就能读到尚未持久化的数据。
-
read preference实现
1.采集个节点信息 2.如果指定tag,按tag过滤 3.选择最近的节点 4.选择距离最近节点一些不太远的节点(ping响应在指定时间内) 5.在这些节点中随机挑选
2.请求记忆:会尽量选择上一次用过的节点,除非发现该节点IOException,或者该请求线程死亡,或者read preference/primary发生变化
-
同步
1.initial sync: Clones all databases and build _id index, then apply change to data set, using oplog, last build all other index
-
调整replica-set
cfg = rs.conf() cfg.members[0].priority = 0 不能成为primary cfg.members[0].hidden = true +隐藏 cfg.members[0].slaveDelay = 3600 +延迟 cfg.members[1].priority = 2 rs.reconfig(cfg)
-
状态检查rs.status()
连接到primary节点,rs.printSlaveReplicationInfo()获取每个second同步延迟
连接到任一成员,rs.printReplicationInfo()获取oplog大小
-
local数据库不会被同步
local.startup_log(capped collection):每次启动插入一条日志
local.system.replset:存放replca-set配置
local.oplog.rs(capped collection):replica-set操作日志
Sharding
-
基本介绍
shards是普通mongod进程,存储数据; Routers是mongos进程,负责请求转发与结果收集;Config Server是mongod进程,存储分片信息。每个数据库有个primary shard,存储那些没有分片的集合。sh.status()查看分片集信息。
Balancing:balancer后台进程不停迁移数据块,使每个shard节点数据量尽量均衡
-
分片keys
shard keys一旦使用,不可更改。shard keys不可以是 multikey index,不可以是text index,不可以超过512字节
keys应该足够离散化,易于划分,
-
Chunk Migration(数据迁移)balancer发送moveChunk命令给source shard
source shard执行内部的moveChunk命令,执行期间,继续负责此chunk的读写
destination shard建立索引,并且开始拷贝该chunk的数据,拷贝结束后,启动一个同步进程,去同步复制期间内,source shard产生的changes
完全同步后,destination shard负责更新config server,更新此chunk的信息
source shard等到同步结束以及没有游标指向该chunk后,删除该chunk
-
管理
设置balancer活动的时间:
db.settings.update( { _id: "balancer" }, { $set: { activeWindow : { start: "23:00", stop: "6:00" } } }, { upsert: true } )
其他时间不会因为balancer影响性能修改Chunk Size:连接mongos,use config;db.settings.save( { _id:"chunksize", value: } );
强制某个字段unique:1.当作shard key 2.用额外一个集合保持其唯一性3.使用uuid等自身唯一的机制