• 微信公众号:美女很有趣。 工作之余,放松一下,关注即送10G+美女照片!

21-5-03_innodb内幕

开发技术 开发技术 1周前 (05-03) 6次浏览

6.5 锁问题

6.5.1 脏读

脏页:缓冲池中已经被修改的页,但还没有刷入到磁盘,与磁盘中的页不一样,日志已经被写入到redo日志中

脏数据:事务对缓冲池中行记录的修改,并且事务没有提交

脏读:一个事务读到了另外一个事务的脏数据,即读到了另外的事务未提交的数据,违反了事务的隔离性

例子:

21-5-03_innodb内幕

6.5.2 不可重复读

一个事务A内多次读取同一数据,但此时另外一个事务B也对此数据进行了修改,且提交了,A第二次读到的数据可能不一样。

与脏读的区别:脏读读的是未提交数据,不可重复读读的是已经提交数据

不可重复读违反了事务一致性要求

例子:

21-5-03_innodb内幕

作者把幻读归为不可重复读里面了,百度到的一半认为:

不可重复读侧重点是修改,幻读侧重点是增删(也有说是增加)

6.5.3 丢失更新

一个事务的更新操作会被另一个事务的更新操作覆盖

数据库的任何隔离级别都不会导致丢失更新

实际世界应用中需要变成串行化:

21-5-03_innodb内幕

在例子中不在update上使用for update是因为在现实中需要对数据进行验证之类的

6.6 阻塞

不同锁之间的兼容关系,在某时刻一个事务的锁需要等待另外一个事务中的释放它占有的资源,即阻塞

mysql> show variables like 'innodb_lock_wait_timeout';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| innodb_lock_wait_timeout | 50    |
+--------------------------+-------+
1 row in set, 1 warning (0.01 sec)

mysql> show variables like 'innodb_rollback_on_timeout';
+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| innodb_rollback_on_timeout | OFF   |
+----------------------------+-------+
1 row in set, 1 warning (0.00 sec)

innodb_lock_wait_timeout表示等待时间,单位秒,动态,可在运行时修改

innodb_rollback_on_timeout等待超时对事务是否进行回滚操作,静态,不可在启动时修改

默认情况innodb不会因为超时等待异常进行回滚的,超时了需要用户进行判断

6.7 死锁

两个或以上事务争夺锁资源造成的相互等待作用

无外力作用下事务都无法推进下去

wait-for graph等待图比超市机制更适合,主动检测死锁

需要保存以下信息:

  • 锁的信息链表
  • 事务等待链表

当事务指向锁,表示在等待,若锁列表中有其他元素,则事务还需等待前面的事务的锁释放

从而构建一张图,存在回路则存在死锁

21-5-03_innodb内幕

由此事务等待链表和锁链表构成的图如下:

21-5-03_innodb内幕

存在死锁将undo量最小的事务进行回滚

之前版本使用dfs,而现在使用的是非递归的算法优化性能

6.7.2 死锁的概率

死锁应该非常少发生,次数要少于等待(定义两个或两个以上)

作者数学证明看不懂……

  • 事务数量越多,发生死锁的概率越大
  • 每个事务的操作的数量越多,发生死锁的概率越大
  • 被操作的数据的数量越少,发生死锁的概率越大

6.7.3 死锁的示例

21-5-03_innodb内幕

innodb不会回滚大部分的错误异常,死锁除外

发现死锁后立即回滚一个事务

当前事务A拥有了记录 4 的X锁,而事务B对 <= 4 进行了查询,由于 4 已经被X锁锁住了,所以事务B进行了等待,同时拥有了 < 4的S锁,此时事务A在此范围插入了 3 记录,于是获不到锁,需要等待,于是形成了死锁

21-5-03_innodb内幕

6.8 锁升级

粒度降低(粒:形容多小,降低,就是升级,看起来怪怪的),一个表的1000个行锁升级成一个行锁

innobd根据页进行加锁,采用位图方式,有效降低内存消耗

第 7 章 事务

事务:区别与文件系统的重要特性之一,会把一种一致状态转换为另一种一致状态,所有的修改都保存了 or 所有的修改都不保存

innodb的事务完全符合acid

  • 原子性atomicity
  • 一致性cosistency
  • 隔离性isolation
  • 持久性durability

7.1 认识事务

7.1.1 概述

事务可由一条或者多条sql语句组成,访问并更新数据库各种数据项的一个程序执行单元

原子性:

事务是不可分割的工作单位,全部成功,或者失败后全部撤销,例如转账

一致性

事务将数据库从一种状态转化为下一中一致状态

