事务特性

原子性
一致性
持久性
隔离性

隔离等级 与问题

读未提交
读提交
可重复读
串行

脏写
脏读
不可重复读
幻读

默认为可重复读

怎么解决不可重复读 —- MVCC 多版本并发控制

MVCC — 如何实现的?
undo log
版本链
假设

  1. # Transaction 100
  2. BEGIN;
  3. UPDATE hero SET name = '关羽' WHERE number = 1;
  4. UPDATE hero SET name = '张飞' WHERE number = 1;
  5. COMMIT;
  1. # Transaction 200
  2. BEGIN;
  3. # 更新了一些别的表的记录
  4. ...
  5. UPDATE hero SET name = '赵云' WHERE number = 1;
  6. UPDATE hero SET name = '诸葛亮' WHERE number = 1;

image.png

read view

m_ids:表示在生成ReadView时当前系统中活跃的读写事务的事务id列表。
min_trx_id:表示在生成ReadView时当前系统中活跃的读写事务中最小的事务id,也就是m_ids中的最小值。
max_trx_id:表示生成ReadView时系统中应该分配给下一个事务的id值。
creator_trx_id:表示生成该ReadView的事务的事务id
有了这个ReadView,这样在访问某条记录时,只需要按照下边的步骤判断记录的某个版本是否可见:

  • 如果被访问版本的trx_id属性值与ReadView中的creator_trx_id值相同,意味着当前事务在访问它自己修改过的记录,所以该版本可以被当前事务访问。
  • 如果被访问版本的trx_id属性值小于ReadView中的min_trx_id值,表明生成该版本的事务在当前事务生成ReadView前已经提交,所以该版本可以被当前事务访问。
  • 如果被访问版本的trx_id属性值大于或等于ReadView中的max_trx_id值,表明生成该版本的事务在当前事务生成ReadView后才开启,所以该版本不可以被当前事务访问。
  • 如果被访问版本的trx_id属性值在ReadViewmin_trx_idmax_trx_id之间,那就需要判断一下trx_id属性值是不是在m_ids列表中,如果在,说明创建ReadView时生成该版本的事务还是活跃的,该版本不可以被访问;如果不在,说明创建ReadView时生成该版本的事务已经被提交,该版本可以被访问。
  1. # 使用READ COMMITTED隔离级别的事务
  2. BEGIN;
  3. # SELECT1:Transaction 100、200均未提交
  4. SELECT * FROM hero WHERE number = 1; # 得到的列name的值为'刘备'
  5. # SELECT2:Transaction 100提交,Transaction 200未提交
  6. SELECT * FROM hero WHERE number = 1; # 得到的列name的值为'张飞'

READ COMMITTDREPEATABLE READ这两个隔离级别的一个很大不同就是:生成ReadView的时机不同,READ COMMITTD在每一次进行普通SELECT操作前都会生成一个ReadView,而REPEATABLE READ只在第一次进行普通SELECT操作前生成一个ReadView,之后的查询操作都重复使用这个ReadView就好了。

时间 事务100 事务200 事务210
begin;
T1 UPDATE hero SET name = ‘关羽’ WHERE number = 1; BEGIN;
T2 UPDATE hero SET name = ‘张飞’ WHERE number = 1; UPDATE hero SET name = ‘赵云’ WHERE number = 1; BEGIN;
T3 UPDATE hero SET name = ‘诸葛亮’ WHERE number = 1;
T4 SELECT * FROM hero WHERE number = 1; # 得到的列name的值为’刘备’
T5 COMMIT;
SELECT * FROM hero WHERE number = 1; # 得到的列name的值为’张飞’


Mysql可重复读下解决幻读了吗