CockroachDB 分裂分析

CockroachDB的分裂支持两种分裂策略:基于range size的分裂和基于range load的分裂。

分裂的整体逻辑是这样的,每一个store维护一个split queue,如果一个range满足分裂的条件,就会插入这个队列,这个队列异步的从队列中pop一个分裂任务去执行。执行的逻辑比较简单,根据split key将old range分裂成两个range,在这里分裂仅仅是修改range的元数据,并不涉及到数据迁移(也可以理解为一种分解设计,毕竟后面的某一个时刻会因为负载均衡发生range迁移)。这个队列会控制并发度,默认情况下最多允许同时进行4个分裂任务。如果分裂失败,则会将任务转入重试队列(代码中叫炼狱队列purgatory map),一个任务的执行默认超时时间是1分钟。

每一次执行写操作(commit之后)都会检查是否需要split,判定规则很简单,如果统计的range size大于配置的range max size(默认64MB)那么向队列中加入一个分裂任务。

对于基于负载的range split则是在写开始的时候,检查当前统计的range QPS(当前一秒内的QPS),如果大于配置的range split QPS,那么向队列中添加一个分裂任务(split key的选择则是基于一个采用算法,并不是遍历range寻找一个合适key,这样也合理,因为负载于按照字典序排序的key之间没有关联,存在很强的随机性)。

CockroachDB的分裂在细节又分为user range split和system range split,仅仅是选择split key的时候算法不同,剩余的流程没有任何差别。

这里还需要补充一个细节,每次写batch之前会判断统计的range size是否是range max size的2倍(系统默认配置),如果是,那么向队列中添加一个分裂任务,并且等待这个任务执行完成,并且当前的range size不满足2倍(小于)的range max size,继续后续的batch操作,在实际的测试过程发生过等待超时(一个任务执行的超时时间是一分钟)导致本次事务失败,这里的根本原因是分裂的时候(修改range元数据也需要需要操作rocksDB的以及log的split)正好遇到了rocksDB write stall或者write slow(引擎层面的写阻塞或者限速),另一个因素是如果同时有很多的分裂任务在排队,那么也会导致这种情况的发生。

解决之道,在系统层面调优rocksDB,在用户层面增加重试机制,毕竟在rocksDB参数合适的情况下,这种概率并不高,因此用户层面的重试成本也不高。

你可能感兴趣的:(CockroachDB 分裂分析)