隔离性:

有其他称呼:并发控制、可串行化、锁

当前事务提交前对其他事务都不可见

持久性:

事务一旦提交,其结果就是永久性的,发生宕机等等情况数据库也能将其恢复

数据库只保证高可靠性,即数据库相关的问题,如硬盘损坏或者其他物理伤害导致的问题,数据库无法进行保证

7.1.2 分类

按理论角度:

  • 扁平事务
  • 带有保存点的扁平事务
  • 链事务
  • 嵌套事务
  • 分布式事务

扁平事务:

最简单,实际应用中最多的,所有操作都处于同一层次

begin work开始,commit work 或 rollback work结束

三种结果:

21-5-03_innodb内幕

扁平事务主要限制不能提交或者回滚事务的某一部分,或者分几个步骤提交

带保存点的扁平事务

S1: A –> B

S2:B –> C

S3:C –> D

由于某些事,S3无法执行,无法从C –> D,此时需要在C待一晚

于是不能直接回滚,代价太大了,带保存点的扁平化事务

除了支持扁平事务支持的操作外,允许事务在执行过程中回滚到同一事务的中较早一个状态,即保存点,不会导致之前的操作都无效

扁平事务隐式第设置了一个保存点,即事务开始前的,则只能回滚到事务开始时状态

显式指明保存点:SAVE WORK 编号

保存点在事务内部是递增的

21-5-03_innodb内幕

链事务

保存点模式的变种,当系统崩溃时,带有保存点的扁平事务的保存点都消失,这些保存点不是持久的persistent,而是易失volatile,恢复时需要重新执行而不能从最近的保存点继续执行

思想:提交一个事务时,释放不需要的对象,将必要的处理上下文隐式地传给下一个要开始的事务,提交事务操作和开始下一个事务操作将合并为一个原子操作,意味着下一个事务将能看到上一个事务的结果,工作方式如下:

21-5-03_innodb内幕

链事务只能回滚到最近的一个保存点,而带有保存点的扁平事务能回滚到任意正确的保存点

链事务在执行commit后释放了当前事务持有的锁,而带有保存点的扁平事务不影响到现在所持有的锁

(我的理解是他把一个大事务才成很多小事务?)

嵌套事务

树?顶层事务控制各个层次的事务,顶层事务之下的事务是子事务,控制局部的变换,结构如图:

21-5-03_innodb内幕

  • 嵌套事务是由若干事务组成的一棵树,子树可以是嵌套事务或者扁平事务
  • 叶节点是扁平事务,每个子事务从根到叶节点的距离可以不同
  • 处于根节点的事务称为顶层事务,其他事务均为子事务,事务的前驱是父事务,事务的下一层是儿子事务
  • 子事务可以提交和回滚,但提交操作需要等到父事务提交,任何子事务都在顶层事务提交后才真正提交
  • 任何一个事务的回滚会引起其所有子事务的回滚,子事务只有ACI特性,无D持久性

实际工作交给叶子节点完成,即叶子节点的事务可以访问数据库、发送消息、获取其他类型的资源,高层事务仅负责逻辑控制,决定何时调用相关子事务

当系统不支持嵌套事务时也可以通过保存点模拟嵌套事务

21-5-03_innodb内幕

但仍有区别:

  • 保存点模拟时无法选择哪些锁被子事务继承,哪些父事务保留,被锁住的对象都可以访问
  • 嵌套查询中不同事务在数据库上持有的锁是不同的,父事务P有X、Y排它锁,子事务A,A可以拥有P的所有锁,也可以什么都没有,或者一个锁,如果子事务A拥有Z锁,甚至可以反向继承,P可以拥有Z锁,如果现在有子事务B,B可以在X、Y、Z锁中随意继承

innodb并不支持嵌套事务,只能通过保存点来模拟

分布式事务

在一个分布式环境下运行的扁平事务,根据数据所在的位置访问网络中的不同节点

7.2 事务的实现

隔离性由锁实现

undo保证事务的一致性

redo保证事务的原子性和持久性

redo恢复提交事务修改的页的操作,undo回滚记录到某个特定版本和MVVC功能,两者记录的内容不同

redo通常是物理日志,记录的是页的物理修改操作,undo是逻辑日志,根据每行记录进行记录

redo日志是顺序写的,undo日志是随机写的

7.2.1 redo

1.概念

两部分组成

  • 内存中的redo日志缓冲,易丢失
  • redo日志文件,持久

事务提交时,必须将事务的所有日志写入到redo日志之中,事务的提交才算完成

