伴鱼技术团队

Technology changes the world

伴鱼开放平台 上线了! 源于实践的解决方案,助力企业成就未来!

TiDB升级之路:从 2.1.15 到 3.0.13

背景

随着公司业务的发展,数据量的持续增加,针对目前数据库基于TiDB 2.1使用过程中遇到的一些问题,例如:瞬间插入性能差,偶尔爆出insert的慢sql,select查询不能准确的使用合适的索引,数据量较大的集群Raftstore单线程压力的问题,以及考虑官方对2.1版本的优化和问题支持的滞后等问题,同时带着对3.0版本新功能和性能提升的期待,开始了tidb集群3.0大版本的升级之路。

升级调研

正如DBA对数据库稳定运行的保障工作一样,TiDB进入3.0版本,官方在系统稳定性、易用性、功能、优化器、统计信息以及执行引擎做了持久的优化改进,稳定性和读写性能有了非常大的提升。

TiDB 3.0优化Raft副本之间的心跳机制,按照Region的活跃程度调整心跳频率,减小冷数据对集群的负担。优化了PD调度流程。新增Fast Analyze功能,提升收集统计信息的速度,降低集群资源的消耗及对业务的影响。其它特性还包括:SQL优化器的提升、支持窗口函数,悲观事务(从2.1升级后的版本依然是乐观事务,新部署的集群3.0.8以后支持悲观事务)、视图、分区表、SQL Trace等新特性。

升级准备

经过一段时间的调研和参数对比,参考官方升级说明并结合部署结构等实际情况,详细制定了升级方案,机器调整方案(包括机器的扩容、下线)重启流程以及回滚方案。操作升级选择在业务低峰期,在这个窗口期,可以对现有集群做一些优化调整(原因是会涉及到集群重启,比如:PD机器的扩容,调整),最终达到各组件机器硬件配置标准化、配置参数标准化、监控及部署结构标准化。升级准备工作如下:

  • 从2.1.15升级到3.0.13版本跨度较大,是否存在复杂场景sql查询不一致的问题,目前已知的问题是where条件后存在小括号时,索引使用不准确的问题。
  • 不支持在升级后回退至2.1或更旧的版本。
  • 在升级的过程中不能执行DDL,否则可能会出现行为未定义的问题。正是这个限制在升级过程中第一台是遇到DDL OWNER机器,导致一个集群升级失败。通过与官方支持沟通最终解决了问题。也为顺利完成其他集群的升级总结了经验。
  • 确认并对比每个集群TiDB、TiKV、PD、Grafana、Prometheus 等组件的组参数的变化,老版本集群配置项不一致,包含日志路径,新增配置,或已有参数的默认值改变等,避免出现因配置不同导致出现问题。

升级过程

对每个需要升级的集群,需要编辑并配置的文件包括inventory.ini、tidb.yml、pd.yml、tikv.yml,通过滚动升级的方式对集群中各个机器的组件进行升级。滚动升级的顺序是:PD->TiKV->PUMP->TiDB

⚠️注意:此处有2个过程会导致连接出现闪断:

A)滚动升级PD Leader时,PD重新选举Leader导致所有连接无法操作数据库
B)滚动升级TiDB过程中,重启TiDB-Server过程中会断开服务的连接

由于每个集群部署时期跨度大使用机器的硬件配置有差异,本次升级过程中对10个集群的机器配置实现了标准化。总调整机器数40台左右。
Tidb_server、PD_server、tikv_server、pump机器在升级过程前后集群的扩容与缩容,回收了高配阿里云机器,节省了成本同时也提高了集群的负载能力。

由于在升级的过程中,应避免执行DDL操作,所以在inventory.ini配置文件里[tidb_servers]项,第一个配置避开DDL OWNER机器

下图:查询DDL OWNER机器

下图:升级过程3.0新的系统表的DDL操作

下图:查看执行的DDL语句

