本文档说要介绍Pegasus负载均衡的概念、使用和设计。

概念篇

在Pegasus中,负载均衡主要包括以下几个方面的内容:

  • 如果某个partition分片不满足一主两备,要选择一个机器将缺失的分片补全。这个过程在Pegasus中叫做cure
  • 当所有的分片都满足一主两备份后,对集群各个replica server上分片的个数做调整,尽量让每个机器上服务的分片数都维持在一个相近的水平上。这个过程在Pegasus中叫做balance
  • 如果一个replica server上挂载了多个磁盘,并且通过配置文件data_dirs提供给Pegasus使用。replica server要尽量让每个磁盘上分片的数量都维持在一个相近的水平上。 围绕这几点内容,Pegasus引入了一些概念方便描述这些情况:

  • Partition的健康状况

Pegasus为Partition定义了几种种健康状况:

  • 【fully healthy】: 健康的,完全满足一主两备
  • 【unreadable】: 分片不可读了。指的是分片缺少primary, 但有一个或两个secondary。
  • 【readable but unwritable】: 分片可读但是不可写。指的是只剩下了一个primary,两个secondary副本全部丢失
  • 【readable and writable but unhealthy】: 分片可读可写,但仍旧不健康。指的是三副本里面少了一个secondary
  • 【dead】: partition的所有副本全不可用了,又称之为DDD状态。 当通过pegasus shell来查看集群、表以及分片的状态时,会经常看到对分片健康情况的整体统计或单个描述。譬如通过ls -d命令,可以看到各个表处于不同健康状况的partition的个数,包括这些:

  • fully_healthy:完全健康。

  • unhealthy:不完全健康。
  • write_unhealthy:不可写,包括上面的readable but unwritable和dead。
  • read_unhealthy:不可读,包括上面的unreadable和dead。
    • Meta server的运行level

meta server的运行level决定了meta server会对整个分布式系统做到何种程度的管理。最常用的运行level包括:

  • blind:在这种运行level之下,meta_server拒绝任何可能会修改元数据状态的操作。一般在做zookeeper迁移的时候会用到这个level。
  • steady:在这种运行level下,meta server只做cure,即只处理unhealthy的partition。
  • lively:在这种运行level下,一旦所有partion都进入了healthy, meta server就会尝试进行balance,来调整各个机器的分片数。

操作篇

观察系统情况

可以通过pegasus的shell客户端来观察系统的Partition情况:

  • nodes -d

可以用来观察系统中每个节点的partition个数:

  1. >>> nodes -d
  2. address status replica_count primary_count secondary_count
  3. 10.132.5.1:32801 ALIVE 54 18 36
  4. 10.132.5.2:32801 ALIVE 54 18 36
  5. 10.132.5.3:32801 ALIVE 54 18 36
  6. 10.132.5.5:32801 ALIVE 54 18 36

如果节点间的partition个数分布差异太大,可以采用"set_meta_level lively"的命令来进行调整。

  • app -d

可以用来某张表的所有partition的分布情况:可以观察到某个具体partition的组成,也可以汇总每个节点服务该表的partition个数。

  1. >>> app temp -d
  2. [Parameters]
  3. app_name: temp
  4. detailed: true
  5. [Result]
  6. app_name : temp
  7. app_id : 14
  8. partition_count : 8
  9. max_replica_count : 3
  10. details :
  11. pidx ballot replica_count primary secondaries
  12. 0 22344 3/3 10.132.5.2:32801 [10.132.5.3:32801,10.132.5.5:32801]
  13. 1 20525 3/3 10.132.5.3:32801 [10.132.5.2:32801,10.132.5.5:32801]
  14. 2 19539 3/3 10.132.5.1:32801 [10.132.5.3:32801,10.132.5.5:32801]
  15. 3 18819 3/3 10.132.5.5:32801 [10.132.5.3:32801,10.132.5.1:32801]
  16. 4 18275 3/3 10.132.5.5:32801 [10.132.5.2:32801,10.132.5.1:32801]
  17. 5 18079 3/3 10.132.5.3:32801 [10.132.5.2:32801,10.132.5.1:32801]
  18. 6 17913 3/3 10.132.5.2:32801 [10.132.5.1:32801,10.132.5.5:32801]
  19. 7 17692 3/3 10.132.5.1:32801 [10.132.5.3:32801,10.132.5.2:32801]
  20. node primary secondary total
  21. 10.132.5.1:32801 2 4 6
  22. 10.132.5.2:32801 2 4 6
  23. 10.132.5.3:32801 2 4 6
  24. 10.132.5.5:32801 2 4 6
  25. 8 16 24
  26. fully_healthy_partition_count : 8
  27. unhealthy_partition_count : 0
  28. write_unhealthy_partition_count : 0
  29. read_unhealthy_partition_count : 0
  30. list app temp succeed
  • server_stat

