点击下方公众号关注并分享获取 MongoDB 最新资讯
本文福利 · 分享有奖
为了感谢本公众号读者的支持,将本文章转发到朋友圈集赞满10个,就可获得社区定制马克杯套盒1份!
社区根据发送截图时间先后的前 10 名用户进行发放(每个 ID 仅参与一次哦~)
扫描文末二维码或添加小助手微信小芒果(ID:mongoingcom)凭截图领取~
注:活动截至时间:9 月 9 日18:00
一、MongoDB 使用规范与限制
最佳实践
最佳实践
如果单条记录超过 16 M 怎么办?
第一种办法:先处理后存储。可以先做压缩,或者也可以对字符进行先哈希,然后再存储,这样大概率就不会超过 16 MB。
第二种方法:通常来说 16 MB 的记录都可以直接写到文本文件里面,然后再将文件存到 MongoDB GridFS 里面或者先业务层处理后存储。
当然其实我们也有其他的方式来解决类似这样的一个问题这个我们后面再说
实际上为什么有这个限制呢? MongoDB 如果索引字段是数组,那我们可以理解为对每个数组元素创建索引。如果要是多个数组字段建组合索引,就意味着它可能会产生笛卡尔级数据量的索引。所以为了避免这种索引的爆炸性增长,需要对此做了相应的一个限制。
通常你想创建一个 TTL 索引,但创建的时候构建了多个字段的组合索引,那么 TTL 就会失效。
另外需要记住的就是哈希索引只支持单例,这个是在 4.4 之前的一个限制,到后面是做了调整,所以在这里也需要给大家提一下。我们本次分享为大部分内容的前提是小于等于 4.2 版本,主要原因在于 4.4 及其以上的 MongoDB 版本其实有很多企业里面都没有使用。
最佳实践
后台建索引意味着它不会阻塞我们的业务的写,否则的话就会加库级别的锁从而造成业务阻塞。当然还有一个情况就是当我们对同一个集合添加多个索引的时候,建议大家用 createIndexes 批量建索引。因为每次创建索引,实际上可以理解 MongoDB 都会去扫描整个集合,通过扫描整个集合去拿到对应字段的记录,然后将这些记录插入到索引文件里面,使用批量建索引只需要扫描一次,如果分开来建索引那么就需要扫描多次,故批量建索引能大大减少对业务的影响。
这个算是一个经验建议,当然 6 个字段也行。有时候要反过来想,当一个索引有 5 、6 个字段或者 7 、8 个字段的时候,我们应该第一时间要反思我们业务设计是否合理。当然有些业务场景比较特殊也确实有这种必要性,那该放开限制还的放开。
MongoDB 每次在数据插入更新删除的时候,实际上需要同步的去做索引的变更,所以索引越多,其实对于这些变更来说,它的代价就越大。所以,推荐创建尽量少的索引去满足更多的业务查询。
前面说过,对存储数组的字段创建索引,实际上是多数组每个元素创建索引,同时,字段值更新也同步更新索引字段。所以,当数组元素量非常大的时候比如 1 w,5 w,这个时候的索引代价就会比较大。
最佳问答
问:为何副本集只可含有 7 个投票节点?
答:提高选举效率、减少心跳网络代价
虽然 4.2 版本可以修改分片 key 的值,4.4 版本可以调整分片 key 的字段,但依然建议不要轻易调整 key 的值或 key 的字段构成 ,而是建议在初始化分片 key 的时候就评估好。
最佳实践
最佳实践:
为排序添加索引;控制排序数据量
最佳实践:
控制计算数据量
调整 allowDiskUse 允许磁盘排序
最佳实践:
通常建议实际业务每次批量控制在 1000 ~ 5000
默认情况下 bulkWrite 操作有序一般建议设置 false
为避免批量操作导致复制延迟可每批适当 sleep
二、MongoDB 数据类型及容量评估
列表里面是 MongoDB 常见或者不常见的一些数据类型,可能大部分人没有详细地去梳理过,可以去仔细了解一下做参考。(可能有些不是 100% 的准确)
在插入一个文档时如果业务没有显示指定 _id 那么 MongoDB 会为每个文档生成一个ObjectId 类型的 _id 来作为主键其构成如下:
自增性问题: _id 不绝对自增前 4 个字节是时间戳故只能精确到秒同一秒进程 ID 大小决定顺序
唯一性问题:同一个机器同一时间下因为计数器的存在,在 2^24-1 个值内都是唯一性
_id 存在意义:解决分布式场景下唯一性标志问题;复制依赖 _id 方便定位修改的记录
ObjectId 用例:
shell 下获取 _id 的时间戳方式
_id.getTimestamp()
MongoDB 底层以 BSON 存储而按照 BSON Date/Time 数据类型定义日期时间被称为 UTC 时间,故业务计算时需注意与本地时间进行转换:
作为 BSON 特殊的时间戳类型 Timestamp 主要用于 MongoDB 内部使用期主要构成如下:
Timestamp 通常应用与复制中的 oplog,业务层面通常建议使用 Date 类型:
如字段 a 不存在 { } 与 {a:null } 相对等价 a
如果 a 字段不存在以及 a 等于空,它其实相对来说就是等价的。
存在性查询
null值查询
数据类型从小到大比较顺序如下:
验证用例:
db.test.insert([{a:1},{a:MinKey()},{a:new Date()},{a:Timestamp()},{a:[1]},{a:null},{a:NumberLong(1)}])db.test.find({},{_id:0}).sort({a:1})
MongoDB 因其包含了非常规数据类型故对容量的评估不能参照关系型数据库评估方式
怎么去预估写入量?
实际上特别简单,不用去计算它每个字段的字节数或者有多少索引,每个索引它的字段对应的字节数,直接拿真实的环境或者跟线上环境类似的测试环境直接模拟写数据,然后直接去查看对应的一个数据大小就可以。
show dbsadmin 0.000GBtest 43.398GB //(storageSize+ indexSize) = du -sh testlocal 9.794GBdb.stats(){"db" : "test","collections" : 5,"views" : 0,"objects" : 276054256, //所有集合的文档数 dataSize/avgObjSize"avgObjSize" : 190.81693790296063, //dataSize/objects (bytes)" dataSize" : 52675827825, //未压缩的数据大小不含索引(bytes)"storageSize" : 12666482688, //存储引擎分配存储数据大小(bytes)"numExtents" : 0,"indexes" : 10,"indexSize" : 33932251136, //所有集合索引(压缩)大小储(bytes)"ok" : 1}
三、集合数据类型及合法性校验
对于 MongoDB 来说为什么要做数据类型及合法性校验?曾经在线上库我们看到如下图片的类似数据,看看 boo_id 存储的数据,各种类型数据都可以写入且没有任何问题。
Validation 相关概念
validator + 查询逻辑操作符
(除 $near、$nearSphere、$text、$where):定义具体的校验规则
validationLevel:定义了插入更新记录时应用校验规则的严格程度
validationAction:定义了当数据不满足校验规则的时候的具体动作
Validation 使用限制
原因:避免系统内部操作无法正常写入系统集合而导致不可预料的问题
validation 的使用
db.createCollection( "validate_test",{ validator: { $and:[{ phone: { $type: "long" } },{ email: { $regex: /@mongodb\.com$/ } },{ status: { $in: [ "good", "bad" ] } }]},validationLevel: "moderate",validationAction: "error"} )db.validate_test.insert({phone:"123456789",email:"andy@mongodb.com",status:"good"})
Validation 属性修改
关闭校验
db.runCommand( {collMod: "validate_test",validationLevel: "off"} )db.validate_test.insert({phone:"000", status:"A"})
将校验级别修改为严格模式
db.runCommand( {collMod: "validate_test",validationLevel: "strict"} );
Validation 属性修改
修改校验之后的动作为告警
db.runCommand( {collMod: "validate_test",validationAction: "warn"} )
验证
db.validate_test.insert({phone:"000", status:"A"})
查看属性修改结果
db.getCollectionInfos({"name":"validate_test"})
MongoDB 3.6 增加 $jsonSchema 操作符 进一步增强了文档校验功能
db.createCollection("person", {validator: {$jsonSchema: {bsonType: "object",required: [ "name", "age", "sex" ],properties: {name: {bsonType: "string",description: "must be a string and is required"},age: {bsonType: "int",minimum: 0,maximum: 125,exclusiveMaximum: false},sex: {enum: [ "MAN", "WOMAN", null ]}}}}})
使用 collMod 修改 $jsonSchema 对应的校验规则
db.runCommand( {collMod: "person",validator: {$jsonSchema: { bsonType: "object",required: [ "name", "age", "sex" ]properties: {name: {bsonType: ["string","int"]},age: {bsonType: "int",minimum: 0,maximum: 125,exclusiveMaximum: false,},sex: {enum: [ "MAN", "WOMAN", null ]}}}},validationLevel: "moderate"} )
Variety 一个开源的 js 脚本集,提供了丰富的数据校验功能
功能特点:
Variety 用例:
对集合 users 进行分析
mongo 127.0.0.1:27017/test --eval "var collection = 'users'" variety.js
按指定条件进行分析
mongo 127.0.0.1:27017/test --eval "var collection = 'users', query = {'name':'Tom'}" variety.js
分析文档最大深度及指定输出格式
db.users.insert({name:"Walter", someNestedObject:{a:{b:{c:{d:{e:1}}}}}}); mongo127.0.0.1:27017/test --eval "var collection = 'users', maxDepth = 3,outputFormat='ascii'" variety.js
创建一个集合 test 并且使用 $jsonSchema 定义校验规则
规则如下:
{"student_id": NumberLong("100"),"student_name": "1234567890","score": 0}
Q A
直播时设置 Q A 环节,让用户与嘉宾讲师可以更好的沟通,帮助解决相关应该场景等问题,助力于快速上手 MongoDB,规避常见使用问题。
让我们一起看看大家都有哪些问题吧!
向上滑动阅览对话
插入或更新导致索引记录超过1024 字节时默认报错失败,请至少提出一种解决方案规避此问题?
采用 hash 索引即可
如何理解分片 Key 及其值为何不允许修改的理解?
早期不支持分片 Key 的值修改,主要是修改 Key 的值可能需要涉及到数据移动,同时 MongoDB 4.2 之前不支持分布式事物,修改分片 Key 值后数据迁移可能存在数据不一致。
如何理解分片集合不能创建普通唯一性索引?
普通唯一性索引只能在单分片中起到唯一性约束的作用,跨分片无法做唯一性检查或者说实现代价太大。
如何理解 MongoDB 中_id 值不采用严格自增 ID 的方式生成?
没有必要,而且分布式集群要实现严格自增代价太大。
集合的命名可以加 / 吗?
不建议加,就是任何的集合命名或者数据库命名建议除小写字母、数字及下划线外,不要使用其他任何特殊字符,报错简单清晰。
关于MongoDB,个人有一点使用方面的问题:易用性 尤其是使用意聚合框架时,易用性相比较关系型数据库的 SQL 操作,差好多,很多时候需要搜文档现学现用。有没有什么使用上的工具或技巧呢?
建议用MongoDB官方开源的 Compass 里面有专门对 Aggregate 写法的格式化图形界面,用起来还可以,建议试试。
对于千万级表的聚合操作性能问题 有没有优化手段?比如:sum...group by...
业务层分批计算后合并结算结果或建议将数据同步至数仓,用数仓去解决重查询的场景。
带条件的 Count 千万数据就很慢,怎么优化比较好?
建议采用计数表,或加一层缓存。
阅读完文章,想要获取李丹老师带来的《MongoDB 使用规范及最佳视频》直播的回放视频,可以点击文末左下角 [阅读原文] 获取~
社区邀请函
为了让社区组委会成员和所有广大 MongoDB 爱好者的灵活参与,我们特此为想要深度参与社区建设的伙伴们开设了快速加入社区的 “ 绿色通道 ”
如果您也有优质的原创文章分享 ?
如果您也有可以参与线上直播分享的干货内容 ?如果您也想加入社区,成为社区核心用户,领取社区专属 IP 头衔 ?
在这里您将会得到:
结交志同道合的技术伙伴沉淀有价值的干货内容一个展示自己的舞台提升自身的技术影响力即刻加入社区~点击提交申请或者联系助手小芒果(ID:mongoingcom)了解详细事宜~
我们一定为各位伙伴准备的内容价值最大化,并且提供一个专属的舞台,加入还有机会领取社区专属定制精美礼品和专属 IP 头衔!!!
获取更多精彩内容点击社区网站www.mongoing.com
扫描上方二维码添加小芒果微信(ID:mongoingcom)进入中文用户组技术交流群
长按二维码加入我们
本文分享自 Mongoing中文社区 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!