0%

MySQL的并发与MVCC

事务的ACID属性

  • Atomicity原子性
  • Consistency一致性:从实际的业务逻辑上来说,最终结果是对的,是跟期望的结果完全符合的,比如转账的加减
  • Isolation隔离性
  • Durability持久性:事务一旦提交,数据会持久化到硬盘,他对数据库的改变是永久性的

四大隔离级别★

脏读 Dirty read 不可重复读 Non repeatable read 幻读 Phantom read
读未提交 Read uncommited ×
读已提交 Read commited × ×
可重复读 Repeatable read × ×
穿行 Serializable × × ×

MVCC

Multi-Version Concurrency Control 多版本并发控制,为每个修改保存一个版本

解决了脏读、不可重复读、幻读问题,但无法解决更新丢失问题

在InnoDB中的实现:即使在有读有写的冲突中,也能做到不加锁,非阻塞并发读

  • 当前读:会对读取的记录加锁,读取最新版本

    1
    2
    3
    4
    5
    select lock in share mode //共享锁
    select for update
    update
    insert
    delete //排他锁
  • 快照读:不加锁的非阻塞读,快照读可能读到的不是最新版本

    1
    select

实现原理

三个隐藏字段

  • DB_TRX_ID: 记录最近修改该行的事务id
  • DB_ROLL_PTR: 回滚指针,指向该记录的上一个版本,用于配合undo log
  • DB_ROW_ID: 隐藏的主键,若没有定义主键,则会自动生成主键

undo log

  1. 事务1执行insert

.image-20220211215532044

  1. 事务2执行update,修改时会加排他锁

.image-20220211215645571

  1. 事务3执行update,同理

.image-20220211215953695

形成链表

read view

快照读时会生成Read View视图,有三个全局属性

  • trx_list: Read View生成时刻系统正活跃的事务id
  • up_limit_id: trx_list中id的最小值
  • low_limit_id: Read View生成时刻系统尚未分配的下一个事务id
事务1 事务2 事务3 事务4
start transaction start transaction start transaction start transaction
.. update and commit
select

.image-20220211221723621

.image-20220211222259286

问:事务2能否看到事务4的提交?

执行比较规则:

1
2
3
4
if(DB_TRX_ID < up_limit_id){可见}
else if(DB_TRX_ID >= low_limit_id){表示该记录在Read View生成后才提交,显然不可见}
else if(DB_TRX_ID 在 trx_list中){表示在Read View生成时该记录处于活跃状态,未提交,则不可见}
else{该事务已提交,可见}
  • RR级别使用的Read View始终是第一次快照读时生成的Read View
  • RC级别每次快照读都会生成一个新的Read View