八股文—MYSQL

数据库介绍

数据库架构概览

在这里插入图片描述

事务

原子性:事务中操作要么全部提交成功,要么全部失败回滚
一致性:事务由于系统故障即使最终未提交,其中所作的修改也不会保存到数据库中
隔离性:事务所作修改在最终提交以前,对其他事务是不可见的
持久性:事务一旦提交,所作的修改就会永久保存到数据库

事务的隔离级别

READ UNCOMMITTED(读未提交):可以读到修改但未提交的数据,脏读
READ COMMITTED(读已提交):也叫不可重复读,事务中两次同样的查询获得到不同的结果,因为其他事务提交而影响当前事务的查询结果
REPEATABLE READ(可重复读 MYSQL默认隔离级别):保证同一个事务中多次读取同样的记录结果一致,但是还是无法解决幻读问题,事务读取某个范围记录时,别的事务在该范围插入了新纪录,当前事务再次读取该范围时,结果会多一行(幻行)。
InnoDB和XtrDB存储引擎通过多版本并发控制(MVCC)解决了幻读问题
SERIALIZABLE(串行化):锁表,效率极低

锁介绍

  1. 基于锁的属性分类:共享锁、排他锁。
    共享锁(S lock),允许事务读取一行数据。
    排它锁(X lock),允许事务删除或者更新一行数据。
    当一个事务获取了行r的共享锁,那么另外一个事务也可以立即获取行r的共享锁,因为读取并未改变行r的数据,这种情况就是锁兼容。
    但是如果有事务想获得行r的排它锁,则它必须等待事务释放行r上的共享锁。

  2. 基于锁的粒度分类:行级锁(INNODB)、表级锁(INNODB、MYISAM)、页级锁(BDB引擎 )
    行锁三种算法:

    • record lock:单个行记录的锁

记录锁其实很好理解,对表中的记录加锁,叫做记录锁,简称行锁。比如
SELECT * FROM test WHERE id=1 FOR UPDATE;
它会在 id=1 的记录上加上记录锁,以阻止其他事务插入,更新,删除 id=1 这一行。
需要注意的是:
id 列必须为唯一索引列或主键列,否则上述语句加的锁就会变成临键锁。
同时查询语句必须为精准匹配(=),不能为 >、<、like 等,否则也会退化成临键锁。
在通过 主键索引 与 唯一索引 对数据行进行 UPDATE 操作时,也会对该行数据加记录锁:
– id 列为主键列或唯一索引列
UPDATE SET age = 50 WHERE id = 1;
记录锁是锁住记录,锁住索引记录,而不是真正的数据记录。
如果要锁的列没有索引,进行全表记录加锁。
记录锁也是排它 (X) 锁,所以会阻塞其他事务对其插入、更新、删除。

* gap lock:间隙锁,锁定一个范围,但不包含记录本身,遵循左开右闭原则。阻止多个事务将纪录插入到同一个范围内,保证某个间隙内的数据在锁定情况下不会发生任何变化。可以解决Phantom Problem(幻读)

间隙锁 是 InnoDB 在 RR(可重复读)隔离级别下为了解决 幻读问题 时引入的锁机制。间隙锁是InnoDB 中行锁的一种。
请务必牢记:使用间隙锁锁住的是一个区间,而不仅仅是这个区间中的每一条数据。
举例来说,假如 emp 表中只有 101 条记录,其 empid 的值分别是1, 2, …, 100, 101,下面的SQL:
SELECT * FROM emp WHERE empid > 100 FOR UPDATE
当我们用条件检索数据,并请求共享或排他锁时,InnoDB 不仅会对符合条件的 empid 值为 101 的记录加锁,也会对 empid 大于 101(这些记录并不存在)的 “间隙” 加锁。
这个时候如果你插入 empid 等于 102 的数据的,如果那边事物还没有提交,那你就会处于等待状态,无法插入数据

用户可以通过以下两种方式来显式地关闭Gap Lock:
a) 将事务的隔离级别设置为 READ COMMITTED
b) 将参数 innodb_locks_unsafe_for_binlog设置为1
* next_key lock:Gap Lock+ Record Lock,锁定一个范围,并且锁定记录本身。InnoDB默认加锁方式是Next_key Lock

