
MongoDB实践总结.ppt
74页Mongodb实践总结20122012年年3 3月月目录目录n NoSQL数据库介绍n MongoDB 简介n 数据文件及内存n 索引最佳实践n 备份及恢复n Sharding机制n 监控及诊断n 测试及总结NoSQLNoSQL数据库介绍数据库介绍随着互联网WEB2.0网站的兴起,传统关系型数据库力不从心n 数据库高并发读写的需求l数据库并发负载非常高,往往每秒数万次读写请求,磁盘IO瓶颈n 海量数据的高效率访问的需求l对数亿甚至数十亿的记录高效查询n 高可扩展性和高可用性的需求l7*24小时高可用,Failover,易扩展NoSQLNoSQL数据库介绍数据库介绍对于互联网海量数据来说,许多SQL特性无用武之地n 数据的一致性需求l很多场景对读一致性要求较低,事务系统成为高负载下的负担n 数据的读写绝对实时性需求lFeed/微博系统,发送一条微博并不需要马上被粉丝读到n 复杂SQL查询,表关联需求l多数业务只是单表查询或主键关联,SQL Parser大量消耗CPU资源NoSQLNoSQL数据库介绍数据库介绍传统SQL数据库时代对大数据的处理n 单表单库时代:用户不停的增长、数据量增大导致压力过大lReplication及主从分离n 分表分库时代:按业务key分片到不同的库,通常按取模算法l增加维护成本,不停的重复劳动l没有完美的Sharding Framework(Hivedb,限制Order/Join)NoSQLNoSQL数据库介绍数据库介绍n 需要的存储:高性能、分布式、易扩展n Nosql = Not Only SQL 根据海量数据特点补充关系型数据库的不足NoSQLNoSQL数据库介绍数据库介绍类型类型部分代表部分代表特点特点key-value存储DynamoTokyo CabinetBerkeley DBVoldemortMemcacheDBRedisLevelDB可以通过key快速查询到其value/list。
value是最灵活及易扩展的数据结构,一般来说,存储不管value的格式,照单全收灵活性和复杂性是双刃剑:一方面可以任意组织文档的结构,另一方面查询需求会变得比较复杂列存储BigTableHbaseCassandraHypertable鉴自Google 的BigTable,按列存储方便存储结构化和半结构化数据,方便数据压缩,对针对某一列或者某几列的查询有非常大的IO优势文档存储MongoDBCouchDBRiak一般用类json的格式存储,存储的内容是文档型的这样也就有有机会对某些字段建立索引,而且可以实现关系数据库的某些复杂查询功能图存储Neo4jFlockDBHyperGraphDB数据并非对等的,关系型的存储或者键值对的存储,可能都不是最好的存储方式,图存储是图形关系的最佳存储NoSQLNoSQL数据库介绍数据库介绍CAP理论介绍n C:Consistency一致性,任何一个读操作总是能读取到之前完成的写操作结果n A:Availability可用性,每一个操作总是能够在确定的时间内返回n P:Tolerance of network Partition分区容忍性,在出现网络分布的情况下,仍然能够满足一致性和可用性n 最多只能同时满足以上两种需求ConsistencyAvailabilityTolerance to Network PartitionsNoSQLNoSQL数据库介绍数据库介绍n I/O的五分钟法则lJim Gray 与 Gianfranco Putzolu 发表了“五分钟法则”,如果一条记录频繁被访问,就应该放到内存里,否则的话就应该待在硬盘上按需要再访问,这个临界点就是五分钟n memory is the new disk,and disk is the new tape.—Jim Grayl对于随机访问,硬盘慢得不可忍受;但如果你把硬盘当成磁带来用,它吞吐连续数据的速率令人震惊;它天生适合用来给以RAM为主的应用做日志n 结论:把随机读写交给RAM,磁盘尽量用作顺序读写及持久化NoSQLNoSQL数据库介绍数据库介绍Consistent Hashingn 传统分布式架构:lHash() mod n or Linear hashingl问题,增删节点或节点失效会引起所有数据重新分配n 一致性哈希架构:l每台Server分成v个虚拟节点,把n台机器的所有虚拟节点哈希映射到一致性哈希环上,增加数据按顺时针寻找第一个vnode节点存储。
节点增删只影响两个节点之间的数据重新分配NoSQLNoSQL数据库介绍数据库介绍Quorum NRW机制n N: 复制的节点数量n R: 成功读操作的最小节点数n W: 成功写操作的最小节点数n 通过,W+R>N 保证强一致性场景举例:n W = 1,R = N,对写操作要求高性能高可用n R = 1,W = N,对读操作要求高性能高可用n W = Q,R = Q where Q = N/2+1 读写之间取得平衡NoSQLNoSQL数据库介绍数据库介绍Vector clockn 一个(node, counter)的列表,记录数据历史版本n 冲突解决:业务逻辑或时间戳NoSQLNoSQL数据库介绍数据库介绍节点间的通信n 传统解决方案:heart-beat,pingn Gossip协议,类P2P病毒式传播,去中心化l通过gossip,每个节点都能知道集群中包含哪些节点,以及这些节点的状态,使得集群中的任何一个节点都可以完成任意key的路由,任意一个节点不可用都不会造成灾难性的后果MongoDB MongoDB 简介简介MongoDB介绍n 一种可扩展的高性能的开源的面向文档的数据库,C++开发n 10gen公司支持与推广,名字从humongous中截取,野心不言而明n 文档完善,官方文档覆盖了所有问题n 一直维持着较快的版本更新速度n 对社区关注度高,积极推动社区活动n 红杉等著名投资机构共融资3000万美金,后续升级及服务有保障MongoDB MongoDB 简介简介MongoDB主要特性n ReplicaSetl Automatic failoverl轻松增删数据结点n Shardingl自动分片l自动数据迁移lmongos自动路由n 数据管理及监控l对单个文档的原子更新l对任意属性可建立索引l数据监控命令工具丰富n 查询支持l丰富的查询语法l查询优化及监控机制完善MongoDB MongoDB 简介简介术语对照术语对照RDBMSMongoDBTableCollectionRow/RecordDocumentColumn nameField nameIndexIndexJoinEmbedding & LinkdingPartitionShardPartition KeySharding KeyMongoDB MongoDB 简介简介BSON及数据类型n Binary JSON是一种类JSON二进制形式的存储格式,支持内嵌的文档对象和数组对象n 基本JSON数据类型:string, integer, boolean, double, null, array 和objectn 包括JSON没有的数据类型: timestamp, object id, binary data, regular expression 和codeMongoDB MongoDB 简介简介ObjectId详解n 被设计为跨机器分布式环境中全局唯一的类型,长度12个字节l0-3这4个字节是时间戳(timestamp)l4-6这3个字节是机器码(machine)l7-8两个字节是进程id(pid)l9-11是程序自增id或随机码(inc/random)n Note1:查询时db.collection.find({_id:”xx”})查不到结果,正确写法是db.collection.find({_id:new ObjectId(“xx”)})n Note2:转成字符串,需要24字节(每字节转成2字节的16进制表示)n Note3:_id保证collection唯一,可以是任意简单类型,不能是array/list,插入或保存为空时则自动生成MongoDB MongoDB 简介简介MongoDB插入操作:n var data={'name':'lisn', 'mobile':13520663641, 'email':'lsn1996@'} use test db.user.insert(data) db.user.save(data) n 两种操作区别:insert操作如果主键(_id)存在则不做任何处理,save操作如果主键(_id)存在则进行记录更新MongoDB MongoDB 简介简介MongoDB更新操作:n 更新语法:db.collection.update( criteria, objNew, upsert, multi )lcriteria:update的查询条件,类似sql update查询内where后面的条件lobjNew:update的对象和一些更新的操作符(如$inc,$set...)等,类似sql update查询内set后面的条件lupsert: 这个参数的意思是,如果不存在update的记录,是否插入,true为插入,默认是false,不插入lmulti: mongodb默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新n Update modifier:l$inc $push $pushAll $pull $pullAll $addToSet $set $unset…都是原子操作,可以混搭MongoDB MongoDB 简介简介MongoDB查询操作:n 查询实例:ldb.user.find({'name':'lisn'})ldb.user.find({'mobile':{$gte:13500000000} }).limit(10)ldb.user.find({}).sort({'mobile':1})ldb.user.find ({'name':'lisn'},{'mobile':1})ldb.user.find().skip(10) ldb.user.count()n Select modifier:l $gt $lte $all $exists $mod $ne $in $nin $or $nor $size $where $type …n 正则表达式:ldb.user.find({‘name’:/l.*n$/i}),查询以l开头以n结尾的记录n Distinct:l返回指定字段的value列表,db.user.distinct('name') MongoDB MongoDB 简介简介Cursor:n 返回结果集的都隐式创建游标,服务端会清理关闭cursorn var cursor = db.user.find(); while (cursor.hasNext()) { var obj = cursor.next(); print(tojson(obj) ) }n 查询一致性:通过对cursor进行snapshot()操作来保证一致性读,此时再进行插入或删除符合条件的记录时,结果集将不再变化,mongodb对小于1M的结果集自动进行snapshotMongoDB MongoDB 简介简介Capped collection:n 性能出色的有着固定大小的集合,以FIFO规则和插入顺序进行 age-out处理n 可以插入及更新,不允许删除,可用drop()删除集合中数据n 创建时需预先指定大小,默认不对_id 创建索引db.createCollection("cappedcoll",{size:10000000,autoIndexId:false})n 普通集合转化:db.runCommand({"convertToCapped":“user",size:10000})n Tailable Cursor作用于capped collection,操作完成后可不关闭,阻塞等待新的集合元素,常用于实现消息队列或数据推送功能数据文件及内存数据文件及内存n MongoDB每个数据库都有自己的独立文件,如果开启 directoryperdb 选项,则每个库的文件会单独放在一个文件夹里n 每个库由一个名字空间文件和多个数据文件组成,名字空间文件以.ns结尾,数据文件按照数据量大小由0开始增长,第一个数据文件为64M,翻倍增长直至2Gn .ns文件保存数据库所有名字空间,每一个集合、索引都将占用一个命名空间以保存元数据。
默认16M最大为2G,启动时可通过nssize参数选项更改ns文件大小以增加可创建集合及索引个数n 使用预分配数据文件方式来保证写入性能的稳定(可用–noprealloc关闭)预分配在后台进行,并且每个预分配的文件都用0进行填充n 数据库启动后会产生一个6bytes的mongd.lock数据文件,用于防止同一个实例多次运行,非正常退出后需要删除此文件才能重启成功数据文件及内存数据文件及内存n 名字空间文件保存的是一个hash table,保存了每个名字空间的信息,其内容为存储信息元数据,包括其大小,块数,第一块位置,最后一块位置,被删除的块的链表以及索引信息n 每个数据文件被分成多个数据块(Extent),用双向链表连接头部包含了块的元数据:自己的位置,上一个和下一个块的位置以及块中第一条和最后一条记录的位置指针剩下部分存储具体的数据记录,也是通过双向链接来进行连接n 每条记录中头部包含记录元数据,如:头部长度,块中的位置偏移,上一条和下一条记录的位置剩余是具体数据内容,如果padding > 1则实际长度大于数据内容本身长度数据文件及内存数据文件及内存n 内存管理采用OS的MMAP内存映射机制,把数据文件映射到内存中,读操作可以起到缓存作用,写操作可由内存转化成顺序磁盘写入n 操作系统虚拟内存管理器会托管所有磁盘操作,这样MongoDB内存管理实现会很简单,缺点是人工没有办法控制占多大内存n 32位主机最大寻址4GB,1GB用于kernel,约0.5GB用于MongoDB Stack空间,剩下约2.5G可用于映射数据文件,超出报”mmap failed with out of memory”n 64位主机最大可映射128TB数据数据文件及内存数据文件及内存n 可通过mongo命令行来监控MongoDB的内存使用情况:db.serverStatus().mem{ "resident" : 22346, "virtual" : 1938524, "mapped" : 962283 }n 相关字段含义:mapped:映射到内存的数据大小virtual: 占用的虚拟内存大小resident:占用物理内存大小索引最佳实践索引最佳实践MongoDB索引简介:n MongoDB的索引跟传统数据库的索引相似,一般的如果在传统数据库中需要建立索引的字段,在MongoDB中也可以建立索引。
MongoDB中_id字段默认已经建立了索引,并且不可删除,Capped Collections例外n 单个collection 最多有64个index,单个query只会选择1个indexn Key大小超过800字节不能被索引,会提示警告n 索引数据结构采用B树,每个节点数据由8k大小的Bucket承载Bucket中的每个key由两部分组成,一部分是排好序的Key Nodes,其中保存子节点及记录值的disk location,另一部分是索引值本身Key Object,两部分通过每个Key Node中 的offset字段相关联索引最佳实践索引最佳实践B树索引示例索引最佳实践索引最佳实践B树索引结构索引最佳实践索引最佳实践B树索引结构索引最佳实践索引最佳实践普通索引:n db.user.ensureIndex({mobile:1}) 其中1表示升序,-1表示降序n db.user.getIndexes()[ { "v" : 1, "key" : { "_id" : 1 }, "ns" : "test.user", "name" : "_id_" }, { "v" : 1, "key" : { "mobile" : 1 }, "ns" : "test.user", "name" : "mobile_1" }]索引最佳实践索引最佳实践唯一索引:n 创建命令:db.users.ensureIndex({name: 1}, {unique: true})n 创建唯一索引后如果插入一条name已经存在的数据会报duplicate key error index错误n 添加dropDups选项可强制将重复的项删掉:db.users.ensureIndex({name: 1}, {unique: true, dropDups: true})索引最佳实践索引最佳实践复合索引:n MongoDB可对多个字段建立复合索引,字段后面的1表示升序,-1表示降序,是用1还是用-1主要是跟排序的时候或指定范围内查询的时候有关的n 示例: db.user.ensureIndex({name:1,mobile:-1}) n 利用复合索引的多值性,可以通过以索引字段开头的组合条件进行查询,例如:以a,b,c三个字段建立索引,则查询可利用索引的查询条件为:a或者a,b或者a,b,c索引最佳实践索引最佳实践多值索引:n 可对array或文档建立索引,生成索引时会分别生成多个索引元素n 多值索引示例:ldb.data.insert({name:“lisn",address:{city: “beijing", state: “haidian" } } );ldb.data.ensureIndex({address:1});ldb.data.find({address:{city: “beijing", state: “haidian"}});ldb.data.find({address:{$gte:{city:“beijing“}}});ldb.data.find({address:{state:“haidian”,city:“beijing”}});无法利用索引索引最佳实践索引最佳实践稀疏索引(1.8+):n Sparse index解决索引文件过大的问题,普通索引会对所有记录建立索引,由于MongoDB的Schema free的特性,每个Collection中的记录属性可能不一致,sparse index只索引包含指定属性的记录,不会对该项值为空的记录进行索引,某些情况下会大大减少索引大小n 示例: db.user.ensureIndex({name:1},{sparse:true})n 限制:目前只能包含一个属性 索引最佳实践索引最佳实践地理位置索引:n 此索引是MongoDB的一大亮点,也是foursquare选择它的原因之一。
原理是建立索引时根据坐标对平面进行多次geohash,近似查询时就可以利用相同前缀的geohash值,mongoDB默认进行26次geohash,hash次数可控n 示例: db.map.ensureIndex({point : “2d”}) db.map.find( {point:[50,50]} ) db.map.find( {point:{$near:[50,50]}}) db.map.find( {point:{$within:{$box:[[40,40],[60,60]]}}} )索引最佳实践索引最佳实践索引管理:n 可通过往system.indexes中插入记录来创建索引,如 var spec = {ns: “test.user", key: {‘name’: 1}, name: ‘name_index’} db.system.indexes.insert(spec, true)n 删除索引命令: db.user.dropIndexes()删除该集合所有索引 db.user.dropIndexes({name:1}) db.runCommand({dropIndexes:'user', index : {name:1}}) db.runCommand({dropIndexes:'user', index :'*'})n 索引重建:db.user.reIndex()索引最佳实践索引最佳实践利用查询计划进行分析:n 使用命令:db.collection.find(query).explain();{ "cursor" : "BasicCursor", "indexBounds" : [ ], "nscanned" : 40, "nscannedObjects" : 40, "n" : 40, "millis" : 0, ….}n 简单说明lcursor: 返回游标类型(BasicCursor 或 BtreeCursor)lnscanned: 被扫描的文档数量,如果过高可能无索引利用ln: 返回的文档数量lmillis: 耗时(毫秒)lindexBounds: 所使用的索引索引最佳实践索引最佳实践优化及注意事项:n Skip函数无法利用索引,对跳过的数据也会进行扫描,如果对页面个数无严格大小要求,可考虑记录时间戳状态进行分页查询n db.user.totalIndexSize()查询索引大小, Memory > Index + hot data 是高性能的保障n 创建索引会添加全局写锁,如果数据比较大,会挂起读写数据库的操作。
可添加background选项,db.user.ensureIndex({name:1}, {background: true})n 不同的数据类型对索引大小影响很大,开发前对字段数据类型的设计很重要备份及恢复备份及恢复MongoDB 可靠性日志n Journal:通过journal日志保证单机的可靠性,启动时可通过选项—journal决定是否开启,会预分配日志空间journal采用group commit机制进行提交,避免频繁的IO操作,默认100ms,可调整n oplog:多机replication机制通过oplog来实现,primary/master向oplog写操作记录,通过集群复制oplog并replay来达到replication目的oplog是capped collection,所以老的日志会被overwrite,如果secondary/slave落后主节点数据量超过oplog大小,会被认为是stale node,将进行全部primary sync操作,所以需要根据网络状况预先设置好oplogSize备份及恢复备份及恢复MongoDB Replication机制n 两种机制:Master-Slave or Replica Set(RS)备份及恢复备份及恢复Replica Set:n 以master-slave复制模式为基础,增加了auto-failover和auto-recovery的功能n 理论上需要三个实例,最小集为2个mongod+1个arbitern 当主节点fail后,集群自动选举新的primaryn 客户端自动切换新的primary节点进行读写操作n 实现读写分离功能,只有primary可写,其余secondary可读n 可以设置被动节点(passive members),该节点永远不会成为primaryn 可以设置延迟节点(delayed nodes),保持与primary一段时间的数据延迟n 可以设置隐藏节点(hidden nodes),此节点不提供线上服务备份及恢复备份及恢复Replica Set配置:n 使用--replSet 参数启动mongod服务,此时log会打印告警信息提示没有初始化集群配置,system.replset config from self or any seed (EMPTYCONFIG)n 在集群中任意节点运行replSetInitiate进行初始化 rs.initiate({ _id : "set", members : [ {_id : 0, host : "172.21.1.197:27018",priority:0}, {_id : 0, host : "172.21.1.197:27017"}, {_id : 1, host : "172.21.1.198:27018"}, {_id : 2, host : "172.21.1.200:27018",arbiterOnly: true} ]}); rs.initiate(config);n rs.status()查看集群当前状态,rs.config()查看集群配置信息备份及恢复备份及恢复Replica Set配置:n 进入primary节点才能进行集群配置修改n 增加新成员:rs.add(“172.21.1.198:27017”),启动后节点会自动全量同步数据并成为secondaryn 删除成员: config = rs.config(); config.members = [config.members[1], config.members[3]]; rs.reconfig(config, {force : true})备份及恢复备份及恢复Replica Set启动配置项详解:命令命令默认默认描述描述arbiterOnlyfalse如果为true,则该节点只负责仲裁,无数据存储buildIndexestrue如果为false,secondary不会被建立索引hiddenfalse如果为true,不会对客户端暴露此节点,通常用于数据备份或数据挖掘priority1.0权重,0则永远不会成为主节点slaveDelay0延迟同步于主节点选项,以秒为单位,如果设置则priority只能为0votes1选举时的投票成员数,通常不变initialSync {}新节点加入时设置同步规则,如指定从哪台主机同步,指定何时开始同步备份及恢复备份及恢复Replica Set选举机制n 各节点每两秒向其他节点发送心跳,每个节点收到心跳后会更新自己的状态映射表,包括:是否有新节点加入,是否有老节点宕机n 如果primary节点状态映射表发生变化则会通过心跳判断自己是否能和其他大多数节点(一半以上的节点)进行通信,如果不能则自动降级为secondaryn 如果secondary节点状态表发生变化则会检测自己是否需要成为primary,它会进行三项检测,如果任意一个是否定的则不会成为primary l是否集群中有其它节点认为自己是 primary l自己是否已经是primary l自己是否有资格成为 primaryn 其他节点收到想要成为primary节点的信息包后会进行三项检测,如果任意一个满足都会投否定票l他们是否已经知识集群中有一个 primary了l他们自己的数据是否比发送节点更新l是否有其它节点的数据比发送节点更新n 如果确认后则节点宣布自己为primary并向其他节点发布,其余节点进行上面的最终一致性确认n 如果第一阶段有赞成有反对则赞成成员30秒内不得投票,发起者不会为primary备份及恢复备份及恢复MongoDB oplog说明:n oplog 在replica set中存在local.oplog.rs中,是一个capped collectionn 可以通过--oplogSize设置大小,默认为剩余磁盘空间的5%n oplog无索引,可通过db.oplog.rs.find().sort({$natural:-1})查看{ "ts" : { "t" : 1330679993000, "i" : 1 }, "h" : NumberLong("-1658698961994549576"), "op" : "i", "ns" : "db0105.info", "o" : { "_id" : ObjectId("4f509190c568791446000000"), "mobile" : 13436105336, "jing" : "006394888", "wei" : "001777359", "direction" : "000", "speed" : "0000", "date" : "110105155959", "distance" : "0044382941", "systime" : 20110105000000, "acc" : "1", "gps" : "0", "incredis" : "0000000000", "increstat" : "1", "locstat" : "2", "terminal" : "0100000000000000" } }n oplog是侵入的,甚至当目前操作时间戳大于要replay的oplog最大时间戳也不会对数据库有任何影响备份及恢复备份及恢复MongoDB oplog详解:n 由五部分组成:{ts:{},h{}, op:{},ns:{},o:{},o2:{} }ts:8字节的时间戳,由4字节unix timestamp + 4字节自增计数表示h: 一个哈希值(2.0+),确保oplog的连续性op:1字节的操作类型ns:操作所在的namespace。
o:操作所对应的document,即当前操作的内容o2:在执行更新操作时o只记录id而o2记录具体内容,仅限于updaten op操作类型“i”:insert操作“u”:update操作“d”:delete操作“c”:db cmd操作"db":声明当前数据库 (其中ns 被设置成为=>数据库名称+ '.')"n": no op,即空操作,其会定期执行以确保时效性 备份及恢复备份及恢复MongoDB增量备份及恢复n 数据量较小可用mongodump和mongorestore来完成数据库的备份和恢复n 可采用备份机 + oplog replay方式实现增量备份比如用备份机实时追踪生产机的oplog,例如一天一次,然后每周一统一进行一次oplog replay操作以此保证一个星期的容错窗口n 也可以用delay node + oplog replay保持一定的容错窗口,注意delay的时间一定不能超过oplog的文件大小,不然会不起作用n Wordnik公司已经把用java开发的增量备份工具开源,使用很方便可到github下载:ShardingSharding机制机制MongoDB Sharding机制介绍n 提供auto-sharding机制,自动对数据进行均衡分片n 可不停机轻松增加新片n 结合Replica Set集群可进行auto-failover,保证高可用ShardingSharding机制机制Sharding中的角色及架构n Sharding中数据分块称为chunk,每个chunk都是Collection中一段连续的数据记录,2.0+中默认大小为64MBn Shard Server: mongod 实例,用于存储实际的数据块n Config Server: mongod 实例,存储了整个Cluster Metadata,其中包括chunk信息n Route Processes: mongos实例,前端路由,客户端由此接入,且让整个集群看上去像单一进程数据库ShardingSharding机制机制MongoDB Sharding配置 n Shard Server:启动时加入--shardsvr选项,注意默认shard server使用27018端口,可利用--port选项更改端口n Config Server:启动时加入--configsvr选项指定为config servern Route Processes:启动时加入--configdb指定多个config server,可指定chunk 块大小,用--chunkSize 选项n 连接到Route进行配置: db.adminCommand({addShard:“set/172.21.1.197:27017,172.21.1.198:27017,172.21.1.200:27017”});添加要分片的集群db.runCommand({enablesharding:“db0101”});设置允许分片的数据库db.runCommand({shardcollection:“db0101.info”,key:{mobile:1}})设置分片的collection和sharding key,Sharding Key系统会自动建立索引ShardingSharding机制机制MongoDB Sharding管理n 查看分片集合信息db.collections.find()n 查看所有存在的分片db.runCommand({listshards:1})n 查看哪些数据库被分片lconfig = db.getSisterDB("config")lconfig.system.namespaces.find()n 查看分片的详细信息ldb.printShardingStatus();lprintShardingStatus(db.getSisterDB("config"),1);ShardingSharding机制机制Sharding平衡机制n MongoDB Sharding 操作有两个关键环节--split & migrate,split是轻量级操作,迁移却需要大量的服务端数据移动n 两大目标,第一保持不同片间的数据平衡,第二尽量最小化不同片间交互的数据块大小n 后台自动运行的任务,客户端不必关心数据如何拆分与迁移n 当数据超过一个chunk时会根据sharding key被split到两个新的chunk中,当两个分片的数据量达到一定差异,平衡器开始工作n 数据迁移的基本单元是chunk,目前版本默认值为64M,可调整大小n 迁移时先把要迁移的数据从磁盘读到内存,再进行迁移,所以会引起热数据被剔除,形成swap颠簸n Sharding前选取良好的Sharding Key非常重要ShardingSharding机制机制Sharding Key选取原则n 避免小基数热点片键,如{server:1},如果server压力是不均衡的,一个大数据量server所有数据会集中在同一chunk中,这样导致chunk不易拆分,当chunk满时会出现整块迁移,{server:1,time:1}是优质方案n 写可扩展性,如果用{time:1}作为片键,导致所有的写操作都会落在最新的一个Chunk上去,这样就形成了一个热点区域选择{server:1,time:1}来作为片键,那么每一个Server上的数据将会写在不同的地方n 避免随机片键,此方案chunk迁移时的数据有可能是冷数据,不在内存中,会不断进行磁盘交换,严重影响性能。
此方案查询也会出现随机性,结果合并会增加mongos负担n 查询隔离,理想的情况是一个查询直接路由到一个实例上去,并且这个实例拥有该次查询的全部数据,如果分散到各个片查询并汇总,会增加mongos主机负载、响应时间与网络成本n 由于Sharding key必须建立索引,有排序查询时以Sharding Key的最左边的字段为依据最佳n 选择片键时尽量能保持良好的数据局部性而又不会导致过度热点的出现,组合片键是一种比较常用的做法ShardingSharding机制机制Manual shardingn 手动split操作,MongoDB平衡机制用同样方式对待split操作,无论手工还是自动数据存在切分: db.runCommand({split:"test.foo",find:{_id:99}})预先切分: db.runCommand({split:"test.foo",middle:{_id:99}})编程切分:for ( var x=97; x<97+26; x++ ) { for( var y=97; y<97+26; y+=6 ) { var prefix = String.fromCharCode(x) + String.fromCharCode(y); db.runCommand({ split :”test.user”, middle : {email : prefix } } ); }}n 手动move chunkdb.adminCommand({moveChunk:"test.user",find:{id:{$gt:13520663641}},to:"shard0001"})n 关闭平衡器: db.settings.update({_id:"balancer"},{$set:{stopped:true}},true)n 定义平衡器窗口: db.settings.update({_id:"balancer"},{$set:{activeWindow:{start:"21:00",stop:"9:00"}}},true)监控及诊断监控及诊断Database Profiler:n Profiling 级别:0-不开启;1-记录慢命令(默认>100ms);2-记录所有命令l设置:db.setProfilingLevel(1,200)l查看:db.getProfilingStatus() { "was" : 1, "slowms" : 200 }n 查询慢命令:db.system.profile.find() { "ts" : ISODate("2012-03-06T02:43:15.992Z"), "op" : "query", "ns" : "db0105.system.profile", "query" : { }, "nscanned" : 3, "nreturned" : 3, "responseLength" : 515, "millis" : 0, "client" : "127.0.0.1", "user" : "“ }ldb.system.profile.find( { millis : { $gt : 50 } } ) 查看大于50ms的命令ldb.system.profile.find().sort({$natural:-1}) 查看最新的profile命令 监控及诊断监控及诊断Profiling部分内容介绍:n ts:该命令在何时执行n millis:该命令执行耗时,以毫秒记n op:本命令的操作信息n query:具体的查询条件n nscanned:本次查询扫描的记录数n nreturned:本次查询实际返回的结果集n responseLength:返回结果大小,以字节记n update:表明这是一个update更新操作n upsert:表明update的upsert参数为truen moved:表明本次update是否进行数据迁移n insert:这是一个insert插入操作n getmore:通常发生结果集比较大的查询时,第一个query返回了部分结果,后续的结果是通过getmore来获取的监控及诊断监控及诊断Mongostat查看实例统计信息监控及诊断监控及诊断Mongostat信息说明:n insert: 每秒插入量n query: 每秒查询量n update: 每秒更新量n delete: 每秒删除量n getmore :每秒的getmore次数n command:每秒命令执行次数n flushes:每秒执行fsync系统调用将数据写入硬盘的次数n mapped:映射总量大小,单位是MBn visze:虚拟内存总量大小,单位是MBn res:物理内存总量大小,单位是MBn faults:每秒的缺页数 (linux only),不要超过100,否则会频繁swap写入n locked:被锁的时间百分比,尽量控制在50%以下吧20以下为佳n idx miss:B树索引缺失率n q t|r|w:全局锁等待队列长度(total|read|write)高并发时队列值会升高n conn:当前连接打开数监控及诊断监控及诊断其他常用监控工具n db.serverStatus()数据库实例各项详细信息n db.stats()数据文件大小及索引大小等信息n iostat/vmstatn MMS/Munion/Nagios…测试及总结测试及总结Replica Set插入测试n 172.21.1.197和172.21.1.198分别部署mongodb存储实例,172.21.1.200部署仲裁实例和测试脚本n 无索引时,写入速度维持平均约12000条/秒,34分钟灌完,2500万条数据,继续写入数据速度下降,当内存用满后速度降低至一万以下n 提前声明一个复合索引,在内存吃满的情况下继续灌数据,刚开始速度维持在8000~9000,随着数据量增大,递减到6000左右测试及总结测试及总结Replica Set 查询测试n单进程,17000次/秒n20个并发,27000次/秒,机器负载4左右,73完成20个100000次查询n50个并发,28900次/秒,机器负载4左右,35秒完成50个20000次查询n200个并发,27900次/秒,机器负载6左右,70秒完成200个10000次查询n500个并发,开始出错,同样处理速度27500次/秒,机器负载6~7测试及总结测试及总结Sharding 测试:n 以mobile为Sharding key,网络环境不稳定,两个片有Replication资源竞争,插入数据很不稳定,2500W数据用时约1.5个小时,写入速度平均3500条/秒Shard1:set1/172.21.1.197:27017,172.21.1.198:27017Shard2:set2/172.21.1.198:27018,172.21.1.197:27018n 以mobile为Sharding key,网络环境不稳定,非RS环境,2500W数据用时33分钟,与单机灌入速度基本一致Shard1:172.21.1.197:27018Shard2:172.21.1.198:27018测试及总结测试及总结关于连接:n Linux默认打开文件数为1024个,为了保证大并发有连接可用建议修改该配置/etc/security/limits.conf中增加nofile配置* soft nofile 10240* hard nofile 10240/etc/pam.d/login中增加配置session required /lib/security/pam_limits.son Linux默认为每个进程分配Stack空间为10M(ulimit -a | grep stack),由于每个连接都会占用一个stack空间,如果连接过多则会消耗大量内存资源,据测试MongoDB最大连接为2000左右,最新版会进行连接优化或手工设置到1M (ulimit -s 1024),同时建议使用连接池使连接不超过1000测试及总结测试及总结关于Shardingn 若非必须,可考虑多库 & 多RS & no-sharding方案,有以下好处l不必担心Route和Config的可靠性问题l冷热数据分库,部署不同的ReplicaSetlReplica Set粒度可控,扩展性可控n 从现有非shard环境迁移是非常痛苦的事情n 若考虑sharding部署一定要认真设计好sharding key,更改很难测试及总结测试及总结关于NUMA问题n NUMA是多核CPU架构中的一种,简单来说就是在多核心CPU中,机器的物理内存是分配给各个核的n 四种访问控制策略缺省(default):总是在本地节点分配(分配在当前进程运行的节点上)绑定(bind):强制分配到指定节点上交叉(interleave):在所有节点或者指定的节点上交织分配优先(preferred):在指定节点上分配,失败则在其他节点上分配n 采用默认策略导致核心只能从指定内存块节点上分配内存,即使总内存还有富余,也会由于当前节点内存不足时产生大量的swap操作n 可在启动前加numactl --interleave=all解决问题测试及总结测试及总结关于文件系统:n 推荐XFS或EXT4,不建议用EXT3,XFS优势l可用空间比高,大文件性能佳,恢复速度快lxfs_growfs 动态扩展空间方便至极n 数据文件建议进行人工预分配,否则在大量写入时预分配文件期间造成短暂不可用,可一次性将数据文件通过脚本建立好lhead -c 2146435072 /dev/zero > db0101.10lcp db0101.10 db0101.11 …测试及总结测试及总结关于维护:n 如果不预设paddingFactor,数据更新操作会导致碎片,时刻关注碎片情况,定期reindex可减少索引碎片n Repair & Compact都能对碎片进行整理lCompact速度快可单独处理collection,但无法释放空间,实用性有限lRepair速度慢但整理彻底,paddingFactor会重置,更新速度下降l需定期对Primary进行repair,P -> stepDown -> repair -> 切回测试及总结测试及总结关于空间及碎片:n 注意数据类型设计,使用短字段做key优于长字段,节省数据及索引空间n 避免碎片的几种方法:l创建文档时设置paddingFactor > 1l字段占位,虽然是反MongDB的,但建议预先设计schemal变长字段放置最后,如String类型lBinData可用于占位,便于扩展测试及总结测试及总结关于FindAndModify及写锁n MongoDB采用的全局写锁一直被诟病, FindAndModify会加写锁以保证原子性n 尽量保证FindAndModify小范围更新n 普通更新采用Read-Update模式,提高索引命中,加热索引纪录n 更新多个文档时循环并一个个更新优于批量更新,减少全局写锁伤害测试及总结测试及总结关于javascript及MapReducen mongodb使用javascript做shell, mongodb的db.eval可以提供给数据驱动与这种javascript shell类似的js接口n 目前mongodb的js引擎使用SpiderMonkey,性能不是十分理想,目前没有改成V8的消息,所以目前数据库的执行逻辑(类似存储过程)暂时不推荐使用MongoDB的js环境实现n 由于JS引擎原因,目前对于Mapreduce模式,Mongodb使用一个单独的进程来跑的,目前开发团队正在设计解决这一问题。
而且目前测试效果来看对于单台机器不是很理想,执行job周期过长。
