高可用测试:KILL TiKV-Server,事务 TPS 掉零现象解读

一、背景概述

高可用测试项中有一项是 kill tikv 实例,观察对于事务 TPS 的影响。按正常思路思考,预期是事务 TPS 会有一定下降,下降比例大致为 kill_tikv_numbers/total_tikv_numbers ,但是测试过程中有一定的概率会出现 TPS 掉零的现象,大概 10s 左右恢复到正常水平的现象(由于 TPS 统计时间粒度的不同,不同工具观测到恢复时间会有不同)。本文针对此现象,剖析 TPS 掉零的原理以及如何如何通过技术手段减少甚至避免此问题发生的概率。

二、预备知识

为了更好的理解后面的内容,需要一些预备知识。

  1. TIDB 采用 Range-based 分片策略

    TIKV 可以看做是一个巨大的有序的 KV map,将整个 Key-Value 空间分成很多段,每一段是一系列连续的 Key,将每一段叫做一个 Region,并且会尽量保持每个 Region 中保存的数据不超过一定的大小。

  2. leader 重新选举

    当某个 region leader 所在的 tikv 异常后,会从其他 tikv 实例选举出新的 leader 继续提供服务。

  3. 选举时间

    raft-base-tick-interval * raft-election-timeout-ticks *(0.0 ~ 1.0)。默认配置下在没有 leader region 的状态下 10s~20s 之间开始选举(选举时间基本维持在 1s 内)。

若对于上述知识有疑问,可以阅读以下三篇文章:

说存储

说计算

谈调度

三、原理解析

简单来讲,一个会话访问的数据所在 region 没有 leader,就会等待重新选举完成,以此类推当在完全随机的压力测试场景下所有会话随着时间的推移都访问到没有 leader 的 region ,那么这个时间点,给到我们的直观感受就是 TPS 掉零,但是其实事务并没有失败,只是在等待新的 leader 。

那么什么场景下会出现上述情况呢?下文会举例分析,并抽象出隐藏在场景下的关键因素。

这里先介绍一个计算公式:

周期数据量= QPS / 并发数 * 选举时间 = QPS / 并发数 * 10s

假设我 sysbench 100 并发测试 QPS 为 200000,那么我的周期数据量就是 200000/100*10 = 20000,也就是说单个会话 10s 时间内理论的数据访问数量,如果 10s 内访问的这些数据 region 所在的 tikv 实例正常,那么我的 QPS 就不等于 0。

TPS 掉零的场景可以大致分为三种:

  1. OLAP 场景,恰好同一时间段同一批用户访问的数据都存放在被 kill 的 tikv 实例上,那么业务 TPS 必然会掉零, 这个场景无法避免。但是跑批业务执行时间普遍都较长,10s 时间对于业务来说几乎无影响。

  2. 热点数据场景,常见于前期测试中,比如有些测试场景选取的测试样例数据小于周期数据量,导致 10s 的周期内可以把所有数据完全遍历完。这个场景 TPS 掉零概率就取决于选取的样例数据是否存在于被 kill 的 tikv 节点上。

  3. 假设数据访问足够随机,典型的 OLTP 业务场景,也是 TPS 掉零影响最大的场景。我们下文主要分析此场景下 TPS 掉零的问题。

假设我们的业务是简单的点查场景,那么一个查询最多访问一个 table region, 那么当我并发足够高的时候必然有会话可以访问到被 kill tikv 之外的实例上存在的 table region,那么 TPS 也就不会掉零。但是我们并没有考虑索引的存在,如果通过访问索引数据再回表(大部分 sql都是这样的访问方式),极端情况下一个 index region 包含表的所有 index 数据,那么只要这个index region 所在的 tikv 被 kill ,那么 TPS 就会掉零。仔细思考下,小表(只含有一个 region)也会出现类似的情况。是否掉零完全取决于该 region 所在 tikv 是否正常。

然而我们更多的场景下,事务都比较复杂,可能涉及很多 table ,同时还可能存在一些小型的范围查询,需要访问很多 region。比如说个人历史账单查询业务,单个账户对应几十甚至上百的流水,而这些流水可能会分布在几十个 region 中,而这些 region很大概率是随机分布在所有的 tikv 实例上。也就是对于任意的一个账户,需要访问的数据分布在每个 TIKV 实例上,这种场景几乎必然是会出现 TPS 掉零。

四、总结

通过上面的分析,可以把影响 TPS 掉零的影响因素归纳成两点:

  • 访问 region 数量

单次查询访问的 region 数量,当 region 数量大于 tikv 实例数, TPS 几乎必掉零。

  • 受影响的数据占表总数据量的比例

以单表为例,考虑到 table region 和 index region 都存在的情况,极端情况下受影响的数据量为 table region keys + index region keys,所以 QPS 掉零的概率计算公式应该为 ( table region keys + index region keys)/table total keys。按这个推论,假设事务内涉及 n 张表,那么 TPS 掉零的概率为 1 - (1 - ( table1 region keys + index1 region keys)/table1 total keys)*(1- ( table2 region keys + index2 region keys)/table2 total keys)。

  • 周期数据量

选举期间访问的数据量越多,访问到问题数据的概率越大。

五、解决方案

减少掉零概率

  1. 增加 tikv 实例的数量,这样可以减少 kill tikv 影响到的 region 数量。

  2. 小表场景可以尝试手工切分 table region 和 index region。

  3. 设置参数减少选举所需要的时间,减少周期数据量。不建议调整到特别小,否则可能影响性能。

尽管上面的措施可以减少掉零的概率,但是并不能完全避免。 region 是完全随机分布,table region、index region 分布和业务没有任何关联。但是很高兴的告诉大家,TIDB 4.0 推出的 Placement Rules 试验特性可以实现类似分库分表的数据分布,从而完全避免 TPS 掉零。Placement Rules 是 PD 在 4.0 版本引入的试验特性,它是一套副本规则系统,用于指导 PD 针对不同类型的数据生成对应的调度。通过组合不同的调度规则,用户可以精细地控制任何一段连续数据的副本数量、存放位置、主机类型、是否参与 Raft 投票、是否可以担任 Raft leader 等属性。比如我们可以将业务表按业务维度建成分区表,通过Placement Rules加分区数据(索引)绑定到不同的tikv节点。这样当我某个 tikv 实例故障,一定存在可以正常访问的数据。但是目前可能规则的编写比较复杂,后续版本我们将会提供 Sql 接口,极大减少操作的复杂度。

通俗易懂的解释了我遇到的问题,大佬威武 @haiyinggao 文章中说了4.0可以解决这个问题,请问是否需要另外的配置?有对应配置的文档吗。

需要额外配置

  1. TIKV 配置 label 标签,可以理解为 TIKV 的地理位置属性
  2. 根据业务维度将表数据切分成几等份(可考虑分区表),计算出每份数据的 startkey 和 endkey
  3. 配置 placements rule 将每份数据根据 label 属性绑定到不同的 TIKV 节点

参考文档:
Placement Rules