mysql 事务

事务的四大特性(ACID)

  • 原子性(atomicity):一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被==回滚(Rollback)==到事务开始前的状态,就像这个事务从来没有执行过一样。
  • 一致性(consisitency):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
  • 隔离性(Durability)(lsolation):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)读提交(read committed)可重复读(repeatable read)和串行化(Serializable)
  • 持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

事务的控制语句

  • BEGINSTART TRANSACTION 显式地开启一个事务(操作默认开启事务);
  • COMMIT 也可以使用 COMMIT WORK,不过二者是等价的。COMMIT 会提交事务,并使已对数据库进行的所有修改成为永久性的;
  • ROLLBACK 也可以使用 ROLLBACK WORK,不过二者是等价的。回滚会结束用户的事务,并撤销正在进行的所有未提交的修改;
  • SAVEPOINT identifier,SAVEPOINT 允许在事务中创建一个保存点,一个事务中可以有多个 SAVEPOINT;
  • RELEASE SAVEPOINT identifier 删除一个事务的保存点,当没有指定的保存点时,执行该语句会抛出一个异常;
  • ROLLBACK TO identifier 把事务回滚到标记点;
  • SET TRANSACTION 用来设置事务的隔离级别。InnoDB 存储引擎提供事务的隔离级别有READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ 和 SERIALIZABLE。

补充:直接用 SET 来改变 MySQL 的自动提交模式:

  • SET AUTOCOMMIT=0 禁止自动提交
  • SET AUTOCOMMIT=1 开启自动提交

日志系统

