持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第7天,点击查看活动详情
本系列主要是《数据密集型应用系统设计》阅读笔记,本文记录分区主题的笔记心得。
随着时间的推移,数据库总会发生某些变化:
- 查询压力增加,因此需要更多的CPU来处理负载
- 数据规模增加,因此需要更多的磁盘和内存来存储
- 节点可能出现故障,因此需要其他机器来接管失效的节点
这样的变化需要将数据和请求从一个节点转移到另一个节点。这样一个负载迁移的过程称为再平衡。 迁移之后,至少满足:
- 负载、数据、请求等更均匀的分布
- 再平衡的过程中,数据库应该可以继续提供读写服务
- 避免不必要的负载迁移,以加快动态再平衡,尽量减少网络和磁盘I/O影响。
那么应该怎么再平衡呢?有几种如下的策略。
再平衡策略
再平衡的时候需要减少数据迁移。哈希分区的分区则可能不适合再平衡了。
固定数量的分区
- 创建远超节点数的分区数
- 每个节点分配多个分区,比如10个节点,逻辑分区为100个,这样大约每个节点承担100个分区
- 如果添加了一个新节点,则可以匀走几个分区。
- 如果删除节点,则是相反的均衡措施 选中的整个分区在节点之间迁移,分区的总数量维持不变,也不改变关键字到分区的映射关系。唯一调整的是分区和节点的对应。
目前ElasticSearch等支持这种动态平衡的方法。 分区的数量在创建数据库的时候就确定了,初始化需考虑将来扩容增长的需求,设置一个足够大的分区。而每个分区也有额外的管理开销,选择太大也有可能有副作用。
动态分区
对于采用关键字区间分区的数据库,如果边界设置有问题,可能会出现所有数据都挤在一个分区而其他分区基本为空的情况。手动重新分配比较繁琐,像HBase等采用了动态创建分区:
- 当分区的数据增长超过一个阈值(比如10G),则拆分为两个分区,每个承担一半的数据。
- 如果大量数据被删除,缩小到某个阈值,则将其与相邻的分区合并 动态分区的优点是分区数可以自动适配数据总量,如果只有少量的数据,少量的分区就足够了,这样系统开销很小;如果有大量的数据,每个分区大小则被限制。
Hbase和MongoDb会在一个空的数据库创建一组初始分区。
按节点比例分区
Cassandra等采用了按节点比例分区,每个节点拥有固定数量的分区。 像一致性哈希分区适合这种再平衡的方式。
自动还是手动执行?
再平衡说应该自动执行还是手动执行呢? 再平衡是个昂贵的操作,需要重新路由请求并将大量数据从一个节点迁移到另一个节点。全自动平衡对时机和风险的评估不够,所以由系统生成一个分区分配的方案,再由管理员确认生效更好一些。
请求路由
当客户需要发送请求的时候,如何知道应该连接哪个节点呢? 尤其是发生了再平衡之后。
概括来讲,有如下几种不同的处理策略:
- 客户端连接任意的节点。如果节点刚好拥有所处理的分区们则处理;否则转发到下一个合适的节点,接收答复并将答复转发给客户端。
- 客户端将请求发送到路由层,后者转发到对应分区的节点,接收答复并将答复转发给客户端。
- 客户端感知分区和节点分配到关系
如下图:
许多分布式数据库依靠独立的协调服务(ZooKeeper)跟踪集群范围内的元数据。每个节点都向ZooKeeper注册自己,ZooKeeper维护了分区到节点的最终映射关系。其他参与者(路由层或感知分区的客户端)可以向ZooKeeper订阅此信息。像Hbase、kafka和MongoDB都是使用ZooKeeper来跟踪分区分配情况的。