3

支持大事务简要 notes

 3 years ago
source link: https://www.zenlife.tk/large-txn.md
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

支持大事务简要 notes

2019-12-01

我们为什么需要大事务

用户是有大事务的需求的

开启 batch dml 以后,无法保证事务的完整性

为什么当前不支持大事务

  • tikv 扛不住?
titan 对单个大的 key-value 的支持能力已经大大增强
prewrite 是按 region 的,单个 region 并没多大
我们可以先假设底层引擎是扛得住的
  • TTL 太小事务被干掉
事务的 primary lock 里面记录了事务状态,有一个 TTL 用于异常处理
TTL 的值太小,在 prewrite 过程中,事务就会被清锁干掉
TTL 设置的太大,会阻塞其它事务
如果事务异常挂掉,锁会很久清不掉,会一直 block 导致其它事务超时失败
解决方案:自动更新 TTL
  • 事务的阻塞
写写冲突,没法解决
读写冲突,**只要让大事务不阻塞读操作,就可以接受**
解决方案:新的事务模型
  • 事务冲突代价太高
不在解决范围之内
或者可以考虑悲观事务

TTL 自动更新

初始设置一个比较短的事务 TTL,然后在事务存活期间,自动更新 primary lock 的 TTL 值

这样就可以解决大事务的 TTL 问题

  • TTL 自动更新导致 ResolveLock 需要调整
  • TTL 自动更新需要引入死锁检测

乐观锁里面不需要死锁检测,因为 TTL 不会更新,即使发生了死锁,过了 TTL 锁也能清掉

自动更新 TTL 之后,需要引入死锁检测

ResolveLock

以前检查 secondary lock 跟检查 primary lock 都一样

操作步骤:

  • 检查到 secondary(primary) lock
  • backoff TTL 那么久
  • 调用 CleanUp 将 primary lock 给 Rollback
  • 调用 ResolveLock 清理 secondary lock

现在 secondary lock 里面信息无效,只有 primary lock 里面是真实的

  • 检查到 secondary(primary) lock
  • 去检查 primary lock 判定事务状态 (CheckTxnStatus)
  • 如果事务 TTL 过期,则清锁
  • 如果事务 TTL 没过期,则 backoff 之后重新检查

CheckTxnStatus 两种异常场景

遇到 secondary lock 了,再去检查 primary lock,发现 primary lock 不存在。

什么情况下会遇到?

  • 由于 prewrite region 并发,secondary lock 先写成功,primary lock 还没写
  • 由于悲观锁回滚不留下 tombstone

为什么遇锁会阻塞读操作

一个事务正在写入,另一个事务去读取。能不能读正在写的内容?

  • 读了,事务回滚了 – read uncommitted
  • 不读,事务成功了 – lost update

读不读都有问题,所以要:等。

等写操作执行完了再读,就没这个麻烦了。

从第二个点想想办法,我们讨论不读

如果写事务在前面,读事务在后面,不读就会出现写丢失。

所以 关键点是重排序,让读事务在前面,写事务到后面,这样读事务就可以不读到写事务

如何调整事务顺序呢? 事务顺序是由 ts 决定的。

一个事务1的 start ts 大于另一个事务2的 commit ts,那么事务1是在事务2 后面的

假设每个 ts 都唯一,并且不考虑 +-1 问题,我们可以换一种说法:

一个事务1的 start ts 小于另一个事务2的 commit ts,那么事务1是在事务2 前面的

所以让读事务,把写事务的 commit ts 往后推,保证读事务 start ts 小于写事务 commit ts

即可达成顺序调整,从而实现重排序

min commit ts

  • 在 primary lock 里面,记录一个 minCommitTS 字段
  • 每次读者遇到锁的时候,调整 minCommitTS,使它大于 reader ts
  • commit 的时候,最终 commit ts 要至少大于 minCommitTS

即:每次读事务,遇到写事务,读事务会把写事务推到自己后面

测试中遇到哪些问题

并发压力过大,tikv 被打死了

prewrite 的时候,按 region 数量并发

大事务需要控制并发量,否则 tikv 会被打死

insert 执行慢

coprocessor 是并行读的

insert 的时候是串行的

在 insert 那里会形成一个瓶颈点,大事务比较明显

memdb 是大块连续内存,在大事务下会分配失败 => 改造成分块的,每块大小上限 128M

insert 过程中,table 接口是用的 []Datum 表示,非常占内存 => 改成尽快刷到 memdb 里面

2pc 里面 mutation 的内存占用高

纯 KV 数据量 2-3G 的大事务,在 mutation 里面的表示大概 7-8G

加上其它包括 dirty table 和进程本身之类会到 10G

由于 Go 的 GC 策略,向系统申请的内存量会在 20G

大概 6 倍的放大

预计支持 10G 事务需要 60G+ 的内存

2-3G 的事务完成时间大概是 10min


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK