几个月之前,正在和同事一起对悲观事务的行为,然后有些bug却显得有点诡异,在我本地跑出来的和在他本地跑出来的行为出现了差异。有点似曾相识,我努力去想,这种熟悉感到底是从哪来的,突然意识到,两个环境的隔离级别不一样。
MySQL默认的隔离级别是可重复读RR,而我平时的使用习惯是安装完就改成读已提交RC。有关为什么MySQL的默认隔离级别是RR,这又是另一个故事,简而言之,是MySQL早期版本为了确保主从同步的数据一致性,而做出的妥协。尽管后面这个问题已经被修复了,但是默认级别是RR却保留了下来。在做DBA的时候,每次配置都会把这个参数改完重启确认,确实是RC才会交付给同事使用。而这个习惯也确实让我后来做产品经理依旧保留了下来。测试的同事按照默认参数安装,并没有留意到这个细节,因此就出现了两个环境的隔离级别不同,跑出来的锁行为不一样的。
读书的时候,我们在数据库系统课上都学过,数据库存在4种隔离级别:读未提交、读已提交、可重复读、串行化,近几年快照隔离也慢慢出现在一些数据库产品中,例如sqlserver就已经支持了快照隔离。和测试同事对完这些bug,我突然萌生了一个疑惑,就是在实际的数据库开发中,真的需要每一种隔离级别吗?
在过往运维过的系统中,最少95%以上的系统就是RC隔离级别。之所以我不敢说是100%,是因为并没有所有生产的数据库都是我本人安装配置并交付给同事使用的,因此只能根据我个人感官来给出一个95%以上。读已提交这个隔离级别,既能够满足绝大多数数据的隔离,又能够提供足够的性能支持。并且开发业务系统的程序员们,也对这个隔离级别最熟悉,无论怎么看,都是无可替代的。
那么问题来了,数据库是不是就不需要支持其他隔离级别了?
我的建议是,如果你是一个初创的数据库软件,目标是支持业务系统,那么只做一个RC就足够了。也许有点暴论,但是这确实是当下看来起来投入产出比最高的选择。不同的隔离级别就要考虑更多的锁行为,同时还有很多性能的并发的问题需要处理。这些都需要很细致的调研、开发和测试。有些看起来不起眼的细节,在开发过程中,却容易被忽略,甚至要导致整个事务逻辑的大改动,无疑是一个投入产出比很低的选择。在没有明确的市场需求以及不可替代的前提下,我建议一个数据库开发中,优先支持且只支持RC即可。
和隔离级别相对应的另一个事务属性,是事务模型。到底是乐观模型还是悲观模型?
悲观模型就是我们平时Oracle或者MySQL的事务模型,在事务开始时就会为事务相关的资源做冲突检测。现实中的业务系统,悲观模型依旧占据了大多数,甚至我印象中,除了大数据相关和一些典型的分布式场景,没有太多业务系统是跑在乐观模型中。悲观模型可以确保数据的一致性和隔离性,适用于写多读多的场景,较适合处理常见的并发冲突。当然也会面对各种阻塞和等待问题,甚至死锁都是要经常面对的。
乐观模型在提交时才会检测冲突,而在事务开始到提交之前是不需要考虑的。这就让系统的并发能力有的显著的提高。所以我在上一段说,只有典型的分布式场景和大数据应用,才见过乐观事务。比如现在很火的数据湖技术Iceberg,就是典型的乐观模型+快照隔离的组合。在数据湖的场景中,恰恰是一个很有用的组合。如果盲目使用RC隔离+悲观模型,很可能得不到预期的结果。
所以在选择事务模型的时候,仍然要立足于业务场景,到底这个数据库软件,是要面向什么样的场景,什么样的业务。
如果说的再通俗一点,隔离级别和事务模型,在开发之前,就要想清楚,这个数据库给谁用,怎么用。有一个预期才能集中力量去做真正有应用场景的业务。并不是悲观一定比乐观好,也不是隔离级别越高就越适用,最终还是要以用户的需求为导向,不要为了做而做。