InnoDB行锁是通过给索引上的索引项加锁来实现的,只有通过索引条件检索数据,InnoDB才使用行级锁或者间隙锁,否则,InnoDB将使用表锁。

  1. 基于锁的状态分类:意向共享锁、意向排它锁
    上锁的对象看成一棵树,那么对最下层的对象上锁,也就是对最细粒度的对象进行上锁,那么首先需要对粗粒度的对象上锁。
    如下图,如果需要对页上的记录r进行上X锁,那么分别需要对数据库A、表、页上意向锁IX,最后对记录r上ⅹ锁。
    若其中任何一个部分导致等待,那么该操作需要等待粗粒度锁的完成。
    举例来说,在对记录r加ⅹ锁之前,已经有事务对表1进行了S表锁,那么表1上已存在S锁,之后事务需要对记录r在表1上加上IX,由于不兼容,所以该事务需要等待表锁操作的完成。
    在这里插入图片描述

死锁和应对方法

如下并行执行的两个事务便会发生死锁,使用mysql命令可以查询

事务 a
表 t  id=100 更新  加行锁
表 t  id=200 更新  已加锁

事务 b
表 t  id=200 更新 加行锁
表 t  id=100 更新 已加锁

死锁无法避免,上线前要进行严格的压力测试

  • 快速失败 innodb_lock_wait_timeout 行锁超时时间
  • 拆分sql,严禁大事务
  • 充分利用索引,优化索引,尽量把有风险的事务sql使用上覆盖索,优化where条件前缀匹配,提升查询速度,引减少表锁
  • 操作多张表时,尽量以相同的顺序来访问避免形成等待环路
  • 单张表时先排序再操作
  • 使用排它锁 比如 for update

MVCC

原理

前提:
每个事务都有一个唯一的事务ID,用于标识该事务的开始和结束。事务ID通常是递增的,并且是全局唯一的。

MVCC三个要素:

  1. 三个数据行的隐式字段:

    • trx_id:创建该数据的事务id
    • db_roll_ptr:数据上一个版本的地址
    • db_row_id:数据的唯一id
  2. undo log:每个数据行都有一个版本链,包含了该数据行的所有历史版本。每次更新操作(如INSERT、UPDATE、DELETE)都会生成一个新的数据版本,链首就是最新的记录,链尾就是最早的旧记录

  3. read view:当一个事务开始时,它会创建一个读视图(Read View)。读视图包括以下信息:
    min_trx_id:Read View 创建时的最小事务ID,表示该视图能够看到的最早版本。
    max_trx_id:Read View 创建时的最大事务ID,表示该视图能够看到的最新版本。
    m_ids:当前活跃的事务ID集合,表示哪些事务还没有提交。

当一个事务执行 SELECT 操作时,它会使用自己的读视图来确定应该看到哪个数据版本。具体规则如下:
如果数据版本的事务ID小于 min_trx_id,表示该版本已经提交,可以读取。
如果数据版本的事务ID大于 max_trx_id,表示该版本在事务开始后创建,不可见。
如果数据版本的事务ID在 min_trx_id 和 max_trx_id 之间,但在 m_ids 集合中,表示该版本由尚未提交的事务创建,不可见。
如果数据版本的事务ID在 min_trx_id 和 max_trx_id 之间,并且不在 m_ids 集合中,表示该版本由已提交的事务创建,可见。
从版本链的最新记录往下找,找到的第一条符合上面可见条件之一的就是最终需要的数据
在这里插入图片描述

RR和RC的细微区别

RR(REPEATABLE READ)事务创建时生成的read view,可以重复读取数据且结果不会受到中间提交事务的改变而改变
RC(READ COMMIT)事务中每一条select语句前生成read view,那么可以读到已经提交的事务的信息

回滚操作

因此,当 InnoDB存储引擎回滚时,它实际上做的是与先前相反的工作:
对于每个INSERT,InnoDB存储引擎会完成一个DELETE
对于每个DELETE,InnoDB存储引擎会执行一个INSERT
每个UPDATE,InnoDB存储引擎会执行一个相反的UPDATE
并且这些操作会追加到innoDB的版本链当中去

索引

mysql索引分三类:B+树、Hash索引、全文索引

MyISAM 和 InnoDB 索引的区别(聚簇和非聚簇)

