mysql 事务、binlog、redo log、undo log、mvcc与锁

事务

事务整体 https://zhuanlan.zhihu.com/p/163838108

ACID

  • 原子性:Atomicity,整个数据库事务是不可分割的工作单位
  • 一致性:Consistency,事务将数据库从一种状态转变为下一种一致的状态
  • 隔离性:Isolation,每个读写事务的对象对其他事务的操作对象能相互分离
  • 持久性:Durability,事务一旦提交,其结果是永久性的

怎么实现

  • 原子性
    • undo Log
  • 一致性
    • 是其他三个特性保证的
  • 隔离性
    • MVCC
  • 持久性
    • redo Log

隔离级别

  • SQL标准通过异常现象定义隔离级别
  • 异常现象
    • 脏写:一个事务修改了另一个事务未提交的数据,所有隔离级别均禁止。加锁控制写写不并发
    • 脏读:一个事务读取了另一个事务未提交的数据
    • 不可重复读:一个事务读取了另一个事务已提交对本记录的修改
    • 幻读:按条件读取的数据多了或者少了,即读取到另外事务的新增或删除
  • 隔离级别
    在这里插入图片描述
  • 特别注意:mysql的innodb引擎RR隔离级别可以防止幻读
    • 当前读,select,mvcc的read view
    • 快照读,select for update,update等,加next key lock
    • 但是两种模式不能混用,否则还会出现幻读
      • 如第一次读取使用select,中间update,然后select
      • 如第一次读取使用select,然后select for update
  • 其他异常现象
    • 更新丢失:https://blog.csdn.net/paopaopotter/article/details/79259686
      • 第一种更新丢失,即回滚丢失,所有级别均可防止
        • 写写不能并发,都是从最新的数据版本写入的,回滚就回滚到上个版本即可
          在这里插入图片描述
      • 第二种更新丢失,即覆盖丢失,
        • 本质上是先读后写,不具有原子性。因为不加锁的读不能禁止其他事务的写
        • 需要加锁
        • 悲观锁,for update,等于可串行化的隔离级别
        • 乐观锁
          • 业务字段
          • 版本字段
            在这里插入图片描述

binlog

  • 格式
    • statement 记录的是执行语句 主从复制时可能会出现问题
    • row 记录要修改的数据 缺点就是 日志文件比较大, 优点就是 数据恢复
    • mixed mysql 会根据执行的每一条具体的 SQL 语句来区分对待记录的日志形式,也就是在 statement 和 row 之间选择一种
  • 刷盘
    • 事务执行过程中,先把日志写到 binlog cache,事务提交的时候,再把 binlog cache 写到 binlog 文件中。
    • 一个事务的 binlog 是不能被拆开的,因此不论这个事务多大,也要确保一次性写入。
    • binlog刷盘的时机是由参数 sync_binlog指定的

Redo Log

  • 作用:持久化-崩溃恢复
  • 两个文件循环写
    • 一旦redo所记录的操作更新到数据文件中,就没有用了,所以两个文件循环写
    • 而binLog不同,binLog是归档,还用于主从同步和其他的操作
  • 记录InnoDB中对Buffer Pool中Page修改的日志,即物理日志,区别于binLog的具体操作日志,即逻辑日志
  • 日志写入硬盘时无需doublewrite
    • 重做日志(内存和硬盘文件)按512字节存储,成为redo block
    • 如果大于 512字节会拆分成多个块存储
    • 写入硬盘时按照redo block写入,即512字节,是硬盘层面最小的写入单位,保证写入必定是原子的
  • innodb_flush_log_at_trx_commit:控制redo日志的刷盘时机
    • 1:默认,事务提交必须调用一次fsync操作
    • 0:事务提交时不进行写入重做日志操作,由master thread每1秒进行一次fsync操作
    • 2:事务提交时仅将重做日志写入文件系统缓存,不进行fsync操作,当mysql宕机而操作系统不宕机时,不会导致事务丢失。
  • redo log的两阶段提交
    • 写redo log,处于prepare阶段
    • 写binlog刷盘
    • 写redo log,处于commit阶段,这个commit不等同于事务commit
  • redo log的使用见崩溃恢复
    在这里插入图片描述

undo log

  • 目的:保证事务的原子性,发生错误时回滚,MVCC
  • 存在表空间的undo段中,实际上是page,需要doublewrite

undo Log删除

  • insert事务提交即可删除undo日志
  • update(包括delete)因为需要支持mvvc,因此需要在mvcc不需要时候才能删除
    • 事务提交时,生成一个事务no,写入到undoLog中,代表着事务提交顺序
    • history链表是按照事务提交顺序排序,因此也是按照事务no排序
    • readview中包括事务no,值为当前最大的事务no+1
    • readview按照创建时间排序,因此也是按照事务no排序
    • 回滚段取 history链表遍历,如果小于readview链表中最小的事务no,则表示没有用了,删除undo日志,如果是delete,还需要删除记录

undo表空间与undo段的关系

  • 一共有128个回滚段
  • 默认
    • 0和33~127在系统表空间
    • 1~32是临时表空间的回滚段
  • 33~127可以指定到undo表空间,只能是初始化时指定,不能修改

