功能简介

在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表的大小,再执行如下命令:

  1. >>> partition_split split_table 8
  2. split app split_table succeed

通过shell工具执行partition split,将split_table partition count从4设置为8

Partition-Split过程中

  1. >>> app split_table -d
  2. [Parameters]
  3. app_name: split_table
  4. detailed: true
  5. [Result]
  6. app_name : split_table
  7. app_id : 2
  8. partition_count : 8
  9. max_replica_count : 3
  10. details :
  11. pidx ballot replica_count primary secondaries
  12. 0 3 3/3 10.239.35.234:34802 [10.239.35.234:34803,10.239.35.234:34801]
  13. 1 3 3/3 10.239.35.234:34803 [10.239.35.234:34801,10.239.35.234:34802]
  14. 2 3 3/3 10.239.35.234:34801 [10.239.35.234:34803,10.239.35.234:34802]
  15. 3 3 3/3 10.239.35.234:34802 [10.239.35.234:34801,10.239.35.234:34803]
  16. 4 -1 0/0 - []
  17. 5 -1 0/0 - []
  18. 6 -1 0/0 - []
  19. 7 -1 0/0 - []
  20. node primary secondary total
  21. 10.239.35.234:34801 1 3 4
  22. 10.239.35.234:34802 2 2 4
  23. 10.239.35.234:34803 1 3 4
  24. 4 8 12
  25. fully_healthy_partition_count : 4
  26. unhealthy_partition_count : 4
  27. write_unhealthy_partition_count : 4
  28. read_unhealthy_partition_count : 4
  29. 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>