MySQL事务
MySQL事务
事务的四大特性
事务是由一系列对系统中数据进行访问或者更新操作组成的一个程序执行逻辑单元(Unit),也就是说,事务是要求这一组程序要么都执行成功,要么都不执行(执行失败)。 ACID,是指数据库管理系统在写入或更新资料的过程中,为保证事务是正确可靠的,所必须具备的四个特性:
- 原子性 Atomicity :要保证事务中包裹的逻辑,要么全部执行成功,要么全部都不执行。
- 一致性 Consistency:要保证事务在执行前后,数据库都要处于正确状态,满足完整性约束。
- 隔离性 Isolation:多个事务并发执行的时候,一个事务不应该影响另一个事务,保证所有事务都好像在独立运行。
- 持久性 Durability:事务处理完成,对数据的修改是永久性的,即使系统故障,都不会丢失。
我的理解中,AID是为了保证C存在的,保证了AID和业务层面代码逻辑正确,就能得到C的结果。
当然,也可以把C解释成与AID一致的层面,比如:数据一致性就是数据要满足一定约束条件,如果事务的执行违反了这个约束条件,那么事务应该失败,这样理解的话,就是ACID是一个层面的特性了。
不管是“AID 是特性,C是目的”,还是“ACID 都是 特性”,都看个人理解,然而这种个人理解无论是哪种,都是有利于“事务”的制定和发展的。
mysql的四大隔离级别
- READ-UNCOMMITTED(读未提交) :最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
- READ-COMMITTED(读已提交) :允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
- REPEATABLE-READ(可重复读) :对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
- SERIALIZABLE(可串行化) :最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。
标准的 SQL 隔离级别定义里,REPEATABLE-READ(可重复读)是不可以防止幻读的。
但是!InnoDB 实现的 REPEATABLE-READ 隔离级别其实是可以解决幻读问题发生的,主要有下面两种情况:
- 快照读:由 MVCC 机制来保证不出现幻读。
- 当前读:使用 Next-Key Lock 进行加锁来保证不出现幻读,Next-Key Lock 是行锁(Record Lock)和间隙锁(Gap Lock)的结合,行锁只能锁住已经存在的行,为了避免插入新行,需要依赖间隙锁。
怎么解决幻读
解决幻读的方式有很多,但是它们的核心思想就是一个事务在操作某张表数据的时候,另外一个事务不允许新增或者删除这张表中的数据了。解决幻读的方式主要有以下几种:
- 将事务隔离级别调整为
SERIALIZABLE
。 - 在可重复读的事务级别下,给事务操作的这张表添加表锁。
- 在可重复读的事务级别下,给事务操作的这张表添加
Next-key Lock(Record Lock+Gap Lock)
MVCC
MVCC是怎么解决不可重复读的
MVCC
的实现依赖于:隐藏字段、Read View、undo log。在内部实现中,InnoDB 通过数据行隐藏字段的最后一次修改数据行的事务id DB_TRX_ID
和 Read View
来判断数据的可见性,如不可见,则通过数据行的 DB_ROLL_PTR
找到 undo log 中的历史版本。Read View会记录:当前事务id,当前活跃事务【也就是还没有提交】的事务id,下一个要分配的事务id。
可见性算法:如果当前数据版本的trx_id == creator_trx_id 说明修改这条数据的事务就是当前事务,所以可见。 如果当前数据版本的trx_id < min_trx_id(当前活跃列表的事务id最小值),说明修改这条数据的事务在当前事务生成readView 的时候已提交,所以可见。 如果当前数据版本的trx_id 大小在 min_trx_id 和 max_trx_id (下一个要分配的事务id)之间,此时 trx_id 若在 m_ids中,说明修改这条数据的事务此时还未提交,所以不可见,若不在m_ids 中,表明事务已经提交,可见。 如果当前数据版本的trx_id >=max_trx_id,说明修改这条数据的事务在当前事务生readView 的时候还未启动,所以不可见(结合事务ID递增来看)。
- 在RC隔离级别下的 每次select 查询前都生成一个 Read View(m_ids 列表)
- 在RR 隔离级别下只在事务开始后 第一次select 数据前生成一个 Read Viw(m_ids 列表)
讲一下undolog版本链
undolog版本链主要是通过roll_pointer这个隐藏字段,当新插入一条数据的时候,txr_id会记录插入这条数据的事务id,roll_pointer指向空;当有别的事务对这条数据进行修改,那么更新之前会生成一份undolog,记录事务修改签的值,让新生成数据的roll_pointer隐藏字段指向生成的这份undolog。
MySQL中的长事务有什么问题?
长事务意味着系统里面会存在很老的事务视图,在这个事务提交之前,回滚记录都要保留,这会导致大量占用存储空间。
除此之外,长事务还占用锁资源,可能会拖垮库。
MVCC➕Next-key-Lock 防止幻读
InnoDB
存储引擎在 RR 级别下通过 MVCC
和 Next-key Lock
来解决幻读问题:
1、只执行普通 select
,此时会以 MVCC
快照读的方式读取数据
在快照读的情况下,RR 隔离级别只会在事务开启后的第一次查询生成 Read View
,并使用至事务提交。所以在生成 Read View
之后其它事务所做的更新、插入记录版本对当前事务并不可见,实现了可重复读和防止快照读下的 “幻读”
2、执行 select...for update/lock in share mode、insert、update、delete 等当前读
在当前读下,读取的都是最新的数据,如果其它事务有插入新的记录,并且刚好在当前事务查询范围内,就会产生幻读!InnoDB
使用 Next-key Lockopen in new window 来防止这种情况。当执行当前读时,会锁定读取到的记录的同时,锁定它们的间隙,防止其它事务在查询范围内插入数据。只要我不让你插入,就不会发生幻读。