对于数据量和请求量很大的业务场景,可以使用文档数据库服务集群版本来提升容量和性能。
集群版本包含的 3 种组件:
- mongos 节点。用户通过 mongos 节点操作数据库,mongos 会将用户的操作请求路由到底层的存储分片中,并收集结果进行合并等操作后赶回给客户端。
- config server 节点。底层是 3 副本主从架构,存储路由表、用户、集群配置等信息。对于用户来说是不感知的。
- shard server 节点。分片集群有 2 个或者多个 shard,每个 shard 都是独立的 3 副本主从架构。每个 shard server 存储具体的用户数据,执行具体的数据操作。
创建分片表
如果使用集群版本,用户可以在建表时指定片建创建分片表。后续对分片表的读写操作会根据指定的分片建路由到合适的 shard server 中。
需要注意的是,集群版本也能创建非分片表。如果用户在连接 mongos 之后没有按照分片表的方式建表,默认会创建一个非分片表,并存储在某一个 shard server 上,此时容量和性能都会受限。
选择分片策略
文档数据库服务集群常用的分片方式有范围和哈希方式。
- 范围分片
范围分片方式会按照分片键的范围将数据分成多个 chunk,每个 chunk (默认配置是 64MB) 存储一部分范围连续的数据。范围分片能够很好地满足范围查询需求,但是缺点也很明显。如果业务的写入模型有明显按照分片建递增或者递减的趋势,则写入操作很大程度上会分布再同一个 shard 上,导致无法扩展写能力。
- 哈希分片
哈希分片方式会先计算分片键的哈希值,然后再根据哈希值的取值范围进行分片,将文档分布到不同的 chunk 中。基于哈希分片能够保证数据在各个分片上的分布基本均衡。但是缺点在于如果出现范围查询,mongos 节点需要将请求广播到 shard server 上,导致查询效率会有所下降。
选择合理的分片键
分片键的选取需要保障数据分布足够离散,每个分片的存储容量均衡,并能够将数据操作均匀分发到集群中的所有分片中。
如果分片键选取不佳,可能会导致各个分片负载不均,出现 jumbo chunk 导致无法分裂等问题。而且分片键一旦确定之后,不能在运行过程中进行变更,需要按新分片键创建新表后重新导入数据。
一般选取分片键时,会考虑以下因素:
- 分片键的区分度
分片键的取值基数决定了最多能包含多少个 chunk,如果取值基数太小,则会导致 chunk 数量很低,可能会有负载不均的问题。比如按照“性别”来设置分片键就不是合理的选择,因为 “性别” 只有 “男”、“女” 2 种取值,这样最多 2 个 chunk。
- 分片键的取值分布是否均匀
如果分片键的取值存在热点,也可能导致分片负载不均。比如以 “国家” 作为片建,会由于各个国家之间人口的差异出现负载不均,人口多的国家存储量大请求多,而人口少的国家存储量小请求少。对于这种场景,可以考虑使用复合键来作为分片键,降低出现热点的概率。
- 是否会按照分片键单调写入
如果业务按照分片键递增或者递减的趋势进行读写,则可能在某一时刻请求集中在某个 chunk 上,无法发挥集群多个分片的优势。比如对于存储日志的场景,如果按照日志创建时间进行范围分片,则在某一时间内一直对同一个 chunk 进行写入。对于这种场景,可以考虑复合分片键或者哈希分片来避免这种情况。
- 查询模型是否包含分片键
在确定分片键后,需要考虑业务的查询请求中是否包含有分片键。mongos 节点会根据查询请求中的分片键将请求转发到对应的 shard server 中。如果查询请求中不包含分片键,则 mongos 节点会将请求广播到后端的所有 shard server,进行 scatter/gather 模式的查询,此时查询性能会有所下降。
创建分片表和相关注意事项
使用 mongo shell 客户端连接到 mongos 节点,创建分片表的步骤和命令如下。
- 设置对应的 database 为分片模式。
sh.enableSharding(<database>)
- 指定分片键创建分片表。
sh.shardCollection(<namespace>, <key>, <unique>, <options>)
命令中其中参数说明:
- namespace:是 . 的形式,比如 "mydb.myshardcollection"。
- key:表示分片键和分片策略,1 表示范围分片,"hashed" 表示哈希分片。比如 {"myshardKey": "hashed"}。
- unique:表示分片键是否有全局唯一性约束,true 表示唯一,对于哈希分片来说只能是 false。
- options:表示分片的参数选项,对于哈希分片来说可以指定预分配的 chunk 个数,比如 {numInitialChunks: 5}。
每个 chunk 的默认大小是 64MB, 对于哈希分片可以通过 numInitialChunks 预分配 chunk,能够有效降低写入过程中 chunk 分裂迁移带来的性能抖动。
配置 balancer 窗口
分片集群内部有 balancer 模块进行数据均衡,保证每个 shard 的 chunk 个数大体相同。Balancer 操作涉及到 chunk 迁移,因此也会占用 shard server 上的硬件资源。
用户可以通过配置 balancer 窗口来指定后台哪些时间段进行数据均衡操作,通过将均衡操作指定在业务低峰期,可以有效避免对线上业务的影响。
设置和步骤和方法如下:
- 使用 mongo shell 连接到 mongos 节点。
- 切换到 config 数据库(use config)。
- 确认 balancer 开关是开启的(sh.getBalancerState())。
- 设置 balancer 时间窗口。
db.settings.update(
{ _id: "balancer" },
{ $set: { activeWindow : { start : "<起始时间>", stop : "<结束时间>" } } },
{ upsert: true }
)
其中 “起始时间” 和 “结束时间” 都是 HH:MM 的形式,比如 02:30。