乐观锁和悲观锁的一个例子
想象一下你马上出发要去一家餐厅吃饭,但是你去之前不确定会不会满桌,你又不想排号。这时的你会有两个选择,如果你是个乐观的人,内心戏可能会是「管他的,去了再说,大不了没座就回来」。反之,如果你是一个悲观的人,可能会先打个电话预约一下,先确认下肯定有座,同时交点定金让餐厅预留好这个座位,这样就可以直接去了。
上面这个例子很直观的对应了两种事务模型的行为,乐观事务模型就是直接提交,遇到冲突就回滚,悲观事务模型就是在真正提交事务前,先尝试对需要修改的资源上锁,只有在确保事务一定能够执行成功后,才开始提交。
理解了上面的例子后,乐观事务和悲观事务的优劣就很好理解了。对于乐观事务模型来说,比较适合冲突率不高的场景,因为直接提交(“直接去餐厅”)大概率会成功(“餐厅有座”),冲突(“餐厅无座”)的是小概率事件,但是一旦遇到事务冲突,回滚(回来)的代价会比较大。悲观事务的好处是对于冲突率高的场景,提前上锁(“打电话交定金预约”)的代价小于事后回滚的代价,而且还能以比较低的代价解决多个并发事务互相冲突、导致谁也成功不了的场景。
再次解释背后思想:
常规的锁是先互斥,再修改数据。不管是不是发生了冲突,我们都会先做互斥。但乐观锁不同,它是先计算出所有修改的数据,然后最后一步统一提交修改。提交时会进行冲突检查,如果没有冲突,也就是说,在我之前没有人提交过新版本,或者虽然有人提交过新版本,但是修改的数据和我所依赖的数据并不相关,那么提交会成功。否则就是发生了冲突,会放弃本次修改。
为什么要用乐观锁?至少它让锁数据库的粒度降到最低,判断冲突的逻辑也都是可预期的行为,这就避免了出现死锁的可能。我们很容易可以推理得知,在所有并行执行的事务中,必然有一个事务的提交会成功。这样就避免了饥饿(永远都没人可以成功)。
reference:
1. https://pingcap.com/blog-cn/pessimistic-transaction-the-new-features-of-tidb/
2. 许式伟架构课