大家使用mysql租户的隔离级别是用"可重复读"还是"读提交"呢?推荐用什么

大家使用mysql租户的隔离级别是用"可重复读"还是"读提交"呢?推荐用什么
看官方文档是读提交read-commited, 但是有很多存量tidb的业务想迁移到tidb,在测试阶段发现在ob mysql租户的可重复读隔离级别下,跟tidb不同,ob mysql租户偶尔会 SQL 报错 Can’t serialize access for this transaction

请问大家推荐用什么隔离级别呢?对于tidb到ob的迁移这种情况大家是如何解决的呢?

测试案例:
ob mysql租户
create table t1(id int not null primary key, log_date date);
insert into t1 values(1, ‘2023-06-30’), (2, ‘2023-06-30’), (3, ‘2023-02-15’);
set transaction_isolation=‘repeatable-read’;

但是在mysql和tidb的可重复读隔离级别下,update t1 set log_date=current_date() where id =1;的结果都是Rows matched: 0
Query OK, 0 rows affected (0.00 sec)
Rows matched: 0 Changed: 0 Warnings: 0

1 个赞

关于事务隔离可以学习下这篇文章
https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000001053058
tidb迁移到ob推荐使用oms迁移工具,这个也有社区版

1 个赞

ob跟mysql/tidb在可重复读隔离级别的异同点已经清楚了, 我想问的是在tidb是可重复读隔离级别,迁移到oceanbase,大家用什么隔离级别或者官方推荐使用什么隔离级别?如果使用oceanbase的可重复读隔离级别,如果出现can’t serialize access for this transaction,大家又是如何处理的呢?

1 个赞

请问这个场景在 tidb 现在的版本里 测试结果是什么样子?

1 个赞

感觉OB的可重复读隔离级别在这种场景下立马报错其实更符合业务,mysql和tidb在执行这条语句的时候其实也不会修改任何数据下面都提示没有数据匹配。。大部分情况用默认的读已提交已经算是最优

1 个赞

update t1 set log_date=current_date() where id =1;的结果都是Rows matched: 0
Query OK, 0 rows affected (0.00 sec)
Rows matched: 0 Changed: 0 Warnings: 0

1 个赞

是的,大部分场景是没有问题的。但是从tidb的可重复读到ob的可重复读毕竟有表现不一样的地方,想问下大家对于tidb到ob的迁移经验,看大家是如何解决这个问题的。
如果是新接入业务也不会考虑这个不同,但这个是存量tidb迁移ob,不考虑不行。

1 个赞

我觉得还是看一下业务上是否依赖可重复读的一些特性,如果有那么对于OB这种并发冲突场景的报错是否能进行判断。。如果没有那么用读已提交就没问题

2 个赞

读已提交就好了。 可重复读还得考虑间隙锁啥的。。 容易造成死锁问题

1 个赞

楼上说的使用读已提交就很方便避免很多死锁场景问题,隔离级别的话还是需要根据自己的业务场景来定。

1 个赞

是的,还是从tidb迁移至ob,还是要提前和业务沟通下,不能无脑选择读提交隔离级别来避免 Can’t serialize access for this transaction这个问题。

不知道大家从tidb迁移到ob是否有更多的经验分享