主键索引:Primary Key,是由一个或多个列组成的唯一性标识,用于唯一地标识数据表中的每条记录
辅助索引:定义在主表的任意一个或多个非排序字段上,用于快速查询特定属性的记录

MyISAM索引

MyISAM引擎使用B+树作为索引结果,叶节点的data域存放的是数据记录的地址。下图为MyISAM表的主索引,Col1为主键。
主索引和辅助索引在结构上没有任何区别,只是主索引要求key是唯一的,而辅助索引的key可以重复
索引查询过程:MyISAM中索引检索的算法为首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,则取出其data域的值,然后以data域的值为地址,读取相应数据记录。
在这里插入图片描述

InnoDB索引实现
主键索引

同样是B+树,实现方式却完全不同。InnoDB表数据文件本身就是一个索引结构,树的叶节点data域保存了完整的数据记录,这种索引叫做聚集索引

在这里插入图片描述

辅助索引

InnoDB的所有辅助索引都引用主键作为data域。下图为定义在Col3上的一个辅助索引
聚集索引这种实现方式使得按主键的搜索十分高效,但是辅助索引搜索需要检索两遍索引:首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录
在这里插入图片描述

区别

1、innodb支持事务,myisam不支持;
2、innodb支持外键,而myisam不支持;
3、innodb是聚集索引(InnoDB的数据文件本身就是主索引文件。而MyISAM的主索引和数据是分开的),而myisam是非聚集索引;
4、innodb支持表、行级锁,而myisam支持表级锁;
5、innodb表必须有主键或唯一索引,而myisam可以没有;(InnoDB如果没有设定主键或非空唯一索引,就会自动生成一个6字节的主键)

B+树

B+树中页中对象的结构:
record_type(图中页内第一行的数据):0普通记录、2最小记录、3最大记录、1是B+树非叶子节点记录
next_record(图中页内第二行的数据):下一条记录相对位置
主键(图中页内第三行的数据):页中排序第一位的主键值
数据:record_type=0时,为用户数据,record_type=1时,为页数
非叶子节点存储 主键+页数,叶子节点存储了完整的用户记录
在这里插入图片描述

优点:
数据访问更快,索引和数据保存在同一个B+树中
对于主键的范围查询和排序查找速度非常快
聚簇索引排序顺序,查询显示一定范围数据时,数据紧密相连,节省大量的IO操作
缺陷:
插入速度严重依赖插入顺序,按照主键顺序插入是最快的,否则将会出现页分裂,严重影响性能,对于innodb表,都会定义一个自增ID作为主键
更新主键代价高,对于innodb,一般定义主键不可更新

EXPLAIN关键字

驱动表和被驱动表

EXPLAIN SELECT * FROM t1 INNER JOIN t2;
内连接时,MySQL性能优化器会自动判断哪个表是驱动表,哪个表示被驱动表,和书写的顺序无关
在这里插入图片描述

id:执行顺序
  • id号每个号码,表示一趟独立的查询, 一个sql的查询趟数越少越好
  • id为NULL的:最后执行
  • 在所有组中,id值越大,优先级越高,越先执行
EXPLAIN SELECT * FROM t1, t2, t3;

在这里插入图片描述
如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行

EXPLAIN SELECT t1.id FROM t1 WHERE t1.id =(
  SELECT t2.id FROM t2 WHERE t2.id =(
    SELECT t3.id FROM t3 WHERE t3.content = 't3_434'
  )
);

在这里插入图片描述

select_type

查询的类型,主要是用于区别普通查询、联合查询、子查询等的复杂查询。
SIMPLE:简单查询。查询中不包含子查询或者UNION。
PRIMARY:主查询。查询中若包含子查询,则最外层查询被标记为PRIMARY。
SUBQUERY:子查询。在SELECT或WHERE列表中包含了子查询。
DEPENDENT SUBQUREY:如果包含了子查询,并且查询语句不能被优化器转换为连接查询,并且子查询是相关子查询(子查询基于外部数据列),则子查询就是DEPENDENT SUBQUREY
UNCACHEABLE SUBQUREY:表示这个subquery的查询要受到外部系统变量的影响
UNION:对于包含UNION或者UNION ALL的查询语句,除了最左边的查询是PRIMARY,其余的查询都是UNION。
UNION RESULT:UNION会对查询结果进行查询去重,MYSQL会使用临时表来完成UNION查询的去重工作,针对这个临时表的查询就是"UNION RESULT"
**DEPENDENT UNION:**子查询中的UNION或者UNION ALL,除了最左边的查询是DEPENDENT SUBQUREY,其余的查询都是DEPENDENT UNION
**DERIVED:**在包含派生表(子查询在from子句中)的查询中,MySQL会递归执行这些子查询,把结果放在临时表里。
MATERIALIZED:**优化器对于包含子查询的语句,如果选择将子查询物化后再与外层查询连接查询,该子查询的类型就是MATERIALIZED。如下的例子中,查询优化器先将子查询转换成物化表,然后将t1和物化表进行连接查询。

