mysql事务,undolog,binlog,redolog,mvcc总结

1,362 阅读10分钟

~ 如有错误 恳请指正

1.什么是事务?事务特点以及开启与提交演示

把需要保证原子性、隔离性、一致性和持久性的一个或多个数据库操作称之为一个事务

  • 事务的特点

      -- 原子性 Atomicity
                  -- 要么都成功 要么都失败 
      -- 隔离性 Isolation
                  -- A事务在修改某条记录时候(也可以理解为状态转换,如扣钱,加钱等) 不会影响B事务内的状态转换(如扣钱,加钱)
      -- 一致性 Consistency
                  -- 只要最后的结果符合所有现实世界中的约束,那么就是符合一致性的。
                  -- 一致性由原子性和隔离性保证
      -- 持久性 Durability
                  --  当现实世界的一个状态转换完成后,这个转换的结果将永久的保留,这个规则被称为持久性
    
  • 事务开启,提交,关闭以及事务的类型,隐示提交和保存点

      -- 开启
      	BEGIN;-- 开启事务 
      	START TRANSACTION;-- 开启事务 注意 他可以携带事务类型
      -- 类型
      	-- READ ONLY:标识当前事务是一个只读事务,也就是属于该事务的数据库操作只能读取数据,而不能修改数据
      	-- READ WRITE:标识当前事务是一个读写事务,也就是属于该事务的数据库操作既可以读取数据,也可以修改数据。
      	-- WITH CONSISTENT SNAPSHOT:启动一致性读
    
      -- eg: (一般我们不设置两种事务类型哦)
      	START TRANSACTION READ ONLY;
      	START TRANSACTION READ ONLY, WITH CONSISTENT SNAPSHOT;
      	START TRANSACTION READ WRITE, WITH CONSISTENT SNAPSHOT
      	-- 如果不指定 那么默认是读写模式
      -- 提交与终止
      	COMMIT; -- 提交事务: 一般程序中我们不需要手动提交 
      	ROLLBACK; -- 终止事务:  一般程序中我们不需要手动提交 
      -- 查看数据库是否为自动提交
      	SHOW VARIABLES LIKE 'autocommit'; -- 查看事务自动提交属性是否开启
      	Variable_name	Value
      	autocommit	ON
                                              
      -- *** 注意 *** 平时我们不设置事务的话 那么每一条语句都算是一个独立的事务,这种特性称之为事务的自动提交 so 知道我们为啥在 service层设置 @Transactional(rollbackFor = Exception.class)了吧
      
      -- 事务的隐示提交与保存点
      -- 隐示提交 DDL语句 && ALTER 、CREATE 、DROP 、GRANT、RENAME 、REVOKE、SET PASSWORD等语句时也会隐式的提交前边语句所属于的事务。
      -- 保存点 SAVEPOINT 保存点名称; 
      		-- 回滚到某保存点 ROLLBACK  TO  保存点名称;
                              
    

事务回滚示例: image.png 事务正常提交示例:

image.png 事务保存点演示:

插入前: image.png

插入两条在1后设置保存点2完成后回滚到保存点1:

image.png

2.并发情况下事务会产生的问题&&隔离级别

2.0 脏写 有的地方也叫丢失更新 这种情况很严重 也就是说 某事务修改了另一个未提交事务修改过的数据,这还了的???所以4种隔离级别种 最低的隔离级别 读未提交 都会解决掉脏写这种问题

2.1 脏读:

如果一个事务读到了另一个事务未提交过的数据,那就意味着发生了脏读

脏读示意图:

image.png

2.2 可重复读

举例: 事务 1 读取表的一条数据期间,事务 2 更新了该条记录并提交,事务 1 再次读取该表该条记录时,发现和第一次内容不一致。也就是说某事务在执行过程中多次读取到某条记录的结果不一样。 示意图如下:

image.png

2.3 幻读

如果一个事务先根据某些条件查询出一些记录,之后另一个事务又向表中插入了符合这些条件的记录,原先的事务再次按照该条件查询时,能把另一个事务插入的记录也读出来,那就意味着发生了幻读

示意图: image.png

区别: 可重复度和幻读的区别:
幻读一般是针对insert可重复度一般是update
幻读和不可重复读都是读取了另一条已经提交的事务;但不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。

事务隔离级别 4种 mysql默认是不可重复读 oracle默认 读已提交

  • 读未提交 (Read Uncommitted) 是指: 一个事务还没提交时,它做的变更就能被别的事务看到。
    可以解决 脏写 但是 脏读,可重复读 幻读这3个问题解决不了

  • 读已提交 (Read Committed) 是指: 一个事务提交之后,它做的变更才会被其他事务看到。
    可以解决 脏读(因为其规定了 只有在已提交后 才会被其他事务看到) ,但是重复读和幻读问题解决不了

  • 可重复读 (Repeatable Read) 是指: 一个事务a执行过程中看到的数据,总是跟这个a事务在启动时看到的数据是一致的(本质是a事务对要读取的数据 比如表 b_table 加了写锁(行锁) 从而保证了多次读取b_table时的数据一致性)。
    可以解决 脏读,可重复读 但是不能解决幻读

  • 串行化 (Serializable): 顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。
    可以解决 脏读,可重复读,幻读

3. binlog

