TiDB 性能问题排查常用操作

性能问题排查常用操作

基本原则

应用压测目标

当前机器拓扑和资源下

  1. 测试出压测程序压测 TiDB 集群,QPS 和 TPS 的 “天花板”
  2. 平均响应时间和最高响应时间满足需求

常见优化思路

  1. 热点优化,当前主键热点可以使用 shard bits 的方式进行优化
  2. 对于索引热点暂时无法优化,后期可以使用离散列做分区表,可以解决该问题
  3. 业务逻辑冲突优化,比如会有同时修改一行的场景。
  4. 按照压测场景进行参数调优
  5. 按照压测场景进行集群拓扑调整

注意事项

  1. 每次压测性能到达瓶颈,需要确认瓶颈,并且判断是否有优化空间

  2. 热点问题:是否可以打散

  3. 写堆积问题:是否可以调整并发写配置、调整调度相关配置来减轻写堆积

  4. 单线程是否成为瓶颈

  5. 多线程是否默认配置不足,比如 grpc 默认 4 线程,是否达到瓶颈等

  6. 压测方式或者程序逻辑是否符合最佳实践

  7. 每次提交,batch 多少

  8. 并发调整多少

  9. 短连接还是长连接

  10. SQL 是否非常复杂,导致 parser 解析代价较高

了解压测基本原则后,我们需要:

  1. 了解客户测试目的

  2. 需要多少 QPS、TPS、Latch

  3. 是否接受集群扩容

  4. 是否接受表结构调整等

  5. 定位瓶颈

  6. CPU、Memory、网络、IO成为瓶颈

  7. 压测程序成为瓶颈

  8. 负载成为瓶颈

  9. 提高压测性能

  10. 调整集群拓扑,比如点查场景,扩容多个 TiDB

  11. 调整系统参数压测

以上为压测常见的原则和优化方向。具体如何处理,下文继续讨论。

压测相关

QPS 理论值计算

作用

使用 QPS 理论值计算之后,可以和实际压测的 QPS 进行对比,一般有几何倍差距,那么可以断定在程序端或者负载耗时较长,TiDB 暂时没有到达瓶颈。

理论值公式

QPS = 活跃连接数 * (1000ms/平均 duration)

解释

  1. 活跃连接数,可以看监控中 TiDB/Server 中的 Connection count 的变化,其中变化量多数场景下就是活跃连接数。
  2. 平均 duration 是指压测过程中,SQL 的平均响应时间

平均 duration 的获取方式

监控中编辑出相关监控:

sum(rate(tidb_server_handle_query_duration_seconds_sum[1m])) / sum(rate(tidb_server_handle_query_duration_seconds_count[1m]))

注意

  1. 有的框架会有对系统信息进行确认,比如 select @@sql_mode,如果这类 SQL 较多,在监控中是看不出来的,那么就会影响 QPS 理论值计算,因为这类 SQL 执行是非常快的。那么需要开启跟踪日志来确认程序实际打到 TiDB 的内容。
  2. 实际活跃连接数并非等于当前连接数,需要结合实际情况来分析

Sysbench 压测

当定位压测性能,调大并发等操作没有明显效果、并且 TiDB 没有发现明显时,可以尝试手动抓取相关业务 SQL,进行 Sysbench 压测。

当发现压测机器 CPU 无法打满,或者和其他服务器差异较大,可以用 Sysbench 压测性能

作用

  1. 可以排除程序内部有瓶颈的干扰,比如程序中有和 redis 交互的逻辑,在高并发下,redis 成为了瓶颈
  2. 便于调试

数据库压测操作方法

安装

sudo yum install sysbench

编写压测脚本

vi config

mysql-host=172.16.4.51

mysql-port=4000

mysql-user=root

mysql-db=acrm

time=1800

threads=1024

db-driver=mysql

report-interval=10

vi test.lua

pathtest = string.match(test, “(.*/)”)

function thread_init(thread_id)

db_connect()

end

function event(thread_id)

local rs

local rs1

local rs2

local rs3

local rs4

local rs5

local rs6

– db_query(“begin”)

rs = db_query(“SELECT * FROM ms_cust_prdt_day_pft_d WHERE pty_id=‘A000000002761250’ AND ccy= ‘401156’ AND data_dt BETWEEN ‘20180101’ AND ‘20180106’”)