可以用来观察各个replica server当前的一些监控数据。如果想分析流量的均衡程度,要重点观察各个操作的qps和latency。对于数据值明显异常的节点(和其他节点差异太大),需要排查下partition个数是不是分布不均,或者是不是出现了某个分片的读写热点。

  1. >>> server_stat -t replica-server
  2. COMMAND: server-stat
  3. CALL [replica-server] [10.132.5.1:32801] succeed: manual_compact_enqueue_count=0, manual_compact_running_count=0, closing_replica_count=0, disk_available_max_ratio=88, disk_available_min_ratio=78, disk_available_total_ratio=85, disk_capacity_total(MB)=8378920, opening_replica_count=0, serving_replica_count=54, commit_throughput=0, learning_count=0, shared_log_size(MB)=4, memused_res(MB)=2499, memused_virt(MB)=4724, get_p99(ns)=0, get_qps=0, multi_get_p99(ns)=0, multi_get_qps=0, multi_put_p99(ns)=0, multi_put_qps=0, put_p99(ns)=0, put_qps=0
  4. CALL [replica-server] [10.132.5.2:32801] succeed: manual_compact_enqueue_count=0, manual_compact_running_count=0, closing_replica_count=0, disk_available_max_ratio=88, disk_available_min_ratio=79, disk_available_total_ratio=86, disk_capacity_total(MB)=8378920, opening_replica_count=0, serving_replica_count=54, commit_throughput=0, learning_count=0, shared_log_size(MB)=4, memused_res(MB)=2521, memused_virt(MB)=4733, get_p99(ns)=0, get_qps=0, multi_get_p99(ns)=0, multi_get_qps=0, multi_put_p99(ns)=0, multi_put_qps=0, put_p99(ns)=0, put_qps=0
  5. CALL [replica-server] [10.132.5.3:32801] succeed: manual_compact_enqueue_count=0, manual_compact_running_count=0, closing_replica_count=0, disk_available_max_ratio=90, disk_available_min_ratio=78, disk_available_total_ratio=85, disk_capacity_total(MB)=8378920, opening_replica_count=0, serving_replica_count=54, commit_throughput=0, learning_count=0, shared_log_size(MB)=4, memused_res(MB)=2489, memused_virt(MB)=4723, get_p99(ns)=0, get_qps=0, multi_get_p99(ns)=0, multi_get_qps=0, multi_put_p99(ns)=0, multi_put_qps=0, put_p99(ns)=0, put_qps=0
  6. CALL [replica-server] [10.132.5.5:32801] succeed: manual_compact_enqueue_count=0, manual_compact_running_count=0, closing_replica_count=0, disk_available_max_ratio=88, disk_available_min_ratio=82, disk_available_total_ratio=85, disk_capacity_total(MB)=8378920, opening_replica_count=0, serving_replica_count=54, commit_throughput=0, learning_count=0, shared_log_size(MB)=4, memused_res(MB)=2494, memused_virt(MB)=4678, get_p99(ns)=0, get_qps=0, multi_get_p99(ns)=0, multi_get_qps=0, multi_put_p99(ns)=0, multi_put_qps=0, put_p99(ns)=0, put_qps=0
  7. Succeed count: 4
  8. Failed count: 0
  • app_stat -a

