在mysql里面, 多个事务对同一行数据并发读写是不会加锁(非串行化级别), 保证数据库的一致性和隔离性, 这个是通过MVCC多版本对比机制实现的.
MVCC(Multi-Version Concurrency Control)通过undo日志版本链和ReadView视图来实现并发控制,确保事务在读取数据时能够看到一致性的快照, 允许并发写入.
1. Undo日志版本链(Undo Log Version Chain):
-
目的:Undo日志版本链用于记录数据的历史版本,主要用于支持事务的回滚和提供一致性读取。
-
维护方式:在多个事务对一行数据写入操作时,MySQL都会创建修改前端Undo回滚日志, 用两个隐藏字段, 事务id 和undo指针, 把这些undo日志串联起来, 形成日志版本链。
-
用途:Undo日志版本链的主要作用是提供回滚操作。如果事务需要回滚,MySQL可以使用Undo日志将数据恢复到之前的版本。此外,当事务需要读取数据时,如果没有找到相应的可见版本,MySQL可以使用Undo日志来查找之前的版本。
2. ReadView视图:
-
目的:ReadView视图用于控制事务在读取数据时, 可以看到哪些版本,以确保事务之间的隔离性。
-
维护方式:不同的隔离级别决定了事务能够看到的数据版本的范围。例如可重复读级别, 只会在第一次查询生成一份readView视图, 读已提交级别每次查询都会重新生成readView.视图是由当前已创建的事务id组成的数组.
版本对比机制
查询操作时, 从上到下扫描undo日志版本链, 拿到事务id去readView视图对比
1. undoTrxId < min(readView), 是已提交事务, 当前数据可见
2. undoTrxId > max(readView), 是未开始事务, 数据不可见
3. undoTrxId >= min(readView) && undoTrxId <= max(readView), 再对比事务id是否在数组内
3.1 readView contain undoTrxId = true, 说明事务是未提交, 数据不可见(若事务id=自己可见)
3.2 readView contain undoTrxId = false, 说明事务是已提交事务, 数据可见
注意:begin/start transaction 命令并不是一个事务的开始,执行到它们之后的第一个修改操作或加排它锁操作(比如select...for update)的语句,事务才真正启动,才会向mysql申请真正的事务id,mysql内部是严格按照事务的启动顺序来分配事务id的。