rs1 = db_query(“SELECT * FROM ms_cust_prdt_day_pft_d WHERE pty_id=‘A000000002861250’ AND ccy= ‘401156’ AND data_dt BETWEEN ‘20190101’ AND ‘20190106’”)

rs2 = db_query(“SELECT * FROM ms_cust_prdt_day_pft_d WHERE pty_id=‘A000000022761250’ AND ccy= ‘401156’ AND data_dt BETWEEN ‘20180201’ AND ‘20180206’”)

rs3 = db_query(“SELECT * FROM ms_cust_prdt_day_pft_d WHERE pty_id=‘A000000005761250’ AND ccy= ‘401156’ AND data_dt BETWEEN ‘20180301’ AND ‘20180306’”)

rs4 = db_query(“SELECT * FROM ms_cust_prdt_day_pft_d WHERE pty_id=‘A000000008761250’ AND ccy= ‘401156’ AND data_dt BETWEEN ‘20180401’ AND ‘20180406’”)

rs5 = db_query(“SELECT * FROM ms_cust_prdt_day_pft_d WHERE pty_id=‘A000000022761250’ AND ccy= ‘401156’ AND data_dt BETWEEN ‘20180501’ AND ‘20180606’”)

rs6 = db_query(“SELECT * FROM ms_cust_prdt_day_pft_d WHERE pty_id=‘A000000003761250’ AND ccy= ‘401156’ AND data_dt BETWEEN ‘20180701’ AND ‘20180706’”)

end

执行

sysbench --config-file=./config ./test.lua --threads=100 --time=10 run

服务器性能压测

CPU 压测

sysbench --test=cpu --threads=32 --cpu-max-prime=2000000 run >>cpu.test &

说明

可以调整 threads,来决定测试的 CPU 个数。

作用

  1. 可以确认各个服务器 CPU 的性能是否有差异性
  2. 可以确认极端情况下,是否是 CPU 有问题,导致性能打不上去

IO 压测

sysbench --test=fileio --num-threads=16 --file-total-size=1G --file-test-mode=rndrw prepare >>io.txt &

注意

  1. 会生成临时文件,在 data 盘上创建一个 tmp 目录临时测试
  2. 注意 io 性能消耗较高,避免影响线上业务

作用

可以确认各个服务器 IO 的性能是否有差异性

抓包

抓包工具较多,这里介绍一个 github 上一个开源的,MySQL 抓包工具 MySQL Sniffer

MySQL Sniffer 是一个基于 MySQL 协议的抓包工具,实时抓取 MySQLServer 端或 Client 端请求,并格式化输出。输出内容包括访问时间、访问用户、来源 IP、访问 Database、命令耗时、返回数据行数、执行语句等。有批量抓取多个端口,后台运行,日志分割等多种使用方式。

作用

可以在不同服务器中进行抓取,比如应用服务器中抓取,通过负载抓取,直连数据库抓取,查看每个 SQL 的执行效率等,可以对比整个压测链路下,各个部分的性能损耗,便于性能排查

说明

可参考 MySQL Sniffer 使用说明

该工具需要编译安装,需要安装 cmake

安装

git clone https://github.com/Qihoo360/mysql-sniffer

cd mysql-sniffer

mkdir proj

cd proj

cmake …/

make

cd bin/

抓包命令

实时抓取某端口信息并打印到屏幕

输出格式为:时间,访问用户,来源 IP,访问 Database,命令耗时,返回数据行数,执行语句。

mysql-sniffer -i eth0 -p 3306

实时抓取某端口信息并打印到文件

-l 指定日志输出路径,日志文件将以 port.log 命名。

mysql-sniffer -i eth0 -p 3306 -l /tmp

实时抓取多个端口信息并打印到文件

-l 指定日志输出路径,-p 指定需要抓取的端口列表逗号分割。日志文件将以各自 port.log 命名。

mysql-sniffer -i eth0 -p 3306,3307,3310 -l /tmp

问题

  1. 有 lvs 环境下,如果 client IP 是保存在在每个连接阶段的tcp opt字段中,那么mysql-sniffer 提取的真实的 client IP 而不是 lvs 的 IP。
  2. 只能抓取新建的链接,如果是之前创建的链接将获取不到用户名和库名,并有一定几率丢包。

监控

Grpc

查看 Grpc 相关的监控,判断是否有热点问题、Grpc 是否成为了瓶颈等等。

请求数量监控添加