可以用来观察某个表中,各个partition的统计信息。对于数据值明显异常的分片,要关注是不是出现了分片热点。

  1. >>> app_stat -a temp
  2. pidx GET MULTI_GET PUT MULTI_PUT DEL MULTI_DEL INCR CAS SCAN expired filtered abnormal storage_mb file_count
  3. 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3
  4. 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1
  5. 2 0 0 0 0 0 0 0 0 0 0 0 0 0 4
  6. 3 0 0 0 0 0 0 0 0 0 0 0 0 0 2
  7. 4 0 0 0 0 0 0 0 0 0 0 0 0 0 3
  8. 5 0 0 0 0 0 0 0 0 0 0 0 0 0 2
  9. 6 0 0 0 0 0 0 0 0 0 0 0 0 0 1
  10. 7 0 0 0 0 0 0 0 0 0 0 0 0 0 3
  11. 0 0 0 0 0 0 0 0 0 0 0 0 0 19

控制集群的负载均衡

Peagsus提供以下几种命令来控制集群的负载均衡:

  • set_meta_level

这个命令用来控制meta的运行level:

  • freezed:meta server会停止unhealthy partition的cure工作,一般在集群出现较多节点宕机或极其不稳定的情况下使用,另外如果集群的节点数掉到一个数量或者比例以下(通过配置文件min_live_node_count_for_unfreezenode_live_percentage_threshold_for_update控制)就会自动变为freezed,等待人工介入。
  • steady:meta server的默认level, 只做cure,不做balance。
  • lively:meta server会调整分片数,力求均衡。 可以使用cluster_info或者get_meta_level查看当前集群的运行level。

关于调整的一些建议:

  • 先用shell的nodes -d命令查看集群是否均衡,当不均衡时再进行调整。通常在以下几种情况发生后,需要开启lively进行调整:
    • 新创建了表,这个时候分片数目可能不均匀。
    • 集群上线、下线、升级了节点,这时候分片数目也可能不均匀。
    • 有节点宕机,一些replica迁移到了别的节点上。
  • 调整过程会触发replica迁移,影响集群可用度,虽然影响不大,但是如果对可用度要求很高,并且调整需求不紧急,建议在低峰时段进行调整。
  • 调整完成后通过set_meta_level steady将level重置为steady状态,避免在平时进行不必要的replica迁移,减少集群抖动。
  • Pegasus还提供了一些精细控制balance的命令,参见负载均衡的高级选项
    • balance

balance命令用来手动发送分片迁移的命令。支持的迁移类型:

  • move_pri:把某个partition下的primary和secondary做调换(本质上为两步:1.将from降级;2.将to升级。如果meta server在第1步完成后挂掉,新的meta server不会继续进行第2步,可以视为move_pri命令被中断了)
  • copy_pri:把某个partition下的primary迁移到一个新节点下
  • copy_sec:把某个partition下的secondary迁移到一个新节点下 注意在使用时,请保证meta server处在steady状态,不然命令无法生效。

参见以下样例(不相关的输出已经被删去):

  1. >>> get_meta_level
  2. current meta level is fl_steady
  3. >>> app temp -d
  4. pidx ballot replica_count primary secondaries
  5. 0 3 3/3 10.231.58.233:34803 [10.231.58.233:34802,10.231.58.233:34801]
  6. list app temp succeed
  7. >>> balance -g 1.0 -p move_pri -f 10.231.58.233:34803 -t 10.231.58.233:34802
  8. send balance proposal result: ERR_OK
  9. >>> app temp -d
  10. pidx ballot replica_count primary secondaries
  11. 0 5 3/3 10.231.58.233:34802 [10.231.58.233:34801,10.231.58.233:34803]
  12. list app temp succeed
  • propose