type

结果值从最好到最坏依次是:
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
比较重要的包含:system、const 、eq_ref 、ref、range > index > ALL
ALL:全表扫描。Full Table Scan,将遍历全表以找到匹配的行
index:当使用覆盖索引,但需要扫描全部的索引记录时
range:只检索给定范围的行,使用一个索引来选择行。key 列显示使用了哪个索引,一般就是在你的where语句中出现了between、<、>、in等的查询。这种范围扫描索引扫描比全表扫描要好,因为它只需要开始于索引的某一点,而结束于另一点,不用扫描全部索引。
ref:通过普通二级索引列与常量进行等值匹配时
eq_ref:连接查询时通过主键或不允许NULL值的唯一二级索引列进行等值匹配时
const:根据主键或者唯一二级索引列与常数进行匹配时
system:MyISAM引擎中,当表中只有一条记录时。(这是所有type的值中性能最高的场景)

key_len

表示索引使用的字节数,根据这个值可以判断索引的使用情况,检查是否充分利用了索引,针对联合索引值越大越好。

-- 创建索引
CREATE INDEX idx_age_name ON t_emp(age, `name`);
-- 测试1
EXPLAIN SELECT * FROM t_emp WHERE age = 30 AND `name` = 'ab%';
-- 测试2
EXPLAIN SELECT * FROM t_emp WHERE age = 30;

在这里插入图片描述

rows

MySQL认为它执行查询时必须检查的行数。值越小越好。

filtered

最后查询出来的数据占所有服务器端检查行数(rows)的百分比。值越大越好。

Spring事务传播属性

事务注解,及其默认的传播属性
@Transactional(propagation=Propagation.REQUIRED)

Spring支持的几种事务传播属性及其含义:

  • Propagation.REQUIRED:如果当前没有事务,则创建一个新的事务;如果已有事务,则将请求的事务合并到现有事务中。这是最常见的配置选项。
  • Propagation.NESTED:如果在当前存在事务的情况下执行方法,那么就作为嵌套事务的一部分来执行。如果没有事务,则会尝试以事务的方式执行(如果可能)。
  • Propagation.REQUIRES_NEW:每次都会新建一个事务,不管是否有事务处于活动状态。如果已有事务,它会将其挂起以便新的事务能够开始。
  • Propagation.SUPPORTS:仅支持当前事务,如果有事务,则以非事务方式执行;否则,会以非事务方式执行。
  • Propagation.MANDATORY:强调节奏,必须使用当前事务,如果当前没有事务,则抛出异常。
  • Propagation.NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,则将其挂起。
  • Propagation.NEVER:永远不使用事务,如果当前存在事务,则抛出异常。

分布式事务

CAP

CAP原理是分布式系统中的核心概念,它由三个基本要素组成:一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)。CAP原理指出,在一个分布式系统中,无法同时满足这三个要求,只能在它们之间进行权衡取舍。

  • C: 一致性(Consistency)
    是指在系统中的所有副本在任何时刻都具有相同的数据,即当系统中一个节点进行数据更新的同时,其他节点能够读取到更新到的最新数据;
  • A:可用性(Availability)
    是指系统在有限的时间内对请求做出响应,并返回相应的结果, 即系统在任何时候都处于可用状态,用户能随时随地的使用;
  • P: 分区容错性(Partition tolerance)
    指系统在遇到网络分区或部分节点失效的情况下,仍然能够继续运行并保持数据的一致性和可用性。

