TiDB MVCC 多版本保存机制及其对性能的影响

从接触TiDB以来,就看到过TiDB官方文档上的提示,gc_life_time设置过大,会因为历史版本过多,影响查询效率,但是为什么SQL非要去扫描历史版本呢?下面列举一些知识点一步一步来解析这个问题

1. TiDB key的编码方式

TiDB 会对每个表分配一个全局唯一的table_id,每一个索引都会分配一个表内唯一的 index_id,每一行分配一个 row_id(如果表有整数型的 Primary Key,那么会用 Primary Key 的值当做 row_id,如果没有,那么TiDB会自动生成一个隐式主键_tidb_rowid)
数据编码方式:
t{table_id}_r{row_id}-->[col1,col2,col3,...]
索引编码方式:
unique index
t{table_id}_i{index_id}_{index_column_value}-->[row_id]
非unique index
t{table_id}_i{index_id}_{index_column_value}_{row_id}-->null
举个栗子:

CREATE TABLE `test_table` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
  `column1` varchar(10) DEFAULT NULL,
  `column2` varchar(10) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_column1` (`column1`)
id column1 column2
1 "a" "b"
2 "c" "d"

那么主键编码可以抽象为
t10_r1-->[1,"a","b"]
t10_r2-->[2,"c","d"]
索引idx_column1编码可以抽象为
t10_i1_a_1-->null
t10_i1_b_2-->null

2. MVCC多版本信息是如何保存的?

TiDB使用基于Percolator的事务模型,将一行数据抽象为default、write 和 lock 3 个 CF(column family)存储,其中:

  • default CF存储的真正数据
    ${key}_${start_ts} --> ${value}
  • write CF存储数据的版本信息,commit_ts代表一行记录的真正版本
    ${key}_${commit_ts}-->${start_ts}
  • lock CF存放锁信息,提交中的事务会加lock,包含primary lock的位置
    ${key}-->${start_ts,primary_key,..etc}

一个读取操作的过程如下:

  1. 事务begin,从PD获取start_ts
  2. 读取key,先判断lock CF有没有锁,如果
    a. 有锁
    判断primary key状态是否超时
    - 若锁未超时,等待
    - 若锁已超时,根据primary key的状态rollback或commit残留事务
    b. 无锁
    根据当前事务获取的start_ts对比数据的commit_ts(write CF)
    - start_ts大于commit_ts,返回这行数据
    - start_ts小于commit_ts,继续查找当前行的下一个(更早的)版本
  3. 根据步骤2得到的commit_ts,从default CF中获取真正的数据

TiDB将一行记录的多个版本按照从新到老的顺序排列,这样方便我们获得满足查询条件的最新记录,TiKV默认存储引擎是RocksDB,RocksDB一个seek操作要比next操作昂贵很多,如下这个例子
假设key1,key2,key3都有多次更新,生成的mvcc版本从老到新分别为key1_v1,key1_v2,key1_v3,key1_v4...
几个相邻key的存放方式抽象为下图:


连续的key排列

Rocksdb没办法精确去定位每一个key,如果扫描每一个key都走seek接口,这样代价太大。所以如图,假设一个范围查询seek到第一个key,key1之后,就开始调用next函数获取后面的key2、key3值,这样需要遍历key1甚至key2的所有历史版本。如此,就能解释为什么过多的历史版本会让查询效率急剧下降了。

我们日常工作经常碰见的几个问题:
一、SQL执行时间不稳定
慢日志中会发现这些SQL语句的total keys比process keys大很多,这就是典型的历史版本过多,导致扫描了大量历史数据。
解决方法

  1. 减小gc_life_time,或者让业务缩小查询范围。
  2. 升级TiDB3.0版本
    TiDB3.0之前的版本,全局GC效率不高,容易积压大量历史版本数据。3.0之后改成了分布式GC,能够快速释放大量已删除的历史版本,再加上更完善的region merge功能,会让整个集群的性能提升一个很大的台阶。

二、删除&归档特定日期以前的记录

while True:
    delete table where {$condition} limit n
    if affectrows==0:
        break

这个场景的现象是delete语句会越来越慢。
因为扫描范围{$condition}是固定不变的,delete删除语句在TiDB处理方式是标记删除,删除本身实际上也是插入一条kv记录,只不过value变成了delete。所以,循环执行delete 语句,每次删除n条记录,下一次delete语句要扫描的key就会+n,执行时间越来越长(大家可以去做个实验,观察慢日志文件,同样的delete语句total keys会不断增加)。

那么,怎样去删除&归档特定日期前的记录比较高效呢?

首先,我们知道TiDB对事务大小是有限制的

  1. 单个事务包含的SQL语句不超过5000条
  2. 操作的单条记录不超过 6MB
  3. 事务操作的总keys不超过 30w
  4. 事务操作的所有记录总大小不超过 100MB

由于TiDB的事务限制和TiDB mvcc的实现原理,想要删除&归档一个特定范围的数据,目前没有太好的方法,个人整理一些心得供大家参考:
第一种方式:
尽量缩小范围删除的粒度,比如提前按分钟将数据分段,打开tidb_batch_delete,提高并发去删除。注意使用开闭区间,分段之间不要出现冲突,TiDB解决事务冲突的代价比较大。

set @@session.tidb_batch_delete=1;
delete from table where create_time > '$start_step' and create_time <= '$end_step';

如果分段内的数据超出事务大小限制,TiDB会自动将delete操作拆分成多个batch。
个人亲测,这种方式删除数据的速度还是比较快的。
第二种方式:
按照日期分表,删除过期的表即可。TiDB删表是秒级的,后续空间回收也比较快,缺点是侵入业务。
两种方式各有利弊,大家各取所需吧。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342