https://tidb.net/book/tidb-monthly/2022/2022-07/usercase/tidb-v5-1-2
笔者最近在一个银行项目中做 PoC 测试,由于客户选择了使用 TiDB 数据库,于是笔者在 TiDB 中选择了一个相对稳定并且 bug 较少的版本:TiDB v5.1.2。
虽然 bug 较少,但是在测试过程中,还是不可避免的发现了一些问题,并通过参数来调整解决。
经过 PoC 测试和方案的制定,就迎来了生产环境的部署。生产环境的部署就不像是部署测试环境那么简单了。测试环境还能“从简”出发,减少一些非必要的配置项的设置,比如 CPU 频率的 cpufreq 模块是否选用 performance 模式;存储介质的 I/O 调度器是否设置为 noop。但是在生产环境中,我们必须把所有优化项都设置好,哪怕只是优化一点点也不能放过。
本文会把项目中的部署架构以及生产环境中需要注意的事项都整理出来,给大家一些参考。
在项目中,客户对容灾要求较高,希望不管是哪个数据中心出现问题,TiDB 数据库都可以无需人工干预并且无缝切换到另一个中心,直接提供服务。根据官网,目前有两种方案:
最后,根据目前的情况,商量出来了一个方案:在两个数据中心部署两套 TiDB 集群,用 TiCDC 组件同步两个集群的数据。由于是两个不同的集群,万一主数据中心出现问题,也不会影响到从数据中心。利用 F5 负载均衡实现主数据中心故障后,自动切换到从数据中心,达到无需人工干预就可以无缝切换到从数据中心的需求。
生产环境检查和测试环境检查就有很大的区别了,主要体现在检查项目变多和检查更加细节,本文只列举,除官方文档之外需要检查的部分:
主要是查看物理机配置是否与客户给的一致,包括 CPU (观察核数以及是否开启超线程),网卡(是否万兆网卡),硬盘(数据盘是否为 NVME SSD 磁盘)等等。并且需要确认 NVME 磁盘使用率不能超过 5%。这些配置一般都没啥问题,但是该检查还是得检查一下,万一出现配置不对的情况,这个锅我们不背!
网络环境一定要检查!网络环境一定要检查!网络环境一定要检查!
重要的事情一定要多说几遍。生产环境中的网络一般很复杂,所以一定要检查集群中网络是否符合要求。如果网络不到万兆,或者网络波动很大,都会对 TiDB 集群产生不好的影响。
磁盘除了要使用 NVME 和检查挂载方式以外,还需要检查磁盘 IO 是否达到 TiDB 集群的最低要求:
指标 | 标准值 |
---|---|
随机读测试 read iops | 不低于 40000 |
随机读与顺序写混合测试 read iops | 不低于 10000 |
随机读与顺序写混合测试 write iops | 不低于 10000 |
随机读与顺序写混合测试 read latency (ns) | 不高于 250000 |
随机读与顺序写混合测试 write latency (ns) | 不高于 30000 |
测试方法:
# 测试脚本准备
wget http://download.pingcap.org/fio-3.8.tar.gz
tar -xzvf fio-3.8.tar.gz
cd fio-3.8
ls
fio parse_fio_output.py
# 包含fio测试文件以及解析测试结果的python脚本
随机读测试 read iops 不低于 40000
[root@tikv01 ~]# ./fio -ioengine=psync -bs=32k -fdatasync=1 -thread -rw=randread -size=10G -filename=fio_randread_test.txt -name='fio randread test' -iodepth=4 -runtime=60 -numjobs=4 -group_reporting --output-format=json --output=fio_randread_result.json
[root@tikv01 ~]# rm fio_randread_test.txt
[root@tikv01 ~]# python parse_fio_output.py --target='fio_randread_result.json' --read-iops
[root@tikv01 ~]# ./fio -ioengine=psync -bs=32k -fdatasync=1 -thread -rw=randrw -percentage_random=100,0 -size=10G -filename=fio_randread_write_test.txt -name='fio mixed randread and sequential write test' -iodepth=4 -runtime=60 -numjobs=4 -group_reporting --output-format=json --output=fio_randread_write_test.json
[root@tikv01 ~]# rm fio_randread_write_test.txt
[root@tikv01 ~]# python parse_fio_output.py --target='fio_randread_write_test.json' --read-iops
[root@tikv01 ~]# python parse_fio_output.py --target='fio_randread_write_test.json' --write-iops
[root@tikv01 ~]# ./fio -ioengine=psync -bs=32k -fdatasync=1 -thread -rw=randrw -percentage_random=100,0 -size=10G -filename=fio_randread_write_latency_test.txt -name='fio mixed randread and sequential write test' -iodepth=1 -runtime=60 -numjobs=1 -group_reporting --output-format=json --output=fio_randread_write_latency_test.json
[root@tikv01 ~]# rm fio_randread_write_latency_test.txt
[root@tikv01 ~]# python parse_fio_output.py --target='fio_randread_write_latency_test.json' --read-lat
[root@tikv01 ~]# python parse_fio_output.py --target='fio_randread_write_latency_test.json' --write-lat
关于互信,这里的操作与官方文档稍有不同。为了避免人为配置互信出现问题,笔者在这一步只创建了 tidb 用户,并且按照官方文档赋予了最高权限,并没有手动去配置互信。笔者在最后的部署阶段,是默认用 tiup 自动去配置各个主机间的互信的,这一步不需要人为干预。
这次是把 TiDB 集群部署在国产海光 CPU 上,所以一定需要用 numa 绑核才能发挥出完全的性能。为什么一定要绑定 numa 才能发挥出应有的性能呢?这个可以看看秦老师的一篇文章:
专栏 - 单机 8 个 NUMA node 如何玩转 TiDB - AMD EPYC 服务器上的 TiDB 集群最优部署拓扑探索
关于 numa 绑核,笔者这次也是踩了坑。说说这是个什么坑吧,一句话概括就是,两个 NVME 硬盘都挂载到同一个物理 CPU 上了,而且想要挂载到不同的物理 CPU 上,条件还不允许。最优的情况是,两个 NVME 硬盘平均挂载到两个不同的物理 CPU 上,这样 CPU 去调用磁盘会更快。
为什么笔者的情况无法解决呢?因为物理机上有一个东西:背板。磁盘是先插到背板上的,由背板走线直接连接到不同的物理 CPU 上。但是背板是有不同型号的,笔者遇到的物理机背板只有两块,一块是只能插 sata 接口的,另一块是只能插 NVME 接口的,这种情况下,两块 NVME 只能插在同一个背板上,而背板只能连接到某一个物理 CPU 上,所以无法分开。所以,在前期规划部署的时候,这一点需要注意。
关于两块 NVME 连接到一个物理 CPU 的性能损耗,得到厂商回复:
连接到两块CPU和连接到一块CPU,访问延迟最多多了240纳秒,这240纳秒主要是CPU到CPU之间的访问延迟。
还有一个小坑要注意一下,麒麟v10是默认开启 numa 自动均衡的,所以一定要手动关闭:
sysctl -w kernel.numa_balancing=0
目前来看,TiDB v5.1.2 的版本也算是有点老了,所以不可避免的还是有点小 bug 的,但是这些 bug 都可以根据参数的设置而避免。那么这个配置参数就非常重要了。根据 PoC 测试和从社区整理的拓扑配置最佳实践如下:
server_configs:
tidb:
# 日志最大保留的天数
log.file.max-days: 15
# 日志的输出级别
log.level: info
# 最长的 SQL 输出长度
log.query-log-max-len: 65536
# 输出慢日志的耗时阈值
log.slow-threshold: 300
# 用于设置新建索引的长度限制
max-index-length: 12288
# 单条 SQL 语句可以占用的最大内存阈值
mem-quota-query: 10737418240
# 单条 SQL 超过内存限制时,取消执行该 SQL 操作
oom-action: cancel
# 在单个事务的提交阶段,用于执行提交操作相关请求的 goroutine 数量
performance.committer-concurrency: 128
# 关闭对查询收集统计信息反馈
performance.feedback-probability: 0
# 单个事务允许的最大语句条数限制
performance.stmt-count-limit: 50000
# 单个事务大小限制
performance.txn-total-size-limit: 10737418240
# 缓存语句的数量
prepared-plan-cache.capacity: 100
# 开启 prepare 语句的 plan cache
prepared-plan-cache.enabled: true
# 防止 prepare plan cache 的内存用量过大
prepared-plan-cache.memory-guard-ratio: 0.1
# 关闭批量发送 rpc 封包(解决 Compaction Filter GC 可能不删除 MVCC deletion 信息的问题,v5.1.3 修复)
tikv-client.max-batch-size: 0
tikv:
# 关闭 GC in Compaction Filter 特性(解决 Compaction Filter GC 可能不删除 MVCC deletion 信息的问题,v5.1.3 修复)
gc.enable-compaction-filter: false
# 日志等级
log-level: info
tiflash:
# 解决因大量 delete 导致 tiflash 无法使用的 bug
profiles.default.dt_enable_skippable_place: 0
系统参数最佳实践如下:
# 统计信息版本设置
set global tidb_analyze_version=1;
# 关闭 Async Commit 特性
set global tidb_enable_async_commit=0;
# 关闭一阶段提交特性
set global tidb_enable_1pc=0;
# 唯一索引的重复值检查不推迟到事务提交时进行
set global tidb_constraint_check_in_place = 1;
# 垃圾回收 (GC) 时保留数据的时限
Set global tidb_gc_life_time = '8h';
# sql mode设置
set global sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';
笔者只写出了基本需要调整的参数,参数数值可以根据项目情况来更改。也可以增加一些笔者没写出来的参数并进行调整。
配置文件参数详情:TiDB 配置文件描述 ,TiKV 配置文件描述,PD 配置文件描述,TiFlash 配置参数
系统变量参数详情:系统变量