值得注意的是:当发生网络分区时,为了保持分区前的一致性,系统可能需要暂停对外服务,牺牲可用性;或者为了保持可用性,系统可能需要在分区问题解决后进行数据同步,牺牲一致性。所以系统设计者只能在一致性、可用性和分区容错性中选择其中两个进行保证,无法同时满足所有三个要求,但实际上可以通过一些策略和技术手段来实现某种程度的权衡和折中。这需要根据具体的系统需求和业务场景,综合考虑数据的重要性、用户的需求以及系统的可靠性。

XA

XA协议。XA是一个协议,由Oracle Tuxedo系统提出的XA分布式事务协议。

XA协议定义了分布式事务参与方的两个角色:
TM:事务协调者Transaction Manager
RM:资源管理器/事务参与者Resource Manager

2PC

XA协议实现之一
二阶段提交协议(Two-phase Commit,即2PC)是常用的分布式事务解决方案,它可以保证在分布式事务中,要么所有参与进程都提交事务,要么都取消事务,即实现 ACID 的原子性(A)。在数据一致性中,它的含义是:要么所有副本(备份数据)同时修改某个数值,要么都不更改,以此来保证数据的强一致性。
2PC 要解决的问题可以简单总结为:在分布式系统中,每个节点虽然可以知道自己的操作是成功还是失败,却是无法知道其他节点的操作状态。当一个事务需要跨越多个节点时,为了保持事务的 ACID 特性,需要引入一个作为协调者的组件来统一掌控所有节点(参与者)的操作结果并最终指示这些节点是否要把操作结果进行真正的提交(比如将更新后的数据写入磁盘等等)。因此,二阶段提交的算法思路可以概括为: 参与者将操作结果通知协调者,再由协调者根据所有参与者的反馈情报决定各参与者是否要提交操作还是中止操作。

  1. 准备阶段有如下三个步骤:
    协调者向所有参与者发送事务内容,询问是否可以提交事务,并等待所有参与者答复。
    各参与者执行事务操作,将 undo 和 redo 信息记入事务日志中(但不提交事务)。
    如参与者执行成功,给协调者反馈 yes,即可以提交;如执行失败,给协调者反馈 no,即不可提交。
  2. 提交阶段
    协调者基于各个事务参与者的准备状态,来决策是事务提交Commit()或事务回滚Rollback()。如果协调者收到了参与者的失败消息或者超时,直接给每个参与者发送回滚(rollback)消息;否则,发送提交(commit)消息。
    参与者根据协调者的指令执行提交或者回滚操作,释放所有事务处理过程中使用的锁资源。(注意:必须在最后阶段释放锁资源)
3PC

了解即可。三阶段提交又称3PC,其在两阶段提交的基础上增加了CanCommit阶段,并引入了超时机制。一旦事务参与者迟迟没有收到协调者的Commit请求,就会自动进行本地commit,这样相对有效地解决了协调者单点故障的问题。
相比较2PC而言,3PC对于协调者(Coordinator)和参与者(Partcipant)都设置了超时时间,而2PC只有协调者才拥有超时机制。
3PC相对于2PC的一个提高(相对缓解了2PC中的前两个问题),但是3PC依然没有完全解决数据不一致的问题。由于3PC 的设计过于复杂,在解决2PC 问题的同时也引入了新的问题,所以在实际上应用不是很广泛

TCC

Try 操作作为一阶段,负责资源的检查和预留。
Confirm 操作作为二阶段提交操作,执行真正的业务。
Cancel 是预留资源的取消
TCC事务的处理流程与2PC两阶段提交类似,不过2PC通常都是在跨库的DB层面,而TCC本质上就是一个应用层面的2PC,需要通过业务逻辑来实现。

设计要点

  1. 空回滚
    如果协调者的Try()请求因为网络超时失败,那么协调者在阶段二时会发送Cancel()请求,而这时这个事务参与者实际上之前并没有执行Try()操作而直接收到了Cancel()请求。
    针对这个问题,TCC模式要求在这种情况下Cancel()能直接返回成功,也就是要允许「空回滚」。
  2. 防悬挂
    接着上面的问题1,Try()请求超时,事务参与者收到Cancel()请求而执行了空回滚,但就在这之后网络恢复正常,事务参与者又收到了这个Try()请求,所以Try()和Cancel()发生了悬挂,也就是先执行了Cancel()后又执行了Try()
    针对这个问题,TCC模式要求在这种情况下,事务参与者要记录下Cancel()的事务ID,当发现Try()的事务ID已经被回滚,则直接忽略掉该请求。
  3. 幂等性
    Confirm()和Cancel()的实现必须是幂等的。当这两个操作执行失败时协调者都会发起重试。

