【 TiDB 使用环境】线上、测试、调研
【 TiDB 版本】
【遇到的问题】
tidb里面既然实现了mvcc,为什么还存在读写冲突呢,mvcc不就是解决了读写冲突这种场景的吗。
【复现路径】做过哪些操作出现的问题
【问题现象及影响】
【附件】
请提供各个组件的 version 信息,如 cdc/tikv,可通过执行 cdc version/tikv-server --version 获取。
【 TiDB 使用环境】线上、测试、调研
【 TiDB 版本】
【遇到的问题】
tidb里面既然实现了mvcc,为什么还存在读写冲突呢,mvcc不就是解决了读写冲突这种场景的吗。
【复现路径】做过哪些操作出现的问题
【问题现象及影响】
【附件】
请提供各个组件的 version 信息,如 cdc/tikv,可通过执行 cdc version/tikv-server --version 获取。
悲观模式下,是不是就不存在这种读写冲突了
悲观模式存在相同问题
参考秒杀的场景,一个货品, 100个人都想要,而且是同一个时间(精确到秒),
实际上数据库这种操作的抢占是精确到 us了,一样会出现抢占的场景 ,不可避免(所以才会有悲观锁和乐观锁之分)
悲观模式下,autocommit的时候,也是优先使用乐观锁模式,冲突重试的时候才转为悲观。
读写冲突目的就是为了确保线性一致性不被破坏,和悲观乐观模式无关。根据线性一致性中的循序性,逻辑上发生的顺序不能违反物理上的先后顺序,例如T时刻发起的读操作一定能读取到T时刻之前的所有变更,因此读写冲突本质就是读操作必须要等待比自己早发生的写操作完成,然后再去读取,从而满足循序性。另一方面,事务的commit是一个过程,例如Txn0从T1时刻开始执行2PC中的commit阶段,确定的commit_ts为T2,在T3时刻commit完成(T1<T2<T3,不考虑Async Commit),那么假设读事务Txn1的start_ts位于T2~T3之间,那么根据循序性它必须要能够读取到Txn0写的数据(因为T2小于自己的start_ts),那么Txn1这时只能等或者backoff
再请教一下,那什么情况下,会使用mvcc的机制呢?
数据库并发访问一致性的功能,并非人为使用场景
MVCC是多版本,当写事务的commit_ts比读事务的start_ts大时,即写事务的修改对读事务不可见时,读事务就会利用这个多版本机制,去读取旧版本的数据
说直白一些: A事务要读数据,搞了个start_ts 这时候总不能整个库就不能写了啊。 mvcc就是其他事务正常写,A事务读的时候,只读start_ts之前的版本。
那这种情况下会发生读写冲突吗?
MVCC就是为了解决并发事务处理中的读写冲突问题的
我最开始的问题就是这个疑问,tidb中有了mvcc以后为什么还会有读写冲突的问题存在,但是看上面的讨论,好像不是这么回事呀
有mvcc,并且要保证一致性,所以在极端条件下,依然会出现读写冲突
很多数据库都会实现多版本并发控制 (MVCC),TiKV 也不例外。设想这样的场景:两个客户端同时去修改一个 Key 的 Value,如果没有数据的多版本控制,就需要对数据上锁,在分布式场景下,可能会带来性能以及死锁问题。TiKV 的 MVCC 实现是通过在 Key 后面添加版本号来实现
我个人的理解是这样的,不知道是否正确哈,说的不对的,请各位老师指点 1、假设B事务要去读id=1这一行数据,然后B事务的start.tso是11:00:00,那么按照tidb的隔离设计原则,B事务只能读到id=1这一行数据在11:00:00 之前版本最新的数据(比如id=1这一行在10:58有事务更新它了,然后10:59分又有事务更新它了,那么只能读到id=1在10:59这个时刻的状态)
2、存在读写冲突的一种情况 假设id=1这一行在10:58:00 这个时刻被某个事务更改提交了(id=1这一行的commit.tso就是10:58:00),假设B事务去读id=1这一行的时候,发现lock cf里面有id=1的记录(假设是A事务产生的,A事务还没有完全执行完整个事务的提交过程),A事务的start.tso是10:59:10(lock cf 里面也有start.tso的记录),这说明A事务已经完成了两阶段提交中的 prewrite阶段,正在进行commit阶段,而且已经获取了1个commit.tso是10:59:50,那么B事务就应该等待A事务提交完成,再去读id=1这一行的最新数据(commit.tso是10:59:50这一刻的状态,而不是直接读commit.tso是10:58:00 这个时刻的状态) mvcc的读写冲突,我个人认为是为了保证隔离级别的正确性,但是不知道如果读到commit.tso是10:58:00 这一刻的id=1的数据会不会有什么不好的后果?
老师说的很清楚,但是我有个疑问,如果不读tx0写的数据会怎么样呢,能否举个例子?
不知道如果读到commit.tso是10:58:00 这一刻的id=1的数据会不会有什么不好的后果?
后果就是,违背了你开始写的这句话
B事务只能读到id=1这一行数据在11:00:00 之前版本最新的数据(比如id=1这一行在10:58有事务更新它了,然后10:59分又有事务更新它了,那么只能读到id=1在10:59这个时刻的状态)
好吧,但是这样的mvcc 设计其实感觉代价比mysql的mvcc 代价更大