为了确保日志都写入redo日志文件,innodb都需要调用一次fsync操作,fsync效率取决于磁盘性能,于是磁盘性能决定事务提交性能,即数据库的性能

innodb_flush_log_at_trx_commit控制redo日志刷入磁盘的策略

  • 0:表示提交时不写入redo日志操作,仅在master Thread写入,即每1秒进行一次redo日志的fsync操作
  • 1:表示事务提交时必须调用一次fsync操作,默认为1
  • 2:表示事务提交时将redo日志写入redo日志文件时仅写入系统的缓存,不进行fsync操作,数据库宕机时事务并不丢失,但当操作系统宕机时,事务将会丢失在文件系统缓存中的部分

mysql中还有一种binlog日志,用来POINT-IN-TIME的恢复及主从复制环境的建立

区别:

  • redo日志在innodb产生,binlog是数据库上层产生的,binlog不仅针对innodb引擎,任何引擎对数据库的更改都会产生bnlog
  • 内容不同,redo日志是物理格式,针对每个页的修改,binlog是逻辑日志,对应sql语句
  • 吸入时间点不同,binlog在事务提交完成后写入,redo日志在事务中不断写入,并不是随事务提交的顺序写入的

2.log block

redo日志以512字节存储的,redo日志缓存、文件都是以块保存的,redo日志块

一个页产生的redo日志大于512B时需要分割为多个redo日志块存储,因为redo日志块和磁盘扇区大小一致为512字节,因此redo日志的写入可以保证原子性,不需要double技术

redo日志除了日志外还有日志块头log block header 12字节及日志块尾log block tailer 4字节,每个日志块实际存储大小为496字节,结构如下:

21-5-03_innodb内幕

log block header如下:

21-5-03_innodb内幕

log_block_hdr_no标记在log buffer数组中的位置,递增循环使用,4字节,第一位判断是否flush bit,所以最大为2G

log_block_hdr_data_len表示log block大小,当被写满时,该值为0x200,表示使用全部空间512字节

log_block_first_res_group 2字节表示第一个日志(不指上一个日志组在当前日志块的剩余量)所在的偏移量,如果和log_block_hdr_data_len相同表示log block不包含新日志,因为日志头12字节

由于日志块实际存储大小为496字节,所以当某个事务产生的redo日志大于496字节时则需要重新申请第二块日志块

例如事务A762字节,事务B100字节,则log buffer如图:

21-5-03_innodb内幕

(图中举例子,数值不对……)

log_block_checkpoint_no:4字节表示该块被写入时检查点第 4 字节的值

log block tailer只由 lob_block_trl 4字节和log_block_hdr_no一样

3.log group

redo日志组,多个redo日志组成,存储的是log block,512字节,根据以下规则刷新磁盘:

  • 事务提交
  • log buffer 一半内存空间被使用
  • log checkpoint时

round-robin:一个blokc追加写入redo日志文件末尾,当被写满时会接着写一个redo日志文件

log group中第一个redo日志文件,前 2KB保存了 4 个 512字节的块:

21-5-03_innodb内幕

上述信息仅在第一个redo log file文件存储,其他log group保留此空间,但不保存上述信息

因为要更新这些信息,所以对redo log file的写入并不是完全顺序的

21-5-03_innodb内幕

在redo file的log file header里面存储了checkpoint值,交替写入

4、redo日志格式

redo日志通用日志格式:

21-5-03_innodb内幕

  • redo_log_type:类型
  • space:表空间id
  • page_no:页的偏移量

插入和删除的redo日志格式:

21-5-03_innodb内幕

5.LSN

log sequence number缩写,日志序列号,8字节单调递增

表示含义有:

  • redo日志写入总量

当事务写了多少字节redo日志,lsn就增长了多少,单位字节

  • checkpoint的位置

在每个数据页和redo页的头部有一个 fil_page_lsn,记录了钙业的lsn,表示该页最后刷新时lsn的大小,redo日志记录每个页的日志,页中的lsn可以来判断页是否需要进行恢复操作

例如页1的lsn是10 000,数据库启动时redo日志的lsn为13 000,并且该事务已提交,所以数据库需要恢复操作,redo日志的lsn13 000 d大于页1中的lsn,说明该页已被刷新,所以不需要重做

  • 页的版本
show engine innodb statusG

查看lsn

  • Log sequence number表示当前lsn
  • Log flushed up to表示刷新到redo日志的lsn
  • Last checkpoint at表示刷新到磁盘的lsn

生产环境下三个值可能是不同的


程序员灯塔
转载请注明原文链接:21-5-03_innodb内幕
喜欢 (0)