MySQL 事务的两阶段锁协议

978 阅读2分钟

本文正在参加「金石计划 . 瓜分6万现金大奖」

1. 概述

MySQL中,两阶段锁协议的含义是:当一个事务获取到了某一个数据库对象的之后,并不是当前事务不需要操作它了之后,这个说就会马上释放掉,这个会一直被这个事务持有,直到这个事务被提交或回滚后,这个才会被释放掉。 两阶段加锁(2PL)存在于 innodb 引擎下,2PL 与 MVCC 一起,主要用于单机事务中的一致性与隔离性。

在对记录更新操作或者(select for update、lock in share model)时,会对记录加锁,在这里,我们暂时不具体讨论锁的类型,而将视角聚焦到 2PL 本身。

在一个事务里面,分为加锁(lock)阶段和解锁(unlock)阶段,从 begin 开始,一直到 commit/rollback,都是加锁阶段,换句话说,整个事务期间,线程只抓取锁,不释放锁,直到事务结束,统一释放。

2. 2PL 的场景分析

2.1 事务 A 与 事务 B 争抢一把锁

image.png

  1. A 拿到锁,执行完操作不释放
  2. B 等待锁的释放
  3. A 因为有锁,可以再次操作
  4. A commit提交数据,释放锁
  5. B 拿到锁,进行操作,之后comiit并且释放锁

2.2 事务 A 与 事务 B 争抢多把锁

当需要获得多组数据的锁时,一般需要把最热点的记录放到事务最后,这样可以显著的提高吞吐量。

如果一个事务操作需要进行更新库存以及锁定账户两个操作,那么就有以下两种时序选择:

image.png

由于库存往往是最重要的热点,是整个系统的瓶颈。那么如果采用第二种方案的话, tps应该理论上能够提升3rt/rt=3倍。这还仅仅是业务就只有三条SQL的情况下, 多一条sql就多一次rt,就多一倍的时间。

需要注意的是,在进行事务中执行数据书序排布时,每个事务应保持一致,以尽量避免思死锁问题:

image.png

当然,事务中的死锁问题在某种意义上只能尽力防止,但不能完全避免,当出现死锁以后,有两种策略:

  • 一种策略是,直接进入等待,直到超时。

  • 另一种策略是,发起死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务。