在滚动升级TiKV过程中,需要调整代码延长Transfer leader时间,来减少滚动TiKV过程的性能抖动,如果提前transfer leader完成以后,会停止check,继续下面的工作。下面是调整方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
vim tidb-ansible-v3.0.13/common_tasks/add_evict_leader_scheduler.yml 
- name: check tikv's leader count
uri:
url: "http://{{ pd_addr }}/pd/api/v1/store/{{ store_id }}"
method: GET
return_content: yes
body_format: json
status_code: 200
register: store_info
until: (store_info.json.status.leader_count is defined and store_info.json.status.leader_count|int < 1) or store_info.json.status.leader_count is not defined
retries: 18 ##我们把18调整到了80
delay: 10
failed_when: false
when: not enable_tls|default(false)

下图:升级过程中tikv transfer leader的过程

升级带来的收益

  • 版本升级后,通过监控Duration指标,999线从4秒降低到150多毫秒。同时业务SQL的查询性能有较高的提升,单纯从大数据集群恢复备份对比看,数据灌入的速度有4倍左右提升。
  • 所有集群升级后自动开启了数据的merge功能,减少了空region给TiDB带来的通讯压力,并释放了磁盘空间。反过来看,DB资源不回收,在数据量达到一定量级时,会是一个非常棘手的问题。同时总的机器使用数量会增加,集群内部机器消耗和稳定性也会有一定的影响。
  • 统一了各组件部署机器的配置,隔离了不合理混部带来的风险,tikv机器全部迁移到物理机器上,提高了集群的抗压能力,可以应对较大的突发流量。
  • 单次事务操作的数据量从5千提升到了30万,核心集群的DML操作依然要谨慎操作,避免大事务导致的锁和性能飙升,影响到生产业务。
  • 大数据集群上游同步了4个生产集群,集群本身计算任务产生的临时表,特殊的使用场景,每天删除部分表的数据或删除整个表,再计算产生新的表或数据插入tidb。在2.1版本没有开启merge的情况下,遗留下大量的未回收的region。(在2.1版本即使开启merge,对region的合并依然不完全),升级后还会有大量的region合并,从而大大减少空region数量,降低通讯消耗。下图展示了region数减少的过程:


上图:region merge过程中空region在减少

上图:tidb集群总的region number在减少

升级后的变化

  • 现有的监控界面图改进,新增了监控项,比如:Region Health
    空region个数的统计和准确程度,提高了Leader Balance,Region Balance准确度。
  • 由于每个TiDB集群独立使用一个prometheus导致采集的监控数据不集中,不好实施监控告警的配置,我们在升级后采用prometheus联邦功能将每个集群的监控数据收集到一个prometheus集群里,完成告警的统一配置。
  • 整个TiDB集群机器数量越多,采集的监控数据量会越来越多。Prometheus数据刷盘的数据和频率会增加。导致磁盘IO瞬时的飙升,可以考虑调整数据落盘的频率
  • 慢日志格式统一,新增Plan项Plan:表示语句的执行计划,使用 select tidb_decode_plan(‘xxx…’) SQL 语句可以解析出具体的执行计划。

最新版的慢查询事例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 Time: 2020-06-15T19:44:20.738011501+08:00
Txn_start_ts: 417391302532137136
User: product@10.100.100.134
Conn_ID: 4560686
Query_time: 0.250088873
Parse_time: 0.000021011
Compile_time: 0.000154348
Process_time: 0.266 Wait_time: 0.002 Request_count: 12 Total_keys: 35927 Process_keys: 34921
DB: growthsystem
Index_names: [growthsystem_rank_day:idx_dt]
Is_internal: false
Digest: aa618de9f9cba86d3ccb6a8d41bdf94ecb0a139b17692ebef4ac0d6ab9c8b5d9
Stats: growthsystem_rank_day:417391301273846080
Num_cop_tasks: 12
Cop_proc_avg: 0.022166666 Cop_proc_p90: 0.065 Cop_proc_max: 0.115 Cop_proc_addr: 10.106.9.13:20160
Cop_wait_avg: 0.000166666 Cop_wait_p90: 0.001 Cop_wait_max: 0.001 Cop_wait_addr: 10.106.9.13:20160
Mem_max: 2298044
Prepared: false
Has_more_results: false
Succ: true
Plan: tidb_decode_plan('8AGYMAkzMV8xMAkwCTIzNzU4Ljc2MzM0MTgyMDQyNAkKMQkxM184CTEJSh0A8El0YWJsZTpncm93dGhzeXN0ZW1fcmFua19kYXksIGluZGV4OmRhdGUsIHRvdGFsLCB1dCwgcmFuZ2U6WzE1OTIxNTA0MDAgMTAwLDYPAEhdLCBrZWVwIG9yZGVyOmZhbHNlAYgUMF85CTEJvogAQkoA')
Plan_digest: 3c2d2aee62687159f33c71ad0eb6dd83c339c474b63cc39fae91630914abb30a
SELECT * FROM growthsystem_rank_day WHERE date=1592150400 AND total=100;

