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 锁范围内插入了一条记录,那么这个插入语句就会被阻塞,无法成功插入,所以就很好地避免了幻读问题。

相关推荐
weixin_424999362 小时前
CSS如何处理移动端弹窗背景滚动_使用JS控制配合CSS样式锁定
jvm·数据库·python
m0_515098422 小时前
golang如何实现日志按级别过滤_golang日志按级别过滤实现教程
jvm·数据库·python
m0_674294642 小时前
怎么为MongoDB事务调优:将读操作尽量移到事务外面执行
jvm·数据库·python
吕源林2 小时前
React Native 中 Button 组件未定义的解决方案
jvm·数据库·python
m0_743623922 小时前
TensorFlow如何实现循环神经网络_使用LSTM或GRU层处理时间序列
jvm·数据库·python
2401_871696522 小时前
HTML怎么配合JavaScript交互_HTML DOM操作入门【指南】
jvm·数据库·python
weixin_408717772 小时前
HTML图片怎么用Bitbucket Pipelines发布_Bitbucket自动构建HTML站点
jvm·数据库·python
m0_747854522 小时前
企业级SQL注入防护手段_使用专用的数据库安全网关
jvm·数据库·python
weixin_424999362 小时前
Golang reflect反射怎么用_Golang反射教程【通俗】
jvm·数据库·python