MySQL:事务机制
标题事务有哪些特性ACID原子性一个事务中的所有操作要么全部完成要么全部不完成不会结束在中间的某一个环节。而且事务在执行过程中发生错会被回滚到事务开始前的状态就像这个事务没有被执行过一样。一致性指事务操作前和操作后数据满足完整性约束数据库保持一致性状态。隔离性数据库允许多个并发事务同时对其数据进行读写和修改的能力隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致因为多个事务同时使用相同的数据时不会相互干扰每个事务都会有一个完整的数据空间对其他并行事务时隔离的。持久性事务处理结束后对数据的修改就是永久的即便系统故障也不会丢失。InnoDB引擎通过什么技术来保证事务的这四个特性的呢持久性是通过redo log重做日志来保证的原子性是通过undo log回滚日志来保证的隔离性是通过MVCC多版本并发控制或锁机制来保证的一致性是通过持久性原子性隔离性来保证的并行事务会引发什么问题MySQL是允许多个客户端连接的这代表了MySQL会出现同时处理多个事务的情况。处理多个事务时就会出现脏读不可重复读幻读的问题。脏读读到了别的事务还没提交的数据万一呢个事务回滚了你读到的数据压根不存在就像读了给脏数据不可重复读同一个事务里两次读取同一行数据结果不一样。因为中间有别的事务改了这行数据并提交了。强调的是数据内容变了。幻读同一个事务里两次执行同样的查询范围返回的行数不一样。因为中间有别的事务插入或删除了符合条件的数据。强调的是数据行数改变了。事务的隔离级别前面我们提到了脏读幻读不可重复读。这些现象会对事务的一致性产生不停程度的影响。严重程度如下SQL标准提出了四种隔离界别来规避这些现象隔离级别越高性能效率就越低。读未提交指一个事务还没提交时它做的变更就能被其他事务看到读提交指一个事务提交之后他做的变更才能被其他事务看到可重复读指一个事务执行过程中看到的数据一直跟这个事务启动时看到的数据是一直的MySQL innodb引擎的默认隔离级别串行化会对记录加上读写锁在多个事务对这条记录进行读写操作时如果发生了读写冲突后访问的事务必须等前一个事务执行完成才能继续执行按隔离水平高低排序如下对于不同隔离级别并发事务时可能发生的现象页不同。MySQL 在「可重复读」隔离级别下可以很大程度上避免幻读现象的发生注意是很大程度避免并不是彻底避免所以 MySQL 并不会使用「串行化」隔离级别来避免幻读现象的发生因为使用「串行化」隔离级别会影响性能。MySQL InnoDB 引擎的默认隔离级别虽然是「可重复读」但是它很大程度上避免幻读现象。对于幻读现象解决方案有两种针对快照读普通select语句通过MVCC方式来解决幻读。在可重复读隔离机制下事务执行过程中看到的数据一直跟这个事务启动时看到的数据时一致的即使途中有其他事务插入了一条数据是查询不出来这条数据的针对当前读selset…for update)通过next-key-lock记录锁间隙锁方式解决了幻读因为当执行 select … for update 语句的时候会加上 next-key lock如果有其他事务在 next-key lock 锁范围内插入了一条记录那么这个插入语句就会被阻塞无法成功插入所以就很好地避免了幻读问题。Read View在MVCC中如何工作的Read View 中四个字段作用Read View 有四个重要的字段• m_ids 指的是在创建 Read View 时当前数据库中「活跃事务」的事务 id 列表注意是一个列表“活跃事务”指的就是启动了但还没提交的事务并且不包括创建该 Read View 的事务自身。• min_trx_id 指的是在创建 Read View 时当前数据库中「活跃事务」中事务id 最小的事务也就是 m_ids 的最小值。• max_trx_id 这个并不是 m_ids 的最大值而是创建 Read View 时下一个将要分配给新事务的事务 id 值即全局事务 id 计数器的当前值。• creator_trx_id 指的是创建该 Read View 的事务的事务 id。聚簇索引记录中两个跟事务有关的隐藏列对于InnoDB存储引擎的数据库表它的聚簇索引记录中都包含下面两个隐藏列trx_id当一个事务对某条聚簇索引记录进行改动时就会把该事务的事务 id 记录在 trx_id 隐藏列里• roll_pointer每次对某条聚簇索引记录进行改动时都会把旧版本的记录写入到 undo 日志中然后这个隐藏列是个指针指向每一个旧版本记录于是就可以通过它找到修改前的记录。总结事务是在MySQL引擎层实现的常见的innodb是支持事务的事务的四大特性是原子性一致性隔离性持久性。当事务并发执行时会引发脏读不可重复读幻读这些问题为了避免这些问题SQL提出了四种隔离级别分别是读未提交读已提交可重复读串行化隔离级别一次递增性能越差。要解决脏读问题就要将隔离级别上升为读已提交以上级别。要解决不可重复读问题就要将隔离级别升级到可重复读以上级别对于脏读不建议将隔离级别升级到串行化因为会导致数据库并发性能很差。而对于幻读现象不建议将隔离级别升级为串行化因为这会导致数据库并发时性能很差。MySQL InnoDB 引擎的默认隔离级别虽然是「可重复读」但是它很大程度上避免幻读现象并不是完全解决了详见这篇文章(opens new window)解决的方案有两种• 针对快照读普通 select 语句是通过 MVCC 方式解决了幻读因为可重复读隔离级别下事务执行过程中看到的数据一直跟这个事务启动时看到的数据是一致的即使中途有其他事务插入了一条数据是查询不出来这条数据的所以就很好地避免了幻读问题。• 针对当前读select … for update 等语句是通过 next-key lock记录锁间隙锁方式解决了幻读因为当执行 select … for update 语句的时候会加上 next-key lock如果有其他事务在 next-key lock 锁范围内插入了一条记录那么这个插入语句就会被阻塞无法成功插入所以就很好地避免了幻读问题。