优化了慢查询日志的查询方法,通过内存表 INFORMATION_SCHEMA.SLOW_QUERY,ADMIN SHOW SLOW 语句查询慢查询,原来的方式都是通过慢日志文件查看,升级后可以在表里查询。查询事例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
  select * from INFORMATION_SCHEMA.SLOW_QUERY limit 1\G
*************************** 1. row ***************************
Time: 2020-05-20 00:07:01.418867
Txn_start_ts: 416783904547012615
User:
Host:
Conn_ID: 0
Query_time: 0.279259531
Parse_time: 0.000052662
Compile_time: 0.000111603
Prewrite_time: 0
Wait_prewrite_binlog_time: 0
Commit_time: 0
Get_commit_ts_time: 0
Commit_backoff_time: 0
Backoff_types:
Resolve_lock_time: 0
Local_latch_wait_time: 0
Write_keys: 0
Write_size: 0
Prewrite_region: 0
Txn_retry: 0
Process_time: 0.236
Wait_time: 0.001
Backoff_time: 0
LockKeys_time: 0
Request_count: 14
Total_keys: 101019
Process_keys: 91607
DB:
Index_names: [stats_buckets:tbl]
Is_internal: 1
Digest: 1b3fae494a405ffc5252cd18390a98d751473ad35dfa6acc7220713c90532bf0
Stats: stats_buckets:pseudo
Cop_proc_avg: 0.016857142
Cop_proc_p90: 0.05
Cop_proc_max: 0.075
Cop_proc_addr: 10.111.203.238:20160
Cop_wait_avg: 0.000071428
Cop_wait_p90: 0
Cop_wait_max: 0.001
Cop_wait_addr: 10.111.203.238:20160
Mem_max: 47997076
Succ: 1
Plan: Projection_5 root 10000 mysql.stats_buckets.table_id, mysql.stats_buckets.is_index, mysql.stats_buckets.hist_id, mysql.stats_buckets.count, mysql.stats_buckets.repeats, mysql.stats_buckets.lower_bound, mysql.stats_buckets.upper_bound
└─Projection_13 root 10000 mysql.stats_buckets.table_id, mysql.stats_buckets.is_index, mysql.stats_buckets.hist_id, mysql.stats_buckets.bucket_id, mysql.stats_buckets.count, mysql.stats_buckets.repeats, mysql.stats_buckets.upper_bound, mysql.stats_buckets.lower_bound
└─IndexLookUp_12 root 10000
├─IndexScan_10 cop 10000 table:stats_buckets, index:table_id, is_index, hist_id, bucket_id, range:[NULL,+inf], keep order:true, stats:pseudo
└─TableScan_11 cop 10000 table:stats_buckets, keep order:false, stats:pseudo
Plan_digest: 54bbce67d7e4f533792032e077071a56595a385a0aad02029f230125064bd9d3
Prev_stmt:
Query: select HIGH_PRIORITY table_id, is_index, hist_id, count, repeats, lower_bound, upper_bound from mysql.stats_buckets order by table_id, is_index, hist_id, bucket_id;

升级后的问题及挑战

  • 集群升级后,事务依然采用乐观锁的方式,在高并发时锁冲突比较严重。
  • 3.0的SQL优化器依然存在使用错误索引的情况,必要情况下还需要使用hint。
  • 3.0版本目前还不支持官方提到的BR物理备份,短期内还需要沿用现有的备份方式。
  • 保证DB的稳定运行是长期且最重要的工作。期间包含很多的风险的发现和故障处理预案,突发事件的处理。

欢迎关注我的其它发布渠道