mysql事务隔离机制及原理

事务特性指的就是ACID。

原子性 Atomicity :一个事务(transaction)中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被恢复(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。即,事务不可分割、不可约简。
一致性 Consistency :在事务开始和完成时,数据必须保持一致。这意味着所有相关的数据规则都必须应用于事务的修改,以保持数据的完整性;事务结束时,所有的内部数据结构(如B树索引或双向链表)也都必须是正确的。
隔离性 Isolation :数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
持久性 Durability :事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

并发带来的问题

更新丢失(Lost Updated)
当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,由于每个事务都不知道其他事务的存在,就会发生丢失更新问题,也就是最后的更新会覆盖掉先前的更新操作。
脏读(Dirty Reads)
事务A读取了事务B已经修改但未提交的数据,如果事务B进行回滚,事务A读取的数据无效,不符合一致性。
不可重复读(Non-Repeatable Reads)
在同⼀个事务中执⾏同⼀个读取操作,前后两次查询结果不一样,同一行数据有变化 (强调更新操作)不符合隔离型。
幻读(Phantom Reads)
在同⼀个事务中执⾏同⼀个读取操作,前后两次查询结果不一样,多了几行数据(强调新增操作)不符合隔离型。
事务隔离级别
上述所说的"脏读",“不可重复读”,"幻读"这些问题,其实就是数据库读一致性问题,必须由数据库提供的事务隔离机制来进行解决。在这里插入图片描述
mysql默认事务隔离级别为可重复读(RR)

数据库的事务隔离越严格,并发副作用越小,但付出的代价越大;因为事务隔离本质就是使事务在一定程度上处于串行状态,这本身就是和并发相矛盾的。

在这里插入图片描述

事务实现原理

事务的实现是基于数据库的存储引擎。 不同的存储引擎对事务的支持不一样,接下来以mysql数据库中InnoDB引擎来解说.

InnoDB引擎是mysql的默认存储引擎,隔离级别为可重复读(RR),并在RR隔离级别下通过

一般是通过锁机制,解决掉不可重复读和幻读的问题。是不是可以通过乐观锁的问题去解决不可重复读和幻读的问题,MySQL 采用的是 MVCC 机制来解决脏读、不可重复读的问题。

MVCC 英文全称是 Muitiversion Concurrency Control,多版本并发控制器,原理是通过数据行的多个版本管理实现数据库的并发控制,通过保存数据的历史版本,可以通过比较版本号决定数据是否显示,读取数据的时候不需要加锁保证事务的隔离效果。

MVCC 是如何解决脏读的?

MVCC 解决了一致性读问题,当我们读取某个数据库在某个时间点的快照时,只能看到时间点之前提交更新的结果,不能看到时间点之后事务提交的更新结果。这样就避免了脏读问题。

MVCC 是如何解决不可重复读的?

在说如何解决不可重复读之前,先谈谈 MVCC 的实现原理。
隐藏字段
在内部,InnoDB 存储引擎为每行数据添加了三个隐藏字段:

DB_TRX_ID(6字节):表示最后一次插入或更新该行的事务 id。此外,delete 操作在内部被视为更新,只不过会在记录头 Record header 中的 deleted_flag 字段将其标记为已删除。
DB_ROLL_PTR(7字节) 回滚指针,指向该行的 undo log 。如果该行未被更新,则为空。
DB_ROW_ID(6字节):如果没有设置主键且该表没有唯一非空索引时,InnoDB 会使用该 id 来生成聚簇索引。
ReadView
主要是用来做可见性判断,里面保存了 “当前对本事务不可见的其他活跃事务”

主要有以下字段:

m_low_limit_id:目前出现过的最大的事务 ID+1,即下一个将被分配的事务 ID。大于等于这个 ID 的数据版本均不可见。
m_up_limit_id:活跃事务列表 m_ids 中最小的事务 ID,如果 m_ids 为空,则 m_up_limit_id 为 m_low_limit_id。小于这个 ID 的数据版本均可见。
m_ids:Read View 创建时其他未提交的活跃事务 ID 列表。创建 Read View时,将当前未提交事务 ID 记录下来,后续即使它们修改了记录行的值,对于当前事务也是不可见的。m_ids 不包括当前事务自己和已提交的事务(正在内存中)。
m_creator_trx_id:创建该 Read View 的事务 ID。

undo log
主要有两个作用:

1.当事务回滚时用于将数据恢复到修改前的样子。
2. 当读取记录时,若该记录被其他事务占用或当前版本对该事务不可见,则可以通过 undo log 读取之前的版本数据,以此实现非锁定读。
快照读

快照读,读取的是历史数据,一般不加锁的 select 查询都是快照读。

当前读

当前读就是读的最新数据,而不是历史数据,加锁的 select,或者对数据进行增删改都会读取当前最新数据。

幻读的快照读通过mvcc解决的
幻读的当前度通过间隙锁解决的
因此,InnoDB的RR隔离级别实现了串行化级别的效果,而且还保留了很好的并发性能。

事务隔离性是通过锁来实现的,而事务的原子性,一致性和持久性都是事务日志来实现的。 也就redo log日志和undo log日志
重做日志 (redo log)

MySQL 中用 redo log 来在系统崩溃重启之类的情况时修复数据,保证事务的持久性。

InnoDB 存储引擎是以页为单位来管理存储空间的,我们进行的增删改查操作其实本质上都是在访问页面(包括读页面、写页面、创建新页面等操作)。
1️⃣ redo 日志的作用

确保事务的持久性。redo日志记录事务执行后的状态,用来恢复未写入 data file 的已成功事务更新的数据。防止在发生故障的时间点,尚有脏页未写入磁盘,在重启 MySQL 服务的时候,根据 redo log 进行重做,从而达到事务的持久性这一特性。

2️⃣ 什么时候产生的

事务开始之后就产生redo log,redo log的落盘并不是随着事务的提交才写入的,而是在事务的执行过程中,便开始写入redo log文件中。

3️⃣ 什么时候释放

当对应事务的脏页写入到磁盘之后,redo log的使命也就完成了,重做日志占用的空间就可以重用(被覆盖)。

4️⃣ 对应的物理文件

MySQL 的数据目录(使用 SHOW VARIABLES LIKE 'datadir’查看)下默认有两个名为 ib_logfile0 和 ib_logfile1 的文件,log buffer 中的日志默认情况下就是刷新到这两个磁盘文件中。

innodb_log_group_home_dir:指定日志文件组所在的路径,默认./ ,表示在数据库的数据目录下。

innodb_log_files_in_group:指定重做日志文件组中文件的数量,默认 2。

innodb_log_file_size:该参数指定了每个 redo 日志文件的大小,默认值为 48MB。

innodb_mirrored_log_groups: 指定了日志镜像文件组的数量,默认 1。

5️⃣ redo log 什么时候写盘

在事务开始之后逐步写盘的。
————————————————
版权声明:本文为CSDN博主「Ayue、」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_43477531/article/details/121884084
撤销日志 (undo log)
我们说过事务需要保证原子性,也就是事务中的操作要么全部完成,要么什么也不做。但是偏偏有时候事务执行到一半会出现一些情况,比如:

事务执行过程中可能遇到各种错误,比如服务器本身的错误,操作系统错误,甚至是突然断电导致的错误。
程序员可以在事务执行过程中手动输入 ROLLBACK 语句结束当前的事务的执行。
这两种情况都会导致事务执行到一半就结束,但是事务执行过程中可能已经修改了很多东西,为了保证事务的原子性,我们需要把东西改回原先的样子,这个过程就称之为回滚(英文名:rollback),这样就可以造成这个事务看起来什 么都没做,所以符合原子性要求。

每当我们要对一条记录做改动时(这里的改动可以指 INSERT、DELETE、UPDATE),都需要把回滚时所需的东西都给记下来

1️⃣ undo 日志的作用

保证数据的原子性,保存了事务发生之前的数据的一个版本,可以用于回滚,同时可以提供多版本并发控制下的读(MVCC),也即非锁定读。

2️⃣ 什么时候产生的

事务开始之前,将当前是的版本生成undo log,undo 也会产生 redo 来保证undo log的可靠性。

3️⃣ 什么时候释放

当事务提交之后,undo log并不能立马被删除,而是放入待清理的链表,由purge线程判断是否由其他事务在使用undo段中表的上一个事务之前的版本信息,决定是否可以清理undo log的日志空间。

4️⃣ 对应的物理文件

MySQL5.6之前,undo表空间位于共享表空间的回滚段中,共享表空间的默认的名称是ibdata,位于数据文件目录中。

MySQL5.6之后,undo表空间可以配置成独立的文件,但是提前需要在配置文件中配置,完成数据库初始化后生效且不可改变undo log文件的个数。

如果初始化数据库之前没有进行相关配置,那么就无法配置成独立的表空间了。
————————————————
版权声明:本文为CSDN博主「Ayue、」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_43477531/article/details/121884084

所以,redo log其实保障的是事务的持久性和一致性,而undo log则保障了事务的原子性。

  1. 二进制日志 (binlog)
    1️⃣ 作用

用于复制,在主从复制中,从库利用主库上的binlog进行重播,实现主从同步。

用于数据库的基于时间点的还原。

2️⃣ 什么时候产生

事务提交的时候,一次性将事务中的sql语句(一个事物可能对应多个sql语句)按照一定的格式记录到binlog中。

这里与redo log很明显的差异就是redo log并不一定是在事务提交的时候刷新到磁盘,redo log是在事务开始之后就开始逐步写入磁盘。

因此对于事务的提交,即便是较大的事务,提交(commit)都是很快的,但是在开启了bin_log的情况下,对于较大事务的提交,可能会变得比较慢一些。

这是因为binlog是在事务提交的时候一次性写入的造成的。

3️⃣ 什么时候释放

binlog的默认是保持时间由参数 expire_logs_days 配置,也就是说对于非活动的日志文件,在生成时间超过expire_logs_days配置的天数之后,会被自动删除。

4️⃣ 对应的物理文件

配置文件的路径为 log_bin_basename,binlog 日志文件按照指定大小,当日志文件达到指定的最大的大小之后,进行滚动更新,生成新的日志文件。

对于每个binlog日志文件,通过一个统一的index文件来组织。

二进制日志的作用之一是还原数据库的,这与 redo log 很类似,很多人混淆过,但是两者有本质的不同

作用不同

redo log是保证事务的持久性的,是事务层面的,binlog 作为还原的功能,是数据库层面的(当然也可以精确到事务层面的),虽然都有还原的意思,但是其保护数据的层次是不一样的。

内容不同

redo log 是物理日志,是数据页面的修改之后的物理记录,binlog是逻辑日志,可以简单认为记录的就是sql语句。

另外,两者日志产生的时间,可以释放的时间,在可释放的情况下清理机制,都是完全不同的。

关于事务提交时,redo log 和 binlog 的写入顺序,为了保证主从复制时候的主从一致(当然也包括使用binlog进行基于时间点还原的情况),是要严格一致的,MySQL 通过两阶段提交过程来完成事务的一致性的,也即redo log和binlog的一致性的,理论上是先写redo log,再写binlog,两个日志都提交成功(刷入磁盘),事务才算真正的完成。

binlog 有三种格式:

Statement(Statement-Based Replication,SBR):每一条会修改数据的 SQL 都会记录在 binlog 中,日志量很小。
Row(Row-Based Replication,RBR):不记录 SQL 语句上下文信息,仅保存哪条记录被修改的详细信息,日志数据量很大。
Mixed(Mixed-Based Replication,MBR):Statement 和 Row 的混合体,一般的语句修改使用 Statement 格式保存 binlog;对于一些 Statement 无法准确完成主从复制的操作,则采用 Row 格式保存 binlog。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值