崩溃恢复

  • 概括
    • 通过redo log恢复所有操作
    • 通过undo log回滚所有未提交的操作
  • https://www.cnblogs.com/xinysu/p/6586386.html
  • redo log
    • 寻找checkpoint,从系统表空间找完整的页,读取LSN
    • redo Log,找到LSN,恢复之后的redo到page
  • undo log及binlog
    • 找到最后一个binlog, 获取所有可能没有提交事务的xid列表;
    • 根据undo log中的 insert_undo_list,upddate_undo_list事务链,构建undo_list,在根据undo_list构建未提交事务链表;
    • 从未提交事务链表中,提取出xid,凡是存在于xid列表中的,则需要提交,不存在的,则回滚。
      在这里插入图片描述

MVCC

  • MVCC:多版本并发控制
  • 目的
    • 读写无锁并发:读不用阻塞写,写不用阻塞读
    • 实现RC和RR的事务隔离级别
      • RC解决脏读
      • RR解决不可重复读和幻读
    • 但不能解决(第二种,即两次提交)更新丢失的问题
  • 两种模式
    • 当前读:读最新的记录
      • 写操作:insert,delete,update
      • 读时显式加锁:select… for update 或 lock in share mode
    • 快照读:读取的是记录的快照版本(有可能是历史版本),不用加锁
      • select
  • 简述:每次修改就保存一个版本,读取时取符合可见性的版本
  • 原理:
    • 行记录有3个隐藏字段,事务Id(它是单调递增),回滚指针,隐藏主键
      • 事务Id是不是事务开始时分配,而是第一次进行修改操作时生成
    • undo Log:
      • 行记录中放的是最新的记录
      • undo Log放的是历史版本的记录
      • 通过回滚指针连接为一个链表,链头是行记录,链尾是最旧的记录
    • read view:读视图,做事务可见性判断。
      • 在执行快照读的一刻,生成视图。
      • read view 4个属性
        • 本事务ID:没有写操作时,默认为0
        • 活跃的事务ID列表
        • 最小活跃事务ID
        • 下一事务ID
    • 可见性规则
      • 遍历版本链表,找到第一个符合可见性的版本。如果都不可见,则不用返回本条记录
      • 怎么判断一条记录符合可见性
        • 行记录的事务ID=本事务ID,说明是本事务写入的记录,可以看到
        • 行记录的事务ID<活跃事务的最小ID,说明行记录的事务已被提交,可以被看到
        • 行记录的事务ID>=下一个事务的ID,说明当前行记录是在read view之后生成的,不应该被看到
        • 行记录的事务ID
          • 在活跃事务中,表示在read view时,没有提交
          • 不在活跃事务中,表示已提交,可以看到
    • RC和RR的可见性规则是相同的,不同是生成快照的时机不同
      • RC每次快照读都生成,因此可以读到别的事务已提交的记录
      • RR第一次快照读才生成,因此读不到之后其他事务已提交的记录

  • 机制:通过加锁,使得读写,写写操作顺序进行,控制并发
  • 一般锁是在事务提交时释放
  • 粒度
  • 锁的类型
    • 意向读锁:表级读锁
    • 意向写锁:表级写锁
    • 读锁:行级读锁,读读不互斥
    • 写锁:行级写锁
  • innodb行锁的细分
    • record lock:记录锁,加在索引上,RC、RR隔离级别都支持
    • gap lock:间隙锁,锁定索引记录间隙,RR隔离级别支持
    • next key lock:记录锁和间隙锁组合,锁住一个前开后闭区间,RR隔离级别支持
  • RC级别仅需记录锁,因此无法防止幻读
  • RR级别通过:gap lock和next key lock锁定范围,防止了幻读
    • 当前读加锁
    • 锁降级:都是先采用Next-Key Lock,包含唯一索引降级为RecordLock
    • 不同类型的索引加锁
      • 主键加锁
      • 唯一索引加锁:先加唯一索引,在通过唯一索引加主键锁
      • 非唯一索引加锁:
        • 先非唯一索引加next key lock,等效于record lock和gap lock
        • 在通过非唯一索引的主键记录加主键的record lock
      • 无索引:
        • 主键全部记录和间隙加锁,等效于全表加锁
  • 排查死锁
    • 查看日志 innodb status
    • 日志中有:事务,sql语句,加锁的类型(X,S规则),谁被回滚了。画个图就一目了然了

innodb的MVCC,锁及隔离级别总结

  • MVCC工作在RC和RR级别
    • RC级别下需要解决脏读问题,换句话说需要读到别的事务已提交的记录,即为不可重复读
    • RR级别下需要解决脏读,不可重复读,和幻读的问题
  • 串行化,通过加锁,等同于 select lock in share mode。读读并发,其他均不可以。
  • 读未提交,直接读最新版本
  • MVCC机制
    • 快照读,读的是快照,普通select
      • RC与RR生成read view时机不同
        • RC每次快照读都生成read view,因此可以读到别的事务已提交的记录
        • RR只要第一快照读的时候才生成read view,因此读不到其他事务已提交的记录
    • 当前读,读的是最新记录,写操做insert,update,delete,读操作显式加锁for update,in share mode
      • RC只加记录锁,因此无法防止幻读
      • RR加间隙锁,举例
        * 防止使用非唯一索引update其他的语句,如
        * t_user.name有非唯一索引
        * update t_user set name=‘zhangsan’ where name=‘张三’
        * 将非唯一索引值为‘张三’的间隙加锁,防止其他事务同时写入张三记录,但是可以写其他的记录,也就是写写互斥
        * 无索引全表锁定,其他事务不能写入
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值