Seata的实现

XA

XA模式优点:
1.事务的强一致性,只要有失败的,TC事务协调者就会发送信息让RM回滚——>满足ACID原则
2.没有代码侵入(*),常用数据库都支持
缺点:
1.第一阶段就要锁定数据库资源,但是却不提交,从而导致数据库所占用的资源不能释放(占数据库锁),性能较差
2.依赖关系型数据库实现事务

AT

一种最终一致的模式:因为RM资源管理器执行sql后会直接提交,那么此时如果是数据不一致的情况下,那么说明肯定是软一致,但是在阶段二时,AT模式RM资源管理器会利用快照进行数据回滚,从而保证最终一致;
原理就是记录数据更新前后的undo-log快照,回滚时根据快照反向执行更新操作。
AT模式的脏写问题(对同数据并发写的问题):简而言之,就是两个事务并发执行,修改同一条数据,我第一个事务修改并且提交之后,释放DB锁资源,第二个事务想要进行回滚,那么就会导致脏写——>前一个事务修改无效
优点:
一阶段完成提交事务,释放数据库资源,性能好
利用全局锁实现读写隔离
没有代码侵入,框架自动完成回滚和提交
缺点:
最终一致性并非强一致性
框架快照功能可能影响性能,比XA要好很多

全局锁
一阶段本地事务提交前,需要确保先拿到 全局锁 。拿不到全局锁 ,不能提交本地事务。
拿全局锁的尝试被限制在一定范围内,超出范围将放弃,并回滚本地事务,释放本地锁。
两个全局事务 tx1 和 tx2,分别对 a 表的 m 字段进行更新操作,m 的初始值 1000。
tx1 先开始,开启本地事务,拿到本地锁,更新操作 m = 1000 - 100 = 900。本地事务提交前,先拿到该记录的全局锁,本地提交释放本地锁。 tx2 后开始,开启本地事务,拿到本地锁,更新操作 m = 900 - 100 = 800。本地事务提交前,尝试拿该记录的全局锁,tx1 全局提交前,该记录的全局锁被 tx1 持有,tx2 需要重试等待全局锁。
在这里插入图片描述

TCC

TCC与AT模式非常相似,使用人工编码来实现数据恢复 上文已经有介绍

Saga

该模式是SEATA提供的长事务解决方案,需要编写状态机和补偿业务,与TCC有些类似

区别

在这里插入图片描述

日志

error log

error log主要记录MySQL在启动、关闭或者运行过程中的错误信息,在MySQL的配置文件my.cnf中,可以通过log-error=/var/log/mysqld.log 执行mysql错误日志的位置。

slow query log

慢查询日志:MySQL的慢查询日志是MySQL提供的一种日志记录,它用来记录在MySQL中响应时间超过阀值的语句,具体指运行时间超过long_query_time值的SQL,则会被记录到慢查询日志中。

general log

general log 记录了客户端连接信息以及执行的SQL语句信息,通过MySQL的命令

redo log

redo log是一种基于磁盘的数据结构,用来在MySQL宕机情况下将不完整的事务执行数据纠正,redo日志记录事务执行后的状态。
当事务开始后,redo log就开始产生,并且随着事务的执行不断写入redo log file中。redo log file中记录了xxx页做了xx修改的信息,我们都知道数据库的更新操作会在内存中先执行,最后刷入磁盘。
redo log就是为了恢复更新了内存但是由于宕机等原因没有刷入磁盘中的那部分数据。

redo log 刷盘流程

MySQL 中数据是以页为单位,你查询一条记录,会从硬盘把一页的数据加载出来,加载出来的数据叫数据页,会放入到 Buffer Pool 中。
BufferPool Processon后续的查询都是先从 Buffer Pool 中找,没有命中再去硬盘加载,减少硬盘 IO 开销,提升性能。
更新表数据的时候,也是如此,发现 Buffer Pool 里存在要更新的数据,就直接在 Buffer Pool 里更新。
然后会把“在某个数据页上做了什么修改”记录到重做日志缓存(redo log buffer)里,接着刷盘到 redo log 文件里。
在这里插入图片描述理想情况,事务一提交就会进行刷盘操作,但实际上,刷盘的时机是根据策略来进行的。
redo log为物理日志,比如,哪一页做了什么样的修改。
每条 redo 记录由“表空间号+数据页号+偏移量+修改数据长度+具体修改的数据”组成

