mongoDB分片技术之片键设计(学习笔记)

选择片键需要考虑的因素:

片键基数:

片键基数指的是划分数据块的能力。举个例子,假如要记录一个省所有考生的高考成绩,如果以成绩作为片键进行范围分片,那存储较高成绩记录的分片的数据量必然要远远小于存储平均成绩左右记录的分片的数据量,存储数据最多的这个分片就会成为瓶颈。这时以考生的考号作为片键就更有利于数据的均匀分布。

写分布:

为了系统负载均衡,我们往往会期望数据均匀分布于各个分片上。如果以一个单调递增的值作为片键,那么最终数据往往只会往一个分片上写入。

查询分发:

同样的,大多数情况下我们也希望读操作均匀的分布于集群的分片。

查询隔离(在一个或尽量少的分片中完成查询所需)

如果分片集群的查询要通过查询所有的分片才能返回结果的话,代价也往往是很高的。考虑上文中片键基数的例子:如果把考生的考号作为片键虽然可以做到写分布均匀,但是如果想查询某个分数的所有考生数据就需要遍历所有的分片了。如果此时的分片很多,那么代价其实还是很高的。如果这种查询很多,就要考虑另外一种片键了。现在我们制作一个复合片键({考生分数,考生的考号}),那么同样的分数必然只处于相近的几个分片上,同时考生的考号又增加了片键基数,算是比较平衡了写入和查询的隔离。

片键的方案

id的hashed

可以使用数据文档_id的哈希作为片键
这个方案能够使读和写都能够平均分布,并且它能够保证每个文档都有不同的片键所有数据块能够很精细。
还是不够完美,因为这样的话对多个文档的查询必将命中所有的分片,但也算一种方案。
在物联网等情况下,存储设备的采集数据,并只需按特定设备ID(先根据关系型数据库业务表设备号获取设备ID)进行查询。此时,如果设备ID是单调递增(表主键)的,可以选择hash(id)来进行存储。个人觉得这种设计可能存在巨大块,但查询特定设备数据,就只需查一个或极少分片。完善,请具体继续往下看。

多租户混合索引

如果想击败哈希索引模式,那么你需要将关联的文档在索引中尽可能聚集在一起的方法。
但是我们不能简单地使用设备ID作为片键,因为那会导致巨大块的产生,所以我们引入了timestamp(设备上报时间-long型-单位:秒即可)来将设备采集的大量数据打散到多个块中,即key={hash(deviceId), timestamp} 或 key={deviceId, timestamp},推荐前者(后者要是设备id都是自增的话,还是导致不同设备数据写入都集中到少数的分片中,仍然避免不了热点问题) 。这些打散的块仍旧是索引连续的,所以仍然会分布在一个分片或少数分片上。

chunk特大块

特大块的产生

相同片键值的文档都处于同一个块中,因此这个块是不可以拆分的。如果块大小超过了config.settings中设置的最大块大小,那么均衡器就无法移动这个块了,这种不可以拆分和移动的块就叫做特大块

特大块分发迁移

1)关闭均衡器

2)调高chunkSize

因为mongoDB不允许移动大小超过最大块大小设置值的块,所以需要临时调高最大块大小的设置值。

3) 使用moveChunk手动迁移块

4)使用splitAt将剩余的块切分的更小

5)将块大小修改了最初的值

6)启用均衡器

防止出现特大块

选择片键的时候可以细化片键的粒度,例如以时间为片键的时候可以精确到秒。还可以采用复合片键,片键的第二个字段选择粒度比较细的字段,例如MD5和UUID。

总结

如果在应用中找不出一个好的聚合键,那么对_id做哈希吧(当然并不建议,没办法中的笨办法)。如果能够找到,那么将它与_id聚合以避免巨大块的产生。请记住无论你使用何种聚合键,它都需要能够将读和写平均分布以充分利用集群中的每个节点。
片键设计原则首先推荐采用类似 key={hash(deviceId), _id: 1} ;而key={deviceId: 1, _id: 1}次之(补充:其实不一定是_id, 根据具体业务查询场景,可以是设备上报时间timestamp,片键不同就可以避免巨大块产生了)。

参考:

来源:MongoDB分片的片键选择
来源:华为云社区文档数据库

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值