sum(rate(tikv_grpc_msg_duration_seconds_count{type!=“kv_gc”}[1m])) by (instance, job)

作用

  1. 监控中观察到每个 tikv 的 Grpc 请求数量不均衡的时候,可以判断有热点
  2. 如果压测过程中,发现提高并发,Grpc 数量没有随着增长,说明请求数没有到 TiKV

Grpc CPU

默认 Grpc 为 4 线程,如果发现 CPU 使用即将到 400%(比如超过了 320%),说明 Grpc 成为了瓶颈,需要修改配置。

监控路径:TiKV/Thread CPU/grpc poll CPU

大 Region

作用

当集群中有大 Region 时,会造成更多的请求打在这个服务器上,会造成集群性能波动较大。现有的监控中 TiKV/Server/approximate region size 。

该监控会粗略的估计当前集群的 region 大小,当发现监控中数值较高时,集群中可能会有大 region 问题。batch split 功能完善后,大 Region 的出现概率降低。

处理方法

  1. 使用 pd-ctl 命令 region topsize 10 找到相关大 region
  2. 使用 pd-ctl 命令手动切割相关 region:operator add split-region 1 --policy=approximate // 将 Region 1 对半拆分成两个 Region,基于粗略估计值

热点

热点问题是 TiDB 中的常见问题,主要原因是数据写入或者读取比较集中,导致只能有 1-2 个 region 在高负载工作。当 TiDB 集群检测到热点,将会等待 15 min 之后,将热点进行调度开。

热点影响

  1. 无法充分使用集群性能,常常由于单个机器成为瓶颈导致性能无法压测上去
  2. 容易造成写堆积,导致集群性能波动较大
  3. 容易造成大 Region,导致集群性能波动较大

热点类型

  1. 主键热点,对于主键造成的热点,常见情况为自增主键和使用 UUID() 函数生成主键。对于这种表,如果写入压力较大,会有热点情况,可以使用 SHARD_ROW_ID_BITS 将主键打散
  2. 索引热点,对于索引写入热点,比如创建索引是时间列、手机号码等。对于这种场景,暂时没有很好的办法进行打散,后期可以使用 partition 表打散这种热点场景。并且在 3.0 rc2 版本中有打散热点索引命令 (需要后期测试)
  3. 小表热点问题。一个新表初始导入数据时,数据量较少,会导致导入数据大概率在一个 region 中,这样就会有一个小表的写入热点。或者当一个小表频繁被读取时,会造成热点读。对于这种情况,需要手动 split 该表并且调度开。后期支持创建表预分配region 可以解决这个问题。

判断方法

  1. 查看 TiKV/Thead CPU 监控中,各 CPU 的使用情况,当发现有明显的 CPU 使用率不平衡时,代表着有热点问题
  2. 查看 Grpc 监控,查看通信有明显差距,说明有热点问题。(参考上文 Grpc)
  3. 查看 Overview 监控面板中的 IO 使用率,如果有明显不均衡,说明有热点问题。
  4. PD 监控界面中有 HotRegion 监控面板

处理方法

  1. 查看监控(参考判断方法),判断是写热点,还是读热点,并且可以了解热点的 tikv store id 为多少

  2. 使用 pd-ctl 工具查看 TOP Write/Read hot region,根据 tikv store id 的信息来对比 top hot region 结果中的 store id 信息,找出出相关的 region id。相关命令:

  3. region topread [limit] // 用于查询读流量最大的 Region,limit 的默认值是 16

  4. region topwrite [limit] // 用于查询写流量最大的 Region,limit 的默认值是 16

  5. 找到 region id 后,使用 TiDB API,反推出相关表名。相关命令:

  6. curl http://{TiDBIP}:10080/schema?table_id={tableID}

  7. 结合客户需求、表结构、业务特点来判读热点类型,然后进行对应处理(参考上文热点类型)。

  8. 如果是小表热点,可以使用 pd-ctl 工具进行手动 split 和调度。相关命令:

  9. operator add split-region 1 --policy=approximate // 将 Region 1 对半拆分成两个 Region,基于粗略估计值

  10. operator add transfer-leader 1 2 // 把 Region 1 的 leader 调度到 store 2

调度

热点调度

TiDB 集群检测到有 hot region,并且热点 region 不均衡时,15 分钟后,将会进行热点调度,从负载高的 TiKV 调度 region 到负载较低的 TiKV 实例中。

