事务的四大特性?如何实现?各隔离级别的问题?隔离性如何实现?
事务
- 概念:事务是一组数据库操作,要么都执行,要么都不执行。
事务特性:ACID
原子性:事务是不可分割的最小执行单位,保证事务要么都完成,要么都不完成。(通过原子提交协议——二阶段提交实现)
一致性:数据库总是从一个一致性的状态转换到另一个一致性的状态。(依靠Server层的binlog在MySQL使用不同引擎时都能使用的特点实现)
隔离性:保证并发访问数据库的事务之间互不影响,隔离性也有不同的级别,但一般来说,一个事务在提交之前对其他事务是不可见的。
持久性:事务一旦提交,对数据库的改变是持久的。(依靠redo log提供的crashsafe能力实现)
隔离级别(概念及每个隔离级别会出现的问题)
读未提交:事务中的修改,即使没有提交,对其他事务也都是可见的;会出现的问题是脏读:事务读取未提交的数据的行为
读提交:事务只能看见已提交的事务所做的修改;这种级别也叫不可重复读:因为两次执行同样的查询可能得到不一样的结果
可重复读:事务在执行期间看到的数据是不变的,重复读取数据结果均相同;根据SQL标准的定义,可重复读隔离级别会导致幻读的问题,但在实际的实现中,每种存储引擎实现的隔离级别是不相同的,InnoDB就使用 MVCC+next-key lock 完全解决了幻读的问题
会出现的问题是幻读:事务A 在读取某个范围内的数据时,事务B 在该范围插入了新的数据,事务A再次读取该范围数据时,读到了事务B 插入的数据 称为幻读。InnoDB通过 MVCC和next-key锁 解决了幻读的问题,完全保证了事务隔离级别,已经达到了SQL标准的串行化隔离级别。
InnoDB幻读的解决方法:
在快照读读情况下通过MVCC来避免幻读
在当前读读情况下通过next-key lock来避免幻读
串行化:通过强制事务排序,使之不可能发生冲突,从而解决幻读问题。
隔离性的实现 (MVCC+next-key lock)
InnoDB引擎可以在可重复读级别就实现了完全的事务隔离(即SQL标准的串行化级别),其实现简单来说就是 快照读时用MVCC 和 当前读时用next-key lock。
快照读的实现:针对普通的select查询使用快照读,InnoDB利用多版本并发控制(MVCC)为每行记录添加了一个版本号(row trx_id),使事务通过回滚读取版本号,确保事务读取到的数据,要么是在事务开始之前已经存在的,要么是事务自身增删改的,解决了不可重复读,并且在快照读情况下避免了幻读问题。补充一句,事务通过快照来判断一条数据是否可见(若版本号小于快照中的低水位或等于自己事务的版本号则可见,否则不可见)可重复读级别下快照在每个事务的第一个select语句运行时产生,由此保证了一致性,而读提交在每一条SQL语句执行时都产生快照**,这就导致不可重复读。
当前读的实现:针对 select … lock in share mode、select … for update、insert、update、delete操作,我们必须要使用当前读取得最新版本的记录,而为了保证当前读的一致性,我们要阻塞其他事务的当前读,所以要对数据行进行加锁(事务提交后释放),使用范围索引时要使用next-key lock避免出现幻读现象。