redo log,undo log bin log[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

redo log(重做日志):

在这里插入图片描述

实现事务的持久性

该日志分为两部分:重做日志缓冲(redo log buff)以及重做日志文件(redo log)

redo log 是物理日志,记载着每次在某个页上做了什么修改。写redo log也是需要写磁盘的,但它的好处就是顺序IO(我们都知道顺序IO比随机IO快非常多)。写入的速度很快

生命周期:

​ 1.事务开始之后,就开始产生 redo log 日志了,在事务执行的过程中,redo log开始逐步落盘

​ 2.当对应事务的脏页写入到磁盘之后,redo log 的使命就完成了,它所占用的空间也就可以被覆盖了。

​ 3.InnoDB 的 redo log 是固定大小的,比如可以配置为一组 4 个文件,每个文件的大小是 1GB。从头开始写,写到末尾就又回到开头循环写

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  1. write pos 是当前记录的位置,一边写一边后移,写到第 3 号文件末尾后就回到 0 号文件开头。checkpoint 是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件。
  2. write pos 和 checkpoint 之间的的部分,可以用来记录新的操作。如果 write pos 追上 checkpoint,表示“粉板”满了,这时候不能再执行新的更新,得停下来先擦掉一些记录,把 checkpoint 推进一下。

存储内容:

​ redo log 包括两部分:

  • 一是内存中的日志缓冲(redo log buffer),该部分日志是易失性的;
  • 二是磁盘上的重做日志文件(redo log file),该部分日志是持久的,redo log 存储的是物理格式的日志,记录的是物理数据页面的修改信息,它是顺序写入 redo log file 中的。

落盘的方式(将 innodb 日志缓冲区的日志刷新到新的磁盘中)

  1. Master Thread 每秒一次执行刷新 Innodb_log_buffer 到重做日志文件
  2. 每个事务提交时会将重做日志刷新到重做日志文件
  3. 当重做日志缓存可用空间少于一半时,重做日志缓存被刷新到重做日志文件

注意:

  1. 其实写redo log的时候,也会有buffer,是先写buffer,再真正落到磁盘中的。至于从buffer什么时候落磁盘,会有配置供我们配置。
  2. redo log 是 InnoDB 引擎==特有的日志==
  3. 持久性就是靠redo log来实现的(如果写入内存成功,但数据还没真正刷到磁盘,如果此时的数据库挂了,我们可以靠redo log来恢复内存的数据,这就实现了持久性)。

undo log(回滚日志)

保存了事务发生之前的数据的一个版本,作用:

  1. 可以用于回滚
  2. 同时可以提供多版本并发控制下的读(MVCC),也即非锁定读

生命周期:

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

​ 2.事务提交之后, undo log 并不能立马被删除,而是放入待清理的链表中

  1. purge 线程判断是否有其它事务在使用 undo 段中表的上一个事务之前的版本信息,从而决定是否可以清理 undo log 的日志空间

存储内容:

undo log 存储的是逻辑格式的日志,保存了事务发生之前的上一个版本的数据,可以用于回滚。当一个旧的事务需要读取数据时,为了能读取到老版本的数据,需要顺着 undo 链找到满足其可见性的记录。

存储位置:

在这里插入图片描述

默认情况下,undo 文件是保存在共享表空间的,也即 ibdatafile 文件中,当数据库中发生一些大的事务性操作的时候,要生成大量的 undo log 信息,这些信息全部保存在共享表空间中,因此共享表空间可能会变得很大,默认情况下,也就是 undo log 使用共享表空间的时候,被“撑大”的共享表空间是不会、也不能自动收缩的。因此,MySQL5.7 之后的“独立 undo 表空间”的配置就显得很有必要了。

bin log(二进制日志)

  • binlog我们可以简单理解为:存储着每条变更的SQL语句
  • 可以通过binlog来对数据进行恢复
  • binlog 可以用于主从复制中,从库利用主库上的 binlog 进行重播,实现主从同步。用于数据库的基于时间点、位点等的还原操作。binlog 的模式分三种:Statement、Row、Mixed。

bin-log 三种模式

Statement 模式

每一条修改数据的 sql 都会记录到 master 的 binlog 中,slave 在复制的时候,sql 进程会解析成和原来在 master 端执行时的相同的 sql 再执行。

  • 优点:在 statement 模式下首先就是解决了 row 模式的缺点,不需要记录每一行数据的变化,从而减少了 binlog 的日志量,节省了 I/O 以及存储资源,提高性能。因为它只需要记录在 master 上执行的语句的细节以及执行语句的上下文信息。
  • 缺点:在 statement 模式下,由于它是记录的执行语句,所以,为了让这些语句在 slave 端也能正确执行,那么它还必须记录每条语句在执行的时候的一些相关信息,即上下文信息,以保证所有语句在 slave 端和在 master 端执行结果相同。另外就是,由于 MySQL 现在发展比较快,很多新功能不断的加入,使 MySQL 的复制遇到了不小的挑战,自然复制的时候涉及到越复杂的内容,bug 也就越容易出现。在statement 中,目前已经发现不少情况会造成 MySQL 的复制出现问题,主要是在修改数据的时候使用了某些特定的函数或者功能才会出现,比如:sleep() 函数在有些版本中就不能被正确复制,在存储过程中使用了 last_insert_id() 函数,可能会使 slave 和 master 上得到不一致的 id 等等。由于 row 模式是基于每一行来记录变化的,所以不会出现类似的问题。

Row 模式

日志中会记录每一行数据被修改的形式,然后在 slave 端再对相同的数据进行修改。row 模式只记录要修改的数据,只有 value,不会有 sql 多表关联的情况。

  • 优点:在 row 模式下,binlog 中可以不记录执行的 sql 语句的上下文相关的信息,仅仅只需要记录哪一条记录被修改了,修改成什么样了,所以 row 的日志内容会非常清楚的记录下每一行数据的修改细节,非常容易理解。而且不会出现某些特定情况下的存储过程和 function,以及 trigger 的调用和触发无法被正确复制问题。
  • 缺点:在 row 模式下,当所有执行语句记录到日志中的时候,都将以每行记录的修改来记录,这样可能会产生大量的日志内容。

Mixed 模式

从官方文档中看到,之前的 MySQL 一直都只有基于 statement 的复制模式,直到 5.1.5 版本的 MySQL 才开始支持 row 复制。从 5.0 开始,MySQL 的复制已经解决了大量老版本中出现的无法正确复制的问题。但是由于存储过程的出现,给 MySQL Replication 又带来了更大的新挑战。另外,看到官方文档说,从 5.1.8 版本开始,MySQL 提供了除 Statement 和 Row 之外的第三种复制模式:Mixed,实际上就是前两种模式的结合。在 Mixed 模式下,MySQL 会根据执行的每一条具体的 SQL 语句来区分对待记录的日志形式,也就是在 statement 和 row 之间选择一种。新版本中的 statment 还是和以前一样,仅仅记录执行的语句。而新版本的 MySQL 也对 row 模式做了优化,并不是所有的修改都会以 row 模式来记录,比如遇到表结构变更的时候就会以 statement 模式来记录,如果 SQL 语句确实就是 update 或者 delete 等修改数据的语句,那么还是会记录所有行的变更。

生命周期:

事务提交的时候,一次性将事务中的 sql 语句(一个事务可能对应多个 sql 语句)按照一定的格式记录到 binlog 中,这里与 redo log 很明显的差异就是 redo log 并不一定是在事务提交的时候才刷新到磁盘,而是在事务开始之后就开始逐步写入磁盘。binlog 的默认保存时间是由参数 expire_logs_days 配置的,对于非活动的日志文件,在生成时间超过 expire_logs_days 配置的天数之后,会被自动删除。

binlog和redo log 写入的细节 - 两阶段提交

在这里插入图片描述

事务特性的实现原理

**原子性:**利用innodb的undolog。叫做回滚日志(实现对数据库的反向操作)

当事务回滚的时候,能够撤销所有已经成功执行的sql语句,需要记录你要回滚的响应日志

例如:

当开启事务执行update 操作时,需要记录之前的旧值,回滚的时候,根据旧值进行update 操作

undo log 记录 了这些回滚需要的信息,当事务执行失败,就可以利用undo log 中的信息将数据回滚到修改之前的样子

持久性: redo log 实现

​ **问题:**mysql把磁盘上的数据加载到内存中,在内存中对数据进行修改,在刷回磁盘上。
​ 此时宕机,内存中的数据就会丢失。

​ 如何避免宕机?
​ 在事物提交之前将数据写入磁盘中。只修改一个页面里的一个字节,就要将整个页面 刷入磁盘,太浪费资源 了,毕竟一个事务里的SQL可能牵涉到多个数据页的修改,而这些数据页可能不是相邻的,也就是属于随机IO。 显然操作随机IO,速度会比较慢。

​ 采用redo log
​ 做数据修改的时候,不仅在内村中操作,还会在redo log 中记录这次操作,
​ 提交事物的时候,会将redo log 日志进行刷盘 redo log 一部分在内存中,一部分在磁盘上。
​ 数据库宕机重启,就将redo log中的内容恢复到数据库中,再根据undo log 和binlog 内容决定
​ 回滚还是提交数据。
​ 好处?
​ redo log 体积小,只记录了那一页修改了什么,体积小,刷盘快。
​ 一直往末尾进行追加,属于顺序io.效率比随机io来快。

隔离性: 利用锁和mvcc机制

​ MVCC,即多版本并发控制(Multi Version Concurrency Control),
​ 一个行记录数据有多个版本对快照数据,这些快照数据在undo log中。

​ 如果一个事务读取的行正在做DELELE或者UPDATE操作,
​ 读取操作不会等行上的锁释放,而是读取该行的快照版本。

​ 在事务隔离级别为读已提交(Read Commited)时,一个事务能够读到另一个事务已经提交的数据,
​ 是不满足隔离性的。但是当事务隔离级别为可重复读(Repeateable Read)中,是满足隔离性的。

一致性:

​ 两个层面。
​ 1.数据库 通过原子性, 持久性,隔离性,来保证一致性。
​ AID 三大特性,是前提。
​ 2.应用层面 通过代码判断数据库数据是否有效,然后再决定是回滚还是提交数据。

隔离级别

  • 未提交读(read uncommitted)
  • 读提交(read committed)
  • 可重复读(repeatable read)
  • 串行读(serializable)

对于不同的事务,采用不同的隔离级别分别有不同的结果。不同的隔离级别有不同的现象。主要有下面3种:

  • 脏读(dirty read):一个事务可以读取另一个尚未提交事务的修改数据。
  • 不可重复读(nonrepeatable read):在同一个事务中,如果数据被其他事务修改,不能重复读取该记录原始值。
  • 幻读(phantom read):在同一事务中,同一查询多次进行时候,由于其他插入操作(insert)的事务提交,导致每次返回不同的结果集
  • 更新丢失(Lost Update):当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,由于每个事务都不知道其他事务的存在,就会发生丢失更新问题——最后的更新覆盖了其他事务所做的更新。例如,两个编辑人员制作了同一文档的电子副本。每个编辑人员独立地更改其副本,然后保存更改后的副本,这样就覆盖了原始文档。最后保存其更改保存其更改副本的编辑人员覆盖另一个编辑人员所做的修改。如果在一个编辑人员完成并提交事务之前,另一个编辑人员不能访问同一文件,则可避免此问题(并发问题)

不同隔离级别产生不同的现象:

在这里插入图片描述

注意:
1.read committed:不可以杜绝幻象读.
2.serializable: 当没有开启autocommit,InnoDB会将select转换为 SELECT … LOCK IN SHARE MODE(加S锁).如果开启autocommit,那么和普通事务中的select一样为一致性非锁定读.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值