binlog是个啥?

  • binlog是用于记录数据库执行的写入性操作(不包括查询)信息,以二进制的形式保存在磁盘中。
  • binlog是mysql的逻辑日志,并且由Server层进行记录,使用任何存储引擎的mysql数据库都会记录binlog日志。
  • binlog 类型: - 逻辑日志: 可以简单理解为记录的就是sql语句。 - 物理日志: mysql数据最终是保存在数据页中的,物理日志记录的就是数据页变更 。

注意: binlog是通过追加的方式进行写入的,可以通过 max_binlog_size 参数设置每个 binlog 文件的大小,当文件大小达到给定值之后,会生成新的文件来保存日志。

binlog使用场景

在实际应用中, binlog 的主要使用场景有两个,分别是 主从复制 和 数据恢复 。

  • 主从复制 :在 Master 端开启 binlog ,然后将 binlog 发送到各个 Slave 端, Slave 端重放 binlog 从而达到主从数据一致。
  • 数据恢复 :通过使用 mysqlbinlog 工具来恢复数据。

binlog刷盘时机

对于 InnoDB 存储引擎而言,只有在事务提交时才会记录 biglog ,此时记录还在内存中,那么 biglog
是什么时候刷到磁盘中的呢? mysql 通过 sync_binlog 参数控制 biglog 的刷盘时机,取值范围是 0-N

  • 0:不去强制要求,由系统自行判断何时写入磁盘;
  • 1:每次 commit 的时候都要将 binlog 写入磁盘;
  • N:每N个事务,才会将 binlog 写入磁盘。

从上面可以看出, sync_binlog 最安全的是设置是 ,这也是 MySQL 5.7.7
之后版本的默认值。但是设置一个大一些的值可以提升数据库性能,因此实际情况下也可以将值适当调大,牺牲一定的一致性来获取更好的性能。

4. undo log

  • undo log是个啥?
    原子性 底层就是通过 undo log 实现的。 undo log 主要记录了数据的逻辑变化,比如一条 INSERT语句,对应一条 DELETE 的 undo log ,对于每个 UPDATE 语句,对应一条相反的 UPDATE undo log ,这样在发生错误时,就能回滚到事务之前的数据状态。同时, undo log 也是 MVCC (多版本并发控制)实现的关键。

5. redo log 重做日志

  • redo log是什么?
    redolog是一种基于磁盘的数据结构,用于在崩溃恢复期间纠正失败事务写入的数据。 在正常操作期间, redolog对由 SQL 语句或低级 API 调用产生的更改表数据的请求进行编码。 redo log 保证持久化的重要特性: ->>>>>(在初始化期间和接受连接之前,会自动重放在意外关闭之前未完成更新数据文件的修改 ) <<<<<<
    注意 : redo log是InnoDB引擎特有的
  • 为什么会有 redo log?
    因为如果每一个事务提交后都写入到磁盘的话 mysql的压力可想而知是多么的大 (不光要写业务过来的insert uodate delete 还得写日志数据) so mysql设计出redolog 用于减轻mysql io压力
  • redo log组成
    1.redolog buffer
    2.redologfile
  • redo log参数说明

image.png

redo log和undo log是如何写入磁盘的 看图说话

先写缓存 再写磁盘 也被简写为 WAL(Write-Ahead Logging) 即每一条DML都会先写入缓存 然后再由fsync()函数写入磁盘 ps:很多地方都用到了该技术 如 mysql的buffer pool 的(flush链表) nio中使用的 mmap filechannel等 我个人理解这些东西的共性就是离不开两个字 --- 刷盘

image.png

binlog和redolog区别

image.png

现有一问: insert update delete 操作 在mysql内部是怎么个执行过程???成功会怎样?失败会怎样?

  • 解: 看图 image.png

6. mysql 锁

7. mvcc多版本并发控制

东说起来东西太多了以后在做深入分析, 这里只说MVCC与事务隔离级别的关系

Read View用于支持RC(Read Committed,读提交)和RR(Repeatable Read,可重复读)隔离级别实现

RR(可重复读)、RC(读已提交)生成时机

  • RC隔离级别下,是每个快照读都会生成并获取最新Read View保证是最新提交的数据
  • 而在RR隔离级别下,则是同一个事务中第一个快照读才会创建Read View, 之后的快照读获取的都是同一个Read View,之后的查询就不会重复生成了,所以一个事务的查询结果每次都是一样的保证不重复读

解决幻读问题

  • 快照读:通过MVCC来进行控制的,不用加锁。按照MVCC中规定的“语法”进行增删改查等操作,以避免幻读。
  • 当前读:通过next-key锁(行锁+gap锁)来解决问题的。

RC、RR级别下的InnoDB快照读区别

  • 在RR级别下的某个事务的对某条记录的第一次快照读会创建一个快照及Read View, 将当前系统活跃的其他事务记录起来,此后在调用快照读的时候,还是使用的是同一个Read View,所以只要当前事务在其他事务提交更新之前使用过快照读,那么之后的快照读使用的都是同一个Read View,所以对之后的修改不可见;
  • 即RR级别下,快照读生成Read View时,Read View会记录此时所有其他活动事务的快照,这些事务的修改对于当前事务都是不可见的。而早于Read View创建的事务所做的修改均是可见
  • 而在RC级别下的,事务中,每次快照读都会新生成一个快照和Read View, 这就是我们在RC级别下的事务中可以看到别的事务提交的更新的原因

参考:
dev.mysql.com/doc/refman/…
juejin.cn/book/684473…
segmentfault.com/a/119000002…