leader/region 调度

  1. leader 和 region 的分布 都是根据 对应的 score来调度的,和 leader 和 region count 无关
  2. 影响 region score 的因素是 region 的 size 和 盘的剩余容量,这里的 size 是估值,大概占物理盘的大小和
  3. 影响 leader score 的因素是 leader 的 size 和 盘的剩余容量无关,这里的 size 是估值,大概占物理盘的大小和
  4. 最终目的是 score 均匀接近

异常判断和处理

  1. 当发现监控 PD leader/region ratio 的值较高,说明 leader 和 region 的分布不均匀

  2. 使用 pd-ctl 查看 store 信息,使用 weight 关键字进行匹配,确认 leader_weight 和 region_weight 每个 tikv 都相等,如果不是需要查看相关比例来确认 ratio 是否预期

  3. 如果非预期,需要查看每个 tikv 的盘容量是否相同,或者有大文件干扰

  4. 可尝试使用 pd-ctl 调大 leader-schedule-limit、region-schedule-limit、replica-schedule-limit,查看分布是否正常

  5. 以上两点是 region/leader 分布不均匀异常,热点调度异常判断可查看监控 PD/Operator:

  6. region/leader 调度请求是否上升:

  7. 已经成功的个数:

  8. 失败的个数(如果明显上升,需要查看 PD leader 日志,找到失败的原因):

小技巧

当 TiKV 机器性能差距较大,可以使用 pd-ctl 配置 weight,人为让 leader/region 分布更为合理,避免单台 TiKV 服务器性能优先成为瓶颈,导致整体集群性能使用不充分。

写堆积

在高并发写入的场景下,可能会发生写堆积状况。在整体监控中,可能无法找到明显瓶颈,但是会发现写入 QPS 不稳定,有大幅度下降和上升过程,这时可能有写堆积,导致写入不稳定。

判断方法

监控中 TiKV/Scheduler/scheduler pending commands 为每个 TiKV 实例上 pending 命令的个数。在高并发的写入场景下,比如 loader 导入数据等,该监控应该是上下波动的,如果向下波动无法到达 0,整体是向上趋势,那么说明写入有瓶颈,有排队。

当监控值超过了 300,会较快的到达 2000+,这时集群的整体性能会大幅度下降,QPS 会明显的减少,当写入处理完之后,会再次上升。

处理方法

  1. 如果单个 tikv 实例内存较大(比如 64G+),推荐调大 TiKV 并发控制相关配置:scheduler-concurrency: 2048000 (2.0.x 较低版本,该值默认较小)

  2. 可以测试写堆积的服务器的盘性能(参考上文 sysbench,或者安装 fio 命令测试)

  3. 可以视情况关闭 sync-log

  4. 可以查看监控,观察是否有写入热点,或者 hot region 不均衡:

  5. 按照上文调度内容进行判断,并且查看是否有 PD 是否有热点调度,并且成功,如若没有,非预期。

  6. 如果 PD 调度过慢,可以参考上文中热点调度方法,找到相关 region,手动 split 并且调度开

冲突

当程序逻辑中,可能会对同一行进行 update/insert/delete 操作时,就会产生冲突。对于主键插入冲突,在 TiDB 中不会重试,其他大多数冲突默认会重试 10 次。

当发生冲突时,TiDB 内部会回滚重试,代价较高,会影响压测性能。根本的解决方法推荐修改程序逻辑,保证先后顺序,避免冲突场景

判断方法

监控中可以查看到一些指标,查看是否有事务冲突,在 TiDB/Server/Failed Query OPM 中有相关错误码,当发现有 1062 关键字时,说明是主键冲突。

监控位置 含义
TiDB/KV Errors/KV Backoff OPS TiKV 返回错误信息的数量(事务冲突等)
TiDB/KV Errors/Lock Resolve OPS 事务冲突相关的数量
TiDB/Server/Failed Query OPM 失败 SQL 的统计,例如语法错误、主键冲突等

冲突处理

  1. 当发现有冲突时,可以使用关键字 confict 对 tidb.log 进行冲突匹配,技巧:

  2. 在冲突记录的行中,有 txn 号全局唯一,可以匹配 txn 号找到相关 SQL

  3. 如果找不到相关 SQL,则大概率是 TiDB 内部冲突/异常,需要联系研发同学进行分析

  4. 确认冲突相关 SQL 后,反查相关业务,确认冲突场景是否可以避免

