功能简介
在pegasus中,表的partition个数是在创建时指定的,并且不会动态改变,但随着数据量不断增大,可能出现partition数据过大的情况,这样可能会导致读写效率下降,因此需要人工增大partition个数来保障服务质量。
在pegasus的设计中,partition个数为2的幂次,目前split功能会将partition个数翻倍,partition(i)将会被分裂为partition(i)和partition(i+original_count) 。例如,原表有4个partition,在split之后将有8个partition,partition(0)将会分裂为partition(0)和partition(4),partition(1)将会分裂为partition(1)和partition(5)……以此类推。我们将partition(i)称为parent partition,partition(i+original_count)称为 child partition
接口描述
partition_split <app_name> <new_partition_count>
- 若当前表不可用,返回 ERR_APP_NOT_EXIST
- 若new_partition_count != old_partition_count*2,返回 ERR_INVALID_PARAMETERS
- 若表正在进行split,返回 ERR_BUSY
- 若split成功返回 ERR_OK
设计与实现
整体流程
partition split过程可分为以下几个步骤:
- client发送partition split请求给meta server。
- replica server通过与meta server的config_sync发现表partition个数发生变化。
- 每个parent partition通过异步learn的方式复制自己所有数据得到child partition。
- 当一个group中的所有child partition就绪后,primary给meta server发送注册child partition的请求。
- meta server注册child partition。
- 所有child partition被注册完成后,split过程结束。
- 清理无效数据,详见如何删除无效数据。
Partition-Split过程中的读写
在partition split过程中,读写流程可保持正常进行,在注册child partition期间有短暂拒绝服务。
为了方便说明,假设app的partition个数为4,分裂后为8,而client希望访问的数据,在分裂前由partition(1)服务,分裂后由partition(5)服务。从split开始到partition进行异步learn,都仍将由partition(1)为client进行服务,但当primary给meta server发送注册child partition请求后,partition(1)将拒绝client的读写请求,直到meta server注册完成。当注册完成后,client并不知道将由partition(5)为其服务,仍将请求发送给partition(1),这时partition(1)会提示client更新访问路由表,而更新路由表是对用户透明的。总体来说服务不可用时间非常短。
为什么需要指定partition个数
由于partition split功能不能取消,且没有减小partition的功能,因此执行partition split需谨慎,虽然目前单次split只能使partition个数翻倍,但仍需指定partition count,这是为了防止client多次重试这个非幂等操作导致partition个数非预期增大。
如何删除无效数据
执行partition split前需要保证磁盘空间可用超过50%,内存可用充足,因为split操作首先需要对每个partition进行复制,在split完成后,pegasus会通过rocksdb提供的filter功能在后台删除split造成的无效数据。若磁盘资源紧张或者希望尽快删除无效数据,可在集群CPU空闲期间执行manual_compact功能,手动触发filter,命令详情参见操作示例。
Partition-Split与热点问题
split功能主要是为了保障在数据量非预期增长情况下的服务质量,并不能完全解决单个partition过热的问题,pegasus的数据模式是hash分片,在split完成后流量并不能保障是被平分在两个partition上,这个是依赖于用户的hashkey决定的,只能说可以缓解热点问题,并且partition split是表级命令,暂时不支持针对单个partition的partition split。
操作示例
执行Partition-Split
在split前,建议先通过app_stat
命令查看待split表的大小,再执行如下命令:
>>> partition_split split_table 8
split app split_table succeed
通过shell工具执行partition split,将split_table partition count从4设置为8
Partition-Split过程中
>>> app split_table -d
[Parameters]
app_name: split_table
detailed: true
[Result]
app_name : split_table
app_id : 2
partition_count : 8
max_replica_count : 3
details :
pidx ballot replica_count primary secondaries
0 3 3/3 10.239.35.234:34802 [10.239.35.234:34803,10.239.35.234:34801]
1 3 3/3 10.239.35.234:34803 [10.239.35.234:34801,10.239.35.234:34802]
2 3 3/3 10.239.35.234:34801 [10.239.35.234:34803,10.239.35.234:34802]
3 3 3/3 10.239.35.234:34802 [10.239.35.234:34801,10.239.35.234:34803]
4 -1 0/0 - []
5 -1 0/0 - []
6 -1 0/0 - []
7 -1 0/0 - []
node primary secondary total
10.239.35.234:34801 1 3 4
10.239.35.234:34802 2 2 4
10.239.35.234:34803 1 3 4
4 8 12
fully_healthy_partition_count : 4
unhealthy_partition_count : 4
write_unhealthy_partition_count : 4
read_unhealthy_partition_count : 4
list app split_table succeed
通过app <table_name> -d
命令查看当前表的详情,ballot=-1表示该partition还没有被meta server注册
Partition-Split完成
同样通过app <table_name> -d
查看表详情,当发现所有ballot都大于0时表示所有partition都被注册,若当前流量不大可以将meta server设置为lively状态,进行负载均衡,并且通过app_stat
命令查看表的大小,应该是split前的2倍左右
手动触发Manual-Compact
关于Manual compact详情可参见Manual compact功能,在集群CPU空闲时进行操作,建议命令示例如下:
./scripts/pegasus_manual_compact.sh -c <meta_list> -a <table_name>