propose命令用来发送更低原语的分片调整命令,主要有以下几种:

  • assign_primary:把某个partition的primary分配到某台机器上
  • upgrade_to_primary:把某个partition的secondary升级为primary
  • add_secondary: 为某个partition添加secondary
  • upgrade_to_secondary: 把某个partition下的某个learner升级为secondary
  • downgrade_to_secondary:把某个partition下的primary降级为secondary
  • downgrade_to_inactive:把某个partition下的primary/secondary降级为inactive状态
  • remove:移除掉某个partition下的某个副本
  1. >>> app temp -d
  2. pidx ballot replica_count primary secondaries
  3. 0 5 3/3 10.231.58.233:34802 [10.231.58.233:34801,10.231.58.233:34803]
  4. list app temp succeed
  5. >>> propose -g 1.0 -p downgrade_to_inactive -t 10.231.58.233:34802 -n 10.231.58.233:34801
  6. send proposal response: ERR_OK
  7. >>> app temp -d
  8. pidx ballot replica_count primary secondaries
  9. 0 7 3/3 10.231.58.233:34802 [10.231.58.233:34803,10.231.58.233:34801]
  10. list app temp succeed

在上面的例子中,propose命令希望把10.231.38.233:34801降级。所以需要把这个命令发给partition的primary(10.231.58.233:34802),由它来执行具体某个副本降级的事宜。注意这里体现了pegasus系统的设计理念:meta server负责管理primary , pimary负责管理partition下的其他副本

上面的例子也许看不出10.231.38.233:34801被降级的痕迹。这是因为系统cure功能的存在,对于unhealthy的partition会迅速修复。你可以通过观察ballot的变化来确认这个命令已经生效了。

正常情况下,你应该不会需要使用到propose命令。

负载均衡的高级选项

meta server提供了一些更细粒度的参数用来做负载均衡的控制。这些参数是通过remote_command命令来调整的:

通过help查看所有的remote_command

  1. >>> remote_command -l 127.0.0.1:34601 help
  2. COMMAND: help
  3. CALL [user-specified] [127.0.0.1:34601] succeed: help|Help|h|H [command] - display help information
  4. repeat|Repeat|r|R interval_seconds max_count command - execute command periodically
  5. ...
  6. meta.lb.assign_delay_ms [num | DEFAULT]
  7. meta.lb.assign_secondary_black_list [<ip:port,ip:port,ip:port>|clear]
  8. meta.lb.balancer_in_turn <true|false>
  9. meta.lb.only_primary_balancer <true|false>
  10. meta.lb.only_move_primary <true|false>
  11. meta.lb.add_secondary_enable_flow_control <true|false>
  12. meta.lb.add_secondary_max_count_for_one_node [num | DEFAULT]
  13. ...
  14. Succeed count: 1
  15. Failed count: 0

remote_command是pegasus的一个特性, 允许一个server注册一些命令,然后命令行可以通过rpc调用这些命令。这里我们使用help来访问meta server leader,获取meta server端支持的所有命令。例子中已经略掉了所有不相关的行,只留下以"meta.lb"开头的所有和负载均衡相关的命令。

由于文档和代码的不一致问题,文档里不一定覆盖了当前meta所有的lb控制命令。如果想获取最新的命令列表,请用最新的代码手动执行一下help。

assign_delay_ms