redo log 刷盘策略

nnoDB 存储引擎为 redo log 的刷盘策略提供了 innodb_flush_log_at_trx_commit 参数,它支持三种策略:

  • 0 :设置为 0 的时候,表示每次事务提交时不进行刷盘操作
  • 1 :设置为 1 的时候,表示每次事务提交时都将进行刷盘操作(默认值),只要事务提交成功,redo log记录就一定在硬盘里,不会有任何数据丢失。
    如果事务执行期间MySQL挂了或宕机,这部分日志丢了,但是事务并没有提交,所以日志丢了也不会有损失。
  • 2 :设置为 2 的时候,表示每次事务提交时都只把 redo log buffer 内容写入 page cache,如果仅仅只是MySQL挂了不会有任何数据丢失,但是宕机可能会有1秒数据的丢失(page cache还未将数据同步到磁盘)

innodb_flush_log_at_trx_commit 参数默认为 1 ,也就是说当事务提交时会调用 fsync 对 redo log 进行刷盘
另外,InnoDB 存储引擎有一个后台线程,每隔1 秒,就会把 redo log buffer 中的内容写到文件系统缓存(page cache),然后调用 fsync 刷盘。
在这里插入图片描述

redo log 两段提交

mysql重启时,根据redo log当前的状态
prepare时,查看是否存在对应binlog,如果没有则回滚
如果存在对应binlog,则提交事务
在这里插入图片描述

使用两阶段提交后,写入binlog时发生异常也不会有影响,因为MySQL根据redo log日志恢复数据时,发现redo log还处于prepare阶段,并且没有对应binlog日志,就会回滚该事务。
在这里插入图片描述

虽然redo log是处于prepare阶段,但是能通过事务id找到对应的binlog日志,所以MySQL认为是完整的,就会提交事务恢复数据。
在这里插入图片描述

bin log

当一条sql操作对数据库中的内容进行了更新,就会增加一条bin log日志。查询操作不会记录到bin log中。
MySQL数据库的数据备份、主备、主主、主从都离不开binlog,需要依靠binlog来同步数据,保证数据一致性。

格式

statement、row、mixed
statement:记录的内容是SQL语句原文,比如执行一条update T set update_time=now() where id=1
同步数据时,会执行记录的SQL语句,但是有个问题,update_time = now() 这里会获取当前系统时间,直接执行会导致与原库的数据不一致。

为了解决这种问题,我们需要指定为row,记录的内容不再是简单的SQL语句了,还包含操作的具体数据,记录内容如下。
在这里插入图片描述
这种格式,需要更大的容量来记录,比较占用空间,恢复与同步时会更消耗IO资源,影响执行速度。
所以就有了一种折中的方案,指定为mixed,记录的内容是前两者的混合。
MySQL会判断这条SQL语句是否可能引起数据不一致,如果是,就用row格式,否则就用statement格式。

刷盘

在这里插入图片描述
binlog_cache_size:数控制单个线程 binlog cache 大小,如果存储内容超过了这个参数,就要暂存到磁盘(Swap)
上图的 write,是指把日志写入到文件系统的 page cache,并没有把数据持久化到磁盘,所以速度比较快
上图的 fsync,才是将数据持久化到磁盘的操作

write和fsync的时机,可以由参数sync_binlog控制
0(默认):表示每次提交事务都只write,由系统自行判断什么时候执行fsync
1:示每次提交事务都会执行fsync,就如同 redo log 日志刷盘流程 一样。
大于1:表示每次提交事务都write,但累积N个事务后才fsync。

undo log

undo log主要用来回滚到某一个版本,是一种逻辑日志。
undo log记录的是修改之前的数据,比如:当delete一条记录时,undolog中会记录一条对应的insert记录,从而保证能恢复到数据修改之前。在执行事务回滚的时候,就可以通过undo log中的记录内容并以此进行回滚。
undo log还可以提供多版本并发控制下的读取(MVCC)。

  • 40
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值