TiKV / Thread CPU

在进行压测分析时,没有发现服务器性能打到瓶颈,那么推荐快速查看 TiKV 的 Thread CPU 监控模块,其中有写单线程或者默认线程数较低的组件是否成为了瓶颈。

组件 用途 是否是瓶颈 首选解决方案
grpc pool 接收请求,发送响应 相关配置:server.grpc-concurrency

默认配置是 4,CPU 打到 400% 就满了|把这个配置调大,比如 6 或更大| |storage readpool|处理写入和部分查询|相关配置:readpool.storage

默认配置是 4,CPU 打到 400% 就满了|把这个配置调大,比如 8 或更大| |coprocessor|处理 SQL 查询|相关配置:

readpool.coprocessor

默认配置:核数 * 0.8|加机器| |raftstore|处理写入请求|3.X 版本的相关配置:

raftstore.store-pool-size

默认值是 2,CPU 打到 200% 就满了|把这个配置调大,比如 4 或更大| |2.X 版本|加 TiKV 实例| |async apply|处理写入请求|3.X 版本的相关配置:

raftstore.apply-pool-size

默认值是 2|把这个配置调大,比如 4 或更大| |2.X 版本|加 TiKV 实例| |scheduler worker|处理事务请求|storage.scheduler-worker-pool-size,核数小于 16 时,默认配置是 4,即 CPU 达到 400% 就满了;核数大于等于 16 时默认为 8|把这个配置调大|

其他

连接数

TiDB 没有连接数限制,但是只会同时处理 1000 个请求,其余将会排毒,当发现监控中 TiDB / connect count 超过了 1000,那么可能需要调整相关参数:tidb-ansible/conf/tidb.yml 中的 token-limit: 1000。

日志

tidb.log 日志解析

关键字

  1. conflict // 冲突
  2. txn // 事务号
  3. SLOW_QUERY // 慢日志,新版本已经单独隔离出来
  4. Welcome // 启动关键字
  5. Out Of Memory Quota // OOM 关键字

技巧

  1. 开启 general log,根据 session 关键字匹配,可以看到同个 session 的所有操作内容,来确认程序到数据库,究竟做了什么
  2. tidb.log 默认所有 DDL 都会记录
  3. 可以确认 SQL 到 TiDB 的时间点

相关操作

  1. 修改日志级别

  2. 修改配置文件,进行滚动修改:

  3. vi tidb-ansible/conf/tidb.yml // 修改配置文件

  4. level: “debug” // 配置日志级别为 debug

  5. ansible-playbook rolling_update.yml --tags=tidb // 滚动 tidb-server

  6. 使用 API 修改(注意 TiDB 版本):

  7. curl -X POST -d “log_level=debug” http://{TiDBIP}:10080/settings // 修改 tidb 日志级别为 debug

  8. curl -X POST -d “log_level=info” http://{TiDBIP}:10080/settings // 修改 tidb 日志级别为 info

  9. 注意

  10. 将日志级别调高,比如 error 级别,理论上 tidb 性能会有一定提升

  11. 修改日志级别之后,一定要查看 tidb.log 的内容,确认修改成功

tidb_slow_query.log 日志解析

关键字

  1. Txn_start_ts
  2. Query_time // 实际执行时间

技巧

  1. 将慢日志阈值调整为 0,可以将所有 SQL 都打入慢 SQL 日志
  2. Query_time 是 SQL 在 tidb 中执行的时间,不包括 SQL parser 解析时间。
4赞

3.0 中 information_schema 里面有很多信息,比如hot region,slow query,集群拓扑,都可以通过 SQL 获取,不需要像文中那么麻烦

是的,这个是之前的文章,在 TiDB 3.0 后,我们可以通过系统视图的方式来直接查看慢查询信息。

有新的文章吗?刚好想查一些用法

同求。现在准备将生产环境升级到3.0.3了。。想更新一下知识库

目前没有新文章,我们可以考虑针对性的更新些内容,你这边具体需求都有那些?

mark:smiley:

@muyubaby 这个

主要需求是如何使用 SQL 来进行慢查询,hot region,实时读写流量等故障排查(其实 INFORMATION_SCHEMA这些信息都有)

ok,get

大赞:+1:

这是根据region_id ,找出表名?

根据table_id找表名。

我想要的是根据region_id找table_id

curl http://{tidbip}:10080/regions/{region id}