https://www.oceanbase.com/docs/community-oms-cn-1000000001302071
tidb迁移推荐浏览学习下官方的文档

  1. RR 是用来实现可重复读。按可重复读的定义是说事务过程中先后查询同样的数据,数据应该保持不变,即使其他比本事务后开启的事务修改这比数据并在本事务之前提交。但是这个示例的场景却违背了这个定义。因为 update 语句(dml语句)只能在最新的数据上修改,数据库在update 数据之前肯定是先将数据读取到内存中,这个读叫当前读。当前读的定义就是要读取最新的数据,RR场景却要求它读取历史数据(跟前面的第一次查询结果保持一致),这本身就是矛盾。在mysql 里这个update 是不报错了,但是这时候紧跟着再查询一次,也是最新的结果(因为已经从快照读转变为当前读了)mysql完全不顾这个事务定义是在RR隔离级别下,直接转变成 RC 了。(经楼主提示表达有误,故删除)。
    所以 mysql在实现RR的时候本身就是有缺陷的。
    然后业务该怎么看呢?如果业务觉得不报错是可以接受的,那业务为什么要用RR呢?用RC不就可以了。

  2. 上面这个例子还只是演示RR的一个例子,RR还有个场景例子是可以部分解决幻读问题,靠的是间隙锁机制。当索引缺乏或者不合理的时候,这个间隙锁经常锁住很多不必要的记录,导致 RR 隔离级别下并发锁冲突问题很多。RR 隔离级别是不适合互联网高并发业务的。

  3. 那为什么这么多人在MySQL里喜欢用RR呢?我理解是有很大一部分是压根就没想过这个问题,myslq安装的时候默认就是RR,就这么用下来。那为什么 MySQL默认要用RR呢?其原因除了是 RR 相比 RC 能解决的不可重复读、部分幻读问题外还有一个原因是早期mysql主从同步是基于 statement 模式的时候,RC 隔离级别会导致主从不一致的风险非常大,所以那个时候就用了RR。即使后来mysql支持基于row模式的主从同步,RR 还是这么沿用下来了。 我这个推测不一定百分百有道理。不过大家可以从用户案例选择里找答案。从十年前开始淘宝电商所有mysql都是rc 隔离级别。即使是现在其他公司互联网业务,肯定是用rc。

  4. 那RC的问题怎么办?如果RC 在业务上有问题,那就是业务设计的问题。应该是业务修改设计而不是数据库改为RR。这里说的是一种小概率事件。评估改不改 只要分析一下或者实际跑一下就知道。不要轻易被这句劝退。

  5. 不同数据库的事务隔离级别机制不一样,不一定要用学术派的那个理论去评价数据库。oracle只有rc和serializable 两个隔离级别。oracle就很鄙视 脏读 这个隔离级别,认为毫无必要。而mysql,sqlserver 都有这个隔离级别,那是因为这两个数据库早期都没有mvcc功能,事务的隔离性靠锁,锁的问题靠脏读解决。从一开始这个方案就意味着以后会有锁问题。

  6. tidb出来时为了兼容mysql,只好默认也RR。早期tidb没有悲观锁只有乐观锁,rr 场景靠事务重试去解决。后面几个版本具体怎样我不清楚。如果按上面说法跟mysql一样update不报错了,那我个人觉得就是即使mysql是错的,也跟着错。不过业务不在乎对错,业务认为大量用户的使用习惯就是对的。这个也有一定道理。

  7. ob的mysql租户在出来的时候只是兼容mysql的sql语法,事务隔离级别特性和原理却是像oracle靠齐的,一开始用了mvcc读(不是用oracle的undo,lsmtree特有机制导致undo并不是那么有必要)。所以早期mysql租户只有rc和serializable两个隔离级别。实在没有什么理由要RR。只是ob发展到3.x后耐不住大量mysql用户的兼容性要求,ob也实现了RR。可是 ob又没有间隙锁那种拙劣的设计,RR也就没有办法做到跟mysql一样。ORACLE 也没有RR,oracle认为如果业务有那种不可重复读和幻读的要求,那可以用 serializable。我估计OB 也是这么认为的。不过 OB 还是努力去实现一个 RR,根据RR的定义,上面那个例子,当update变成一致性读的时候,OB做了跟mysql不一样的选择,OB认为既然你事务隔离级别声明为RR了,那么数据发生变化后你再更新就不不满足这个RR的定义了,直接判你“违例”了(ORA-08177: can’t serialize access for this transaction)。OB的 serializable隔离级别里也很容易看到这个错误。

8.也许有人会说 OB 不是声称兼容mysql 吗?这里不兼容让业务怎么办。我觉得业务还是要想想mysql里你是否一定要用RR?有没有想过在RC下业务也能跑通。很多企业的核心业务跑在ORACLE上,都用的是 RC。所以 mysql迁移到 tidb 的时候有一次机会重新选择事务隔离级别,tidb迁移到 ob的时候又有一次选择的机会。是选择无脑兼容,还是重新选择?
反正要求ob的mysql事务隔离级别RR跟mysql的RR保持完全一致是绝对不可能的。mysql里用间隙锁来实现了RR消除幻读读带来的锁泛滥问题 这个特性 OB 是没法复制的。

9.从mysql入行看oracle 或看 ob,跟从oracle入行看mysql 或看 ob,有些问题的看法上会有区别。ob社区版能力只是 ob 全部能力的一半。ob社区版跟企业版底层代码还是同一份,兼容mysql只是尽可能语法兼容,在特性上 ob都是向 oracle靠拢的。如果oracle的做法更好,肯定不会选mysql的方案。 (这些都是个人观点:smile:

早些年也测试过几个数据库的事务隔离级别,有兴趣的可以看看: 分布式数据库的快照隔离级别案例对比分析 (qq.com) (其中 ob 和 tidb版本比较早,只是看看。tidb最新能力以tidb官网为准。)

3 个赞

在读已提交的前提下,进行代码开发。 能规避很多问题。读已提交基本能满足所有业务。特殊的业务,大可从应用代码层面来解决

2 个赞

选择读提交 既可以满足大部分应用场景了。

非常感谢您精彩详细的回复,让我了解到不同数据库隔离级别的区别,以及ob隔离级别开发的相关背景。具体隔离级别的设置我再和业务沟通下。

不过有两点不同看法:
1、

在上面的示例中,mysql在update之后再次查询还是快照读,与update之前的读取一致,这样mysql认为不会出现幻读,也不是直接退还成RC的,还是满足可重复读的,如下图

2、

从我接触的几家的互联网公司,mysql的隔离级别选用的都是RR,业界关于锁分析的案例也是大多基于RR,恐怕业界mysql使用RC的并不多。

  1. 哦,这个场景下那是我表述错了。这个结果就尴尬了,在一个事务里,自己更新了记录,接着读还是老的数据,这个业务逻辑要错乱了。
    你再测试一个场景,那个 update 变 delete,然后跑 select 看看是不是 记录还存在。

  2. 恩,确实 RR , RC 都有很多人用。我是想表达 RC 可以满足业务的,不一定要 RR。 到 了 OB ,建议还是 RC 或者说 基于 RC 开发吧。

1、delete之后再select也是能查到数据的。
mysql在可重复读隔离级别下,只需要在事务开始的时候创建一致性视图,之后事务里的其他查询都共用这个一致性视图;
mysql在读提交隔离级别下,每一个语句执行前都会重新算出一个新的视图。

2、嗯,是的

1 个赞