assign_delay_ms用来控制当partition缺少一个secondary时,我们要延时多久才选择一个新的。之所以这么做,是因为一个副本的掉线可能是临时性的,如果不给予一定的缓冲就选择新的secondary, 可能会导致巨量的数据拷贝。

  1. >>> remote_command -t meta-server meta.lb.assign_delay_ms
  2. COMMAND: meta.lb.assign_delay_ms
  3. CALL [meta-server] [127.0.0.1:34601] succeed: 300000
  4. CALL [meta-server] [127.0.0.1:34602] succeed: unknown command 'meta.lb.assign_delay_ms'
  5. CALL [meta-server] [127.0.0.1:34603] succeed: unknown command 'meta.lb.assign_delay_ms'
  6. Succeed count: 3
  7. Failed count: 0
  8. >>> remote_command -t meta-server meta.lb.assign_delay_ms 10
  9. COMMAND: meta.lb.assign_delay_ms 10
  10. CALL [meta-server] [127.0.0.1:34601] succeed: OK
  11. CALL [meta-server] [127.0.0.1:34602] succeed: unknown command 'meta.lb.assign_delay_ms'
  12. CALL [meta-server] [127.0.0.1:34603] succeed: unknown command 'meta.lb.assign_delay_ms'
  13. Succeed count: 3
  14. Failed count: 0
  15. >>> remote_command -t meta-server meta.lb.assign_delay_ms
  16. COMMAND: meta.lb.assign_delay_ms
  17. CALL [meta-server] [127.0.0.1:34601] succeed: 10
  18. CALL [meta-server] [127.0.0.1:34602] succeed: unknown command 'meta.lb.assign_delay_ms'
  19. CALL [meta-server] [127.0.0.1:34603] succeed: unknown command 'meta.lb.assign_delay_ms'
  20. Succeed count: 3
  21. Failed count: 0

如例所示,命令不加参数表示返回当前设定的值。加参数表示期望的新值。

assign_secondary_black_list

该命令用来设定添加secondary的黑名单。这个命令在批量下线集群节点的时候非常有用, 例如:

add secondary时候的流控

在某些时候, 负载均衡的决策算法可能会要求一个机器上要新增不少secondary副本, 如

  • 一个或多个节点的宕机,会导致正常的节点瞬时接受很多的芬片
  • 新节点的加入,可能会有大量的涌入。 但在执行这些增加分片的决策动作时,我们应该避免同一时刻有大量的secondary分片同时添加, 因为

  • 添加secondary副本基本会涉及数据拷贝, 如果量太大可能会对正常读写造成影响

  • 带宽总量是有限的, 如果由分给多个添加分片的任务去分享这些带宽, 那么每个任务执行的时常都会拉长, 从而让系统长期处在一个大量分片都不健康的状态下, 增加了稳定性的风险。 所以, pegasus用两个命令来对流控做支持:

  • meta.lb.add_secondary_enable_flow_control: 表示是否开启流控的feature

  • meta.lb.add_secondary_max_count_for_one_node: 表示对于每个节点,同时执行多少个add_secondary的动作。

精细控制balancer

balancer表示把各节点个数调匀的过程。在目前的pegasus实现中,balancer过程大概可以用四点来概括:

  • 尽量通过角色互换来做到primary均衡
  • 如果1做不到让primary变均匀,通过拷数据来做到primary均衡
  • 在2做完后,通过拷数据做到secondary的均衡
  • 分别针对每个表做1-2-3的动作 pegasus提供了一些控制参数给些过程可以提供更精细的控制:

  • meta.lb.only_primary_balancer: 对于每个表,只进行1和2(减少copy secondary带来的数据拷贝)

  • meta.lb.only_move_primary: 对于每个表,primary调节的时候只考虑方法1(减少copy primary带来的数据拷贝)
  • meta.lb.balancer_in_turn:各个表的balancer用串行的方式做,而不是并行进行(用于调试,观察系统行为)

一些命令的使用案例

通过把上面的这些负载均衡原语结合起来,pegasus提供了一些一些脚本来执行滚动升级、节点下线等一些操作,如:

这个脚本用来把某个节点上服务的所有primary都赶走

用来对集群中的节点做在线滚动升级

用来做一批节点的下线

不过有部分脚本的逻辑依赖小米的minos部署系统。这里希望大家可以帮助pegasus, 可以支持更多的部署系统。

设计篇

TBD