TiDB MVCC 版本堆积相关原理及排查手段

news2025/1/25 9:26:16

导读

本文介绍了 TiDB 中 MVCC(多版本并发控制)机制的原理和相关排查手段。 TiDB 使用 MVCC 机制实现事务,在写入新数据时不会直接替换旧数据,而是保留旧数据的同时以时间戳区分版本。 当历史版本堆积过多时,会导致读写性能下降。 为了解决这个问题,TiDB 使用 Garbage Collection(GC)定期清理不再需要的旧数据。 文章从 TiDB 中 MVCC 版本的生成原理、数据写入过程和 TiDB 版本堆积常见排查手段等方面进行了详细介绍 。


TiDB 的事务的实现采用了 MVCC(多版本并发控制)机制,当新写入的数据覆盖旧的数据时,旧的数据不会被替换掉,而是与新写入的数据同时保留,并以时间戳来区分版本。 Garbage Collection(GC)的任务便是清理不再需要的旧数据。

如上所述,TiDB 底层使用的是单机存储引擎 rocksdb, 为了实现分布式事务接口,TiDB 又采用 MVCC 机制,基于 rocksdb 实现了高可用分布式存储引擎 TiKV。也就是当新写入(增删改)的数据覆盖到旧数据时,旧数据不会被替换掉,而是与新写入的数据同时保留,并以时间戳来区分版本。当这些历史版本堆积越来越多时,就会引出一系列问题,最常见的便是读写变慢。TIDB 为了降低历史版本对性能的影响,会定期发起 Garbage Collection(GC) ( https://docs-archive.pingcap.com/zh/tidb/v7.2/garbage-collection-overview ) 清理不再需要的旧数据。

本文作为 TiDB GC 的前序文章,我们将详细介绍一下这些旧版本数据是如何堆积起来的,以及如何排查确认当前版本数据的堆积已经对集群性能构成了影响。

TiDB 中的 MVCC 版本的生成原理

  • 在 TIDB 层,我们最初收到的是一个关系型表的数据,TiDB 会将这个关系型表数据转化成 key-value,同时调用分布式事务接口,将 key-value 数据写入到 TiKV。
  • 在 TIKV 层,我们采用 MVCC 机制提供了分布式事务接口,最终所有的写入都会转化成一条 MVCC key-value 格式写入到 raftstore. 说到 MVCC 格式的 key-value, 无非就是每一个 key 上都有一个版本号,代表其提交的先后顺序。后面我们将这类格式的数据统一称为 MVCC key-value 对。
  • 在 raftstore 层,则最终将数据以 key-value 的形式,写入到 rocksdb 中。(注意,rocksdb( https://docs.pingcap.com/tidb/stable/rocksdb-overview ) 本身基于 LSM 架构实现,所以它也有 MVCC 的概念,本文不做详细介绍,只对 TiDB 相关的内容点到为止)

数据写入过程

下面我们举个例子来详细讲讲在 TiDB 集群中,一个具体的写入过程。

当我们在 TiDB 中,执行以下 SQL 时:

insert into students set name="Bob",age=12,score=99 

1.1 TiDB  SQL table 转为 Key-Value

在 TiDB 层,我们有以上关系型表,上面这一行数据最终会变成三对 key-value(详细原理 https://book.tidb.io/session1/chapter3/tidb-kv-to-relation.html ),分别对应:

  • 主键对需要保证 key 唯一性:主键值 => 本行所有列数据
  • 唯一索引按 key 有序排列加速查询速度:name 列:唯一索引 => 主键
  • 非唯一索引按 key 有序排列提升查询性能:age 列:索引+主键 => 空值

1.2 TiKV 侧 MVCC 版本写入

在 TiKV 层,分布式事务接口在收到对应的 key-value 对后,会转成对应的 MVCC key-value 写入到 raftstore. 这里我们不展开分布式事务的具体实现逻辑,只用最简单的乐观锁模型( TiDB 最佳实践系列(三)乐观锁事务 - 知乎 )来举例。

Prewrite 接口完毕后:

其中锁 Lock CF(无版本号)如下:

  • t{table-id}r1=> start_ts,primary_key,ttl,PUT
  • t{table-id}i{indexID}_Bob=>start_ts,primary_key,ttl,PUT
  • t{table-id}i{indexID}_12_1=>start_ts,primary_key,ttl,PUT

数据 Default CF(mvcc 版本号 start_ts 在 key 的后缀里)如下:

  • t{table-id}r1_{start_ts}=> {Bob,12,99}
  • t{table-id}i{indexID}Bob{start_ts}=>1
  • t{table-id}i{indexID}_12_1{start_ts}=>null

具体实现中,会有一个优化,即当 value 值不是很大时,不会将数据单独放在 Default CF 里面(这里不展开具体介绍)。

Commit 接口调用完毕之后:

以主键对为例子,数据会发生如下变化:

Write CF 里面写入:

  • t{table_id}{commit_ts}=>start_ts

Lock CF 中对应 key 被删除(注意这里是 rocksdb 的一次删除,rocksdb 底层 LSM 也是 mvcc, 即删除对 rocksdb 也是写入一个新版本):

  • t{table-id}r1=> start_ts,primary_key,ttl,PUT

综上,我们 以 主键所在 key 为例 ,展开 讲讲这个 key 随着增删改 mvcc 版本的变迁。

transaction 1: insert set id=1

transaction 2: update where id=1

update 之后,在 raftstore 里面留下的 mvcc 信息如下:

也就是说,update 并没有直接去更新上一次写入的内容,而是重新写了一份数据到底层。

transaction 3: delete id=1

那如果我们 delete id=1 的这一行数据呢?从下面我们可以看到,delete 也是通过写入一个新版本到底层。

综上,当我们对 id=1 依次做了 insert/update/delete 之后,对于 TiDB 客户端来说,这一行数据已经删除,但是对于存储底层来说,此时在 raftstore 层留下了以下多个 mvcc 版本。

可以看到,同一行数据会随着增删改的次数,积累越来越多的版本,这里历史的 mvcc 版本如果不及时清理,不光物理磁盘空间无法释放,更会对读写产生性能影响,所以我们需要 GC 来对这些旧版本数据进行回收。

TiDB 版本堆积常见排查手段

如前文所说,当 MVCC 版本出现堆积时,会对读写造成性能影响,此时,我们就需要对 GC 参数及状态进行判断,加速旧版本数据的回收,提升集群读写性能。

那么,在实际的业务场景中,如何判断我们的 MVCC 数据版本是否出现堆积,并对当前集群读写性能造成了影响呢?

2.1 Slow log 视角(具体慢 SQL 视角)

如前文所说,MVCC 版本堆积最直接的影响是读写变慢,所以我们从 slow log( https://docs-archive.pingcap.com/zh/tidb/v7.2/identify-slow-queries ) 可以来排查 SQL 执行慢的原因是否是 mvcc 历史版本是否堆积过多。

tidb slow log: scan_detail: {total_process_keys: 1139428, total_process_keys_size: 433849330, total_keys: 1139434, rocksdb: {delete_skipped_count: 0, key_skipped_count: 2278852,....

上面摘取的一段日志是 slow log 里面,与 TiDB mvcc 版本数量有关的几个字段:

  • total_process_keys: 本次查询扫描的有用的用户 key 个数。不包含已删除的版本及 rocksdb 里面 tombstone 的版本
  • total_keys :本次查询总共扫的 mvcc 版本个数

当 total_keys > total_process_keys*6 时,代表着查询范围内的平均每个 key 的 mvcc 版本是 6 以上,需要注意 GC 的相关参数是否合理,检查 GC 的状态是否正常。

Rocksdb 相关指标 (rocksdb 里面的 mvcc):

  • delete_skipped_count: rocksdb 里面被标记为删除的 key. 当这个值比较大时,意味着 rocksdb 需要做 compaction 了
  • key_skipped_count( https://github.com/facebook/rocksdb/blob/9f1c84ca471d8b1ad7be9f3eebfc2c7e07dfd7a7/include/rocksdb/perf_context.h#L84 ): 实际读取过程中,rocksdb 读取迭代器中执行 next 的个数,代表着实际数据的读取量

下面我们举个例子来加深理解 slow log 里面的这些字段。(注意后续所有的例子 SQL 中,查询语句需要加上“explain analyze( https://docs.pingcap.com/tidb/stable/sql-statement-explain-analyze )” 才能看到具体的 mvcc 扫描详情)

Step 1:创建表结构

  • total_process_keys 是 0,因为它是一张空表。
  • total_keys =1 因为我们在查询之前并不知道这张表是否为空,需要拿出符合条件的第一个 MVCC 版本才能确认这条 mvcc 不是本表数据。

Step 2:插入一条 ID=1 的新数据

可以看到,插入完成后再查询时:

  • total_process_keys =1, 表中当前一共有一行数据(id=1)
  • total_keys =2, 扫完 id=1 的 key 后,还要往后扫一个 key 才能确认此表中已经没有数据

Step 3:更新 ID=1 的这行数据

更新完后再查询时:

  • total_process_keys =1 因为确实这张表中只有一行数据
  • total_keys = 3, 因为 id=1 这行数据有两个版本, 也就是本次更新增加了一个版本

Step 4:删除 ID=1 所在行

删除后执行查询时:

  • total_process_keys =0:删除了 id=1 这行数据后,表里面没有数据了
  • total_keys = 3+1:而删除 id=1 给这行数据增加了一个版本,所以 total_keys 比上一次多了 1 个

Step 5:插入一条 ID=2 的新数据

请尝试自行分析。

2.2 Grafana (集群)视角

因为 slow log 默认只记录 300 ms 以上的 SQL 读取细节,怎么看整个集群 mvcc 读取状态呢?这就需要我们从 grafana 级别来宏观分析了。

分布式事务 mvcc

监控地址:tikv-details->coprocessor-details-> Total Ops Details(TableScan/IndexScan)

如图所说:

Ops 具体分两种:

  • Table scan:代表着按 table 主键查询
  • Index scan:代表着按索引查询

具体监控值分两类:

  • processed_keys:代表查询后实际用户可见的 key 个数,与 slow-log 中的 total_processed_keys 概念一致
  • next/seek/..:代表本次查询在 TiKV 迭代器中每个指令的调用次数,一般 next 居多。所有指令总调用次数接近于 slow log 里面 total_keys

同样的,如果从上图中看到 processed_keys 所在的线如果远远小于 next, 则说明 mvcc 版本冗余对当前的读取已经构成性能影响。

Rocksdb 层看 MVCC

tikv-details->coprocessor->total rocksdb perf statistics:

这里 delete_skipped 主要是指 rocksdb 里面的 tombstone, 对应于 slow log 里面的 delete_skipped_count。

2.3 Region 视角(热点更新表视角)

在实际业务中,我们往往对某些 table 或者 table 中的某些行更新比较频繁,从集群角度看,就只有这些 table 涉及到的 region 的数据版本堆积比较严重。

同时 TiDB 在设计时,要求同一个 key 所在的所有 mvcc 版本数据只能落在一个 region 里面,所以如果 TiDB 中某一行数据更新过于频繁,会导致版本堆积过多而出现大 region 的情况(大于 1 G)。那么在遇到大 region 时,我们如何判断是否出现了这种情况呢?

tikv-ctl( https://docs.pingcap.com/tidb/stable/tikv-control#print-some-properties-about-region )工具提供了命令来查看具体 region 内 mvcc 数据的分布:

tiup ctl:v6.5.0 tikv --host 127.0.0.1:20160 region-properties -r  6493
Starting component `ctl`: /home/tidb/.tiup/components/ctl/v6.5.0/ctl tikv --host 127.0.0.1:20160 region-properties -r 6493
mvcc.min_ts: 442383585748713474
mvcc.max_ts: 442383589195644931
mvcc.num_rows: 410870
mvcc.num_puts: 410870
mvcc.num_deletes: 0
mvcc.num_versions: 410870
mvcc.max_row_versions: 1
writecf.num_entries: 410870
writecf.num_deletes: 0
writecf.num_files: 1
writecf.sst_files: 053983.sst
defaultcf.num_entries: 0
defaultcf.num_files: 0
defaultcf.sst_files: 
region.start_key: 7480000000000000ffe75f728000000000ff3f028e0000000000fa
region.end_key: 7480000000000000ffe75f728000000000ff454f410000000000fa
region.middle_key_by_approximate_size: 7480000000000000ffe75f728000000000ff42250e0000000000faf9dc567895dbfffe

其中我们重点关注 mvcc 为前缀的为 mvcc 相关数据:

  • mvcc.min_ts:这个 region 里面的所有版本中最小(最老)的 tso
  • mvcc.min_ts:本 region 数据中最新的 mvcc 版本 的 tso
  • mvcc.num_rows:用户可见的 key 个数(包含已删除的)= mvcc.num_put+mvcc.num_delete
  • mvcc.num_put:用户可见的 key 个数(不包含已删除的)
  • mvcc.num_delete:用户可见的已删除的 key 数
  • mvcc.num_version:用户可见的 mvcc 版本个数
  • mvcc.max_row_versions:本 region 中版本数最多的那个 key 拥有的版本数量

Rocksdb 的相关指标不详细展开,只需要关注到 *cf.num_deletes 比较高时,可以通过 手动 compaction ( https://docs.pingcap.com/tidb/stable/tikv-control#compact-data-of-each-tikv-manually )指定 CF 来解决。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1575829.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【c语言】strncat函数模拟实现

strncat函数模拟实现 strncat函数在cplusplus网站中的定义 模拟实现源码 //strncat函数模拟实现 #include <stdio.h> #include <string.h> #include <assert.h>char* my_strncat(char* destination, const char* source, size_t num) {assert(destination &a…

第十三届蓝桥杯真题解析

本专栏内容为&#xff1a;算法学习专栏&#xff0c;分为优选算法专栏&#xff0c;贪心算法专栏&#xff0c;动态规划专栏以及递归&#xff0c;搜索与回溯算法专栏四部分。 通过本专栏的深入学习&#xff0c;你可以了解并掌握算法。 &#x1f493;博主csdn个人主页&#xff1a;小…

抖音-引流私域转化模式1.0现场视频,从抖音源源不断把人加到私域,

抖音-引流私域转化模式1.0现场视频&#xff0c;从抖音源源不断把人加到私域&#xff0c;让加到私域的粉丝买单 抖音-引流私域转化模式1.0现场视频&#xff0c;从抖音源源不断把人加到私域 - 百创网-源码交易平台_网站源码_商城源码_小程序源码 课程内容&#xff1a; 01.第一…

JetBrains IDE 2024.1 发布 - 开发者工具

JetBrains IDE 2024.1 (macOS, Linux, Windows) - 开发者工具 CLion, DataGrip, DataSpell, Fleet, GoLand, IntelliJ IDEA, PhpStorm, PyCharm, Rider, RubyMine, WebStorm 请访问原文链接&#xff1a;JetBrains IDE 2024.1 (macOS, Linux, Windows) - 开发者工具&#xff0…

代码随想录阅读笔记-二叉树【合并二叉树】

题目 给定两个二叉树&#xff0c;想象当你将它们中的一个覆盖到另一个上时&#xff0c;两个二叉树的一些节点便会重叠。 你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠&#xff0c;那么将他们的值相加作为节点合并后的新值&#xff0c;否则不为 NULL 的节…

uniapp 密码框的眼睛

效果展示&#xff1a; uniapp input 官网链接&#xff1a;链接 按照官方文档&#xff0c;uni-icon出不来。 通过自己的方法解决了&#xff0c;解决方案如下&#xff1a; 代码&#xff1a; <uni-forms-item name"password"><inputclass"uni-input&quo…

批发批量订购下单商城公众号 小程序_博纳软云

批发批量订购下单商城——满足您的批量采购需求 在如今快节奏的商业环境中&#xff0c;批发批量订购已成为众多企业和个人创业者的首选采购方式。为了满足广大用户的批量采购需求&#xff0c;我们特别推出了批发批量订购下单商城&#xff0c;为您提供一站式的采购解决方案。 …

牛客论坛项目中使用到Redis的地方总结

实体分为很多类&#xff0c;实体的确定要通过实体类型和实体id两个属性同时确定。牛客论坛中使用到了3类实体&#xff1a; 1 登录 使用到的Redis命令&#xff1a; set key value // 设置指定key的值为value get key // 获取指定key的值1.1 存储/获取验证码 验证码文本&…

【iOS】UITableView性能优化

文章目录 前言一、优化的本质二、卡顿产生原因三、CPU层面优化1.使用轻量级对象2.cellForRowAtIndexPath方法中不要做耗时操作3.UITableView的复用机制4.提前计算好布局了解tableView代理方法执行顺序cell高度计算rowHeightestimatedRowHeight 高度计算进行时机rowHeight计算时…

无参数绕过RCE

一.什么是无参数 顾名思义&#xff0c;就是只使用函数&#xff0c;且函数不能带有参数&#xff0c;这里有种种限制&#xff1a;比如我们选择的函数必须能接受其括号内函数的返回值&#xff1b;使用的函数规定必须参数为空或者为一个参数等 无参数题目特征 if(; preg_replace…

这就是AI眼中的物理世界:OpenAI Sora音乐短片《Worldweight》和超现实影片《气球人》

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;所以创建了“AI信息Gap”这个公众号&#xff0c;专注于分享AI全维度知识…

Java8关于Function接口

Java学习-Function接口 1 函数式接口简介和学习地址2 两种常见的函数式接口2.1 Runnable&#xff1a;执行接口&#xff0c;不接收参数&#xff0c;也无返回结果。2.2 Consumer&#xff1a;作为消费接口&#xff0c;接收一个参数&#xff0c;无返回结果。 3 初识3.1 定义Functio…

JVM从1%到99%【精选】-初步认识

目录 &#x1f95e;1.什么是JVM &#x1f37f;2.JVM的功能 &#x1f953;3.常见的JVM &#x1f32d;4.JVM的位置 &#x1f9c2;5.JVM的整体结构 &#x1f383;6.JVM的生命周期 &#x1f388;7.JVM的架构模型 1.什么是JVM JVM本质上是一个运行在计算机上的程序,他的职责…

WPS快速将插入Excle数据插入Word

前置条件&#xff1a; 一张有标题、数据的excle表格word中的表格与excle表格标题对应或包含电脑已经安装WPS软件 第一步、根据word模板设计excle模板&#xff0c;标头对应 第二步、word上面选【引用】--【邮件】&#xff0c;选打开数据源&#xff0c;找到excle文件&#xff0c;…

vscode-插件开发-hello world-创建初始模板

参考vscode官方示例&#xff1a;如何创建你的第一个插件开发项目模板的步骤进行了下文操作。 目录 前言1.环境配置全局安装 yo, generator-code 2. 新建一个插件项目模板问题1: F5 按键无法启动launch.json调试(解决)问题1 描述:问题1: 找错误问题1: 可行的解决方案 3. 开发插…

Linux部署sonarqube+Gogs+Jenkins(二)

Linux部署sonarqubeGogsJenkins 一、Jenkins执行任务1、使用源码管理拉取代码-操作步骤第一步&#xff1a;确认环境&#xff0c;进入到Jenkins【系统管理】—>【全局工具配置】—>【Git】为下图显示&#xff1b;第二步&#xff1a;构建项目时对项目的源码管理选择 Git第三…

基于R语言地理加权回归、主成份分析、判别分析等空间异质性数据分析教程

原文链接&#xff1a;基于R语言地理加权回归、主成份分析、判别分析等空间异质性数据分析教程https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247600473&idx6&sn431e9408a42862d29fe4f4ef7703595b&chksmfa8208becdf581a820d9479d2aa61b88e96612c4ab72b0…

系统架构评估_2.SAAM方法

SAAM&#xff08;Scenarios-based Architecture Analysis Method&#xff09;是卡耐基梅隆大学软件工程研究所&#xff08;SEI at CMU&#xff09;的Kazman等人于1983年提出的一种非功能质量属性的架构分析方法&#xff0c;是最早形成文档并得到广泛使用的软件架构分析方法。最…

设计方案:914-基于64路AD的DBF波束形成硬件

一、硬件概述 &#xff24;&#xff22;&#xff26;技术的实现全部是在数字域实现&#xff0c;然而天线阵列接收的信号经过多次混频后得到的中频信号是模拟信号&#xff0c;实现&#xff24;&#xff22;&#xff26;处理并充分发挥&#xff24;&#xff22;&…

pdf操作器(图片转文字、PDF转word、PDF拆分、图片jpg、png互转)

pdf操作器&#xff08;不用联网图片转文字、PDF转word、PDF拆分、图片jpg、png互转&#xff09;介绍目前该软件实现了以下功能 pdf转wordpdf拆分图片&#xff0c;图片导出在桌面的一个文件夹里图片合并为pdf压缩、转换图片格式&#xff08;jpg和png&#xff09;OCR图片转文字&…