基础

SQL语句主要分为哪几类

  • 数据定义语言DDL(Data Ddefinition Language):CREATE,DROP,ALTER 主要为以上操作 即对逻辑结构等有操作的,其中包括表结构,视图和索引。
  • 数据查询语言DQL(Data Query Language)SELECT

这个较为好理解 即查询操作,以select关键字。各种简单查询,连接查询等 都属于DQL。

  • 数据操纵语言DML(Data Manipulation Language)INSERT,UPDATE,DELETE,主要为以上操作 即对数据进行操作的,对应上面所说的查询操作 DQL与DML共同构建了多数初级程序员常用的增删改查操作。而查询是较为特殊的一种 被划分到DQL中。
  • 数据控制功能DCL(Data Control Language)GRANT,REVOKE,COMMIT,ROLLBACK ,主要为以上操作 即对数据库安全性完整性等有操作的,可以简单的理解为权限控制等。

    六种关联查询

  • 交叉连接(CROSS JOIN)

  • 内连接(INNER JOIN)
    • 等值连接:ON A.id=B.id
    • 不等值连接:ON A.id > B.id
    • 自连接:SELECT * FROM A T1 INNER JOIN A T2 ON T1.id=T2.pid
  • 外连接(LEFT JOIN/RIGHT JOIN)
    • 左外连接:LEFT OUTER JOIN, 以左表为主,先查询出左表,按照ON后的关联条件匹配右表,没有匹配到的用NULL填充,可以简写成LEFT JOIN
    • 右外连接:RIGHT OUTER JOIN, 以右表为主,先查询出右表,按照ON后的关联条件匹配左表,没有匹配到的用NULL填充,可以简写成RIGHT JOIN
  • 联合查询(UNION与UNION ALL)

就是把多个结果集集中在一起,UNION前的结果为基准,需要注意的是联合查询的列数要相等,相同的记录行会合并;如果使用UNION ALL,不会合并重复的记录行;效率 UNION 高于 UNION ALL

  • 全连接(FULL JOIN)

MySQL不支持全连接;可以使用LEFT JOIN 和UNION和RIGHT JOIN联合使用 SELECT FROM A LEFT JOIN B ON A.id=B.id UNION SELECT FROM A RIGHT JOIN B ON A.id=B.id

  • 交叉连接(CROSS JOIN)

    MySQL 存储引擎架构

    image.png
    官方文档](https://dev.mysql.com/doc/refman/8.0/en/pluggable-storage-overview.html))
    根据图示,逻辑架构从上往下分为:
    - 应用层,主要包含连接处理,鉴权,安全管理
    - 服务层
    - NoSQL Interface,CRUD 操作
    - SQL Interface,DML,DDL,存储过程,视图,触发器
    - Parser 解析器,查询事物,对象优先级
    - Optimizer 优化器,查询路径
    - Caches & Buffers,缓存,缓冲区
    - 存储引擎,支持多种存储引擎
    - 文件系统,数据,索引,日志

    百万级别或以上的数据如何删除

    关于索引:由于索引需要额外的维护成本,因为索引文件是单独存在的文件,所以当我们对数据的增加,修改,删除,都会产生额外的对索引文件的操作,这些操作需要消耗额外的IO,会降低增/改/删的执行效率。所以,在我们删除数据库百万级别数据的时候,查询MySQL官方手册得知删除数据的速度和创建的索引数量是成正比的。
    1. 所以我们想要删除百万数据的时候可以先删除索引(此时大概耗时三分多钟)
    2. 然后删除其中无用数据(此过程需要不到两分钟)
    3. 删除完成后重新创建索引(此时数据较少了)创建索引也非常快,约十分钟左右。
    4. 与之前的直接删除绝对是要快速很多,更别说万一删除中断,一切删除会回滚。那更是坑了。

    MySQL 主从复制

    主从架构:一主一从、主主复制、一主多从、多主一从、级联复制
    复制办法:异步复制、全同步复制、半同步复制(介于异步与全同步之间,即主库执行完事物操作之后,得到至少一个从库接收到消息并写入 relay log 后,再返回客户端。这样保证了如果主库挂断,事物至少有传递到一个从库中。)image.png
    image.pnghttps://zhuanlan.zhihu.com/p/50597960
    MySQL主从复制涉及到三个线程

  • 主节点 binary log dump 线程

当从节点连接主节点时,主节点会创建一个log dump 线程,用于发送bin-log的内容。在读取bin-log中的操作时,此线程会对主节点上的bin-log加锁,当读取完成,甚至在发动给从节点之前,锁会被释放。

  • 从节点 I/O线程

当从节点上执行start slave命令之后,从节点会创建一个I/O线程用来连接主节点,请求主库中更新的bin-log。I/O线程接收到主节点binlog dump 进程发来的更新之后,保存在本地relay-log中。

  • 从节点SQL线程

SQL线程负责读取relay log中的内容,解析成具体的操作并执行,最终保证主从数据的一致性。

explain 含义解析

image.png

  • id 表示一个查询中各个子查询的执行顺序,id相同执行顺序由上至下。 id不同,id值越大优先级越高,越先被执行。id为 null 时表示一个结果集,不需要使用它查询,常出现在包含union等查询语句中。
  • select_type 表示查询中每个 select 子句的类型:
    • SIMPLE 不包含任何子查询或union等查询
    • PRIMARY 包含子查询最外层查询就显示为 PRIMARY
    • SUBQUERY 在select或 where字句中包含的查询
    • DERIVED from字句中包含的查询
    • UNION 出现在union后的查询语句中
    • UNION RESULT 从UNION中获取结果集
  • table 查询的数据表,当从衍生表中查数据时会显示 x ,表示对应的执行计划id
  • partitions 表分区、表创建的时候可以指定通过那个列进行表分区。
  • type 表示MySQL在表中找到所需行的方式,又称「访问类型」
    • ALL 扫描全表数据
    • index 遍历索引
    • range 索引范围查找
    • index_subquery 在子查询中使用 ref
    • unique_subquery 在子查询中使用 eq_ref
    • ref_or_null 对Null进行索引的优化的 ref
    • fulltext 使用全文索引
    • ref 使用非唯一索引查找数据
    • eq_ref 在join查询中使用PRIMARY KEYorUNIQUE NOT NULL索引关联。
  • possible_keys 可能使用的索引,但不一定被查询使用
  • key 显示MySQL在查询中实际使用的索引,若没有使用索引,显示为NULL; 查询中若使用了覆盖索引(覆盖索引:索引的数据覆盖了需要查询的所有数据),则该索引仅出现在key列表中
  • key_len 表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度
  • ref 表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值
  • rows 返回估算的结果集数目,并不是一个准确的值。
  • filtered
  • Extra 包含不适合在其他列中显示但十分重要的额外信息

    • Using index 使用覆盖索引
    • Using where 使用了用where子句来过滤结果集
    • Using filesort 使用文件排序,使用非索引列进行排序时出现,非常消耗性能,尽量优化。
    • Using temporary 使用了临时表

      分库分表

      | # | 分库分表前 | 分库分表后 | | —- | —- | —- | | 并发支撑情况 | MySQL 单机部署,扛不住高并发 | MySQL 从单机到多机,能承受的并发增加了多倍 | | 磁盘使用情况 | MySQL 单机磁盘容量几乎撑满 | 拆分为多个库,数据库服务器磁盘使用率大大降低 | | SQL 执行性能 | 单表数据量太大,SQL 越跑越慢 | 单表数据量减少,SQL 执行效率明显提升 |
  • 水平拆分的意思,就是把一个表的数据给弄到多个库的多个表里去,但是每个库的表结构都一样,只不过每个库表放的数据是不同的,所有库表的数据加起来就是全部数据。水平拆分的意义,就是将数据均匀放更多的库里,然后用多个库来扛更高的并发,还有就是用多个库的存储容量来进行扩容。

image.png

  • 垂直拆分的意思,就是把一个有很多字段的表给拆分成多个表或者是多个库上去。每个库表的结构都不一样,每个库表都包含部分字段。一般来说,会将较少的访问频率很高的字段放到一个表里去,然后将较多的访问频率很低的字段放到另外一个表里去。因为数据库是有缓存的,你访问频率高的行字段越少,就可以在缓存里缓存更多的行,性能就越好。这个一般在表层面做的较多一些。

image.png

Page 存储结构

  • 页头:记录页面的控制信息,56 字节,包括页左右兄弟页面指针、页面空间使用情况
  • 虚记录
    • 最大虚记录:比业内最大主键还大
    • 最小虚记录:比页内最小主键还小
  • 记录堆:行记录存储区,分为有效记录和已删除记录两种
  • 自由空间链表:已删除记录组成的链表
  • 未分配空间
  • Slot 区 连续的几个空间
  • 页尾:校验

image.png

MySQL 数据类型

  • 整数类型,包括TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT,分别表示1字节、2字节、3字节、4字节、8字节整数。
  • 实数,包括FLOAT、DOUBLE、DECIMAL。DECIMAL可以用于存储比BIGINT还大的整型,能存储精确的小数。而FLOAT和DOUBLE是有取值范围的,并支持使用标准的浮点进行近似计算。计算时FLOAT和DOUBLE相比DECIMAL效率更高一些,DECIMAL你可以理解成是用字符串进行处理。
  • 字符串类型,包括 VARCHAR、CHAR、TEXT、BLOB。VARCHAR 用于存储可变长字符串,它比 CHAR 定长类型更节省空间;当CHAR值被存储时,它们被用空格填充到特定长度,检索CHAR值时需删除尾随空格。
  • 枚举类型,把不重复的数据存储为一个预定义的集合。有时可以使用ENUM代替常用的字符串类型。ENUM存储非常紧凑,会把列表值压缩到一个或两个字节。ENUM在内部存储时,其实存的是整数。尽量避免使用数字作为ENUM枚举的常量,因为容易混乱。排序是按照内部存储的整数
  • 日期时间类型,尽量使用timestamp,空间效率高于datetime,用整数保存时间戳通常不方便处理。如果需要存储微妙,可以使用bigint存储。

CHAR和VARCHAR的区别?

  • CHAR和VARCHAR类型在存储和检索方面有所不同
  • CHAR列长度固定为创建表时声明的长度,长度值范围是1到255
  • 当CHAR值被存储时,它们被用空格填充到特定长度,检索CHAR值时需删除尾随空格。

mysql中int(10)和char(10)以及varchar(10)的区别

  • int(10) 的 10 表示显示的数据的长度,不是存储数据的大小;chart(10) 和 varchar(10) 的 10 表示存储数据的大小,即表示存储多少个字符。
  • int(10) 10 位的数据长度 9999999999,占 32 个字节,int型4位
  • char(10) 10 位固定字符串,不足补空格,最多 10 个字符;varchar(10) 10 位可变字符串,不足补空格,最多 10 个字符
  • char(10) 表示存储定长的 10 个字符,不足 10 个就用空格补齐,占用更多的存储空间;varchar(10) 表示存储 10 个变长的字符,存储多少个就是多少个,空格也按一个字符存储,这一点是和 char(10) 的空格不同的,char(10) 的空格表示占位不算一个字符

MySQL 中 datetime 和 timestamp 的区别与选择 https://segmentfault.com/a/1190000017393602

MySQL 索引

优点:可以加快检索速度
缺点:创建与删除索引,修改数据,需要动态维护索引;索引需要占用物理空间

分类

根据创建语法的不同进行分类:

  • 单列索引,仅包含单个列
    CREATE INDEX index_name ON table(column(length))
  • 联合索引,包含多个列
    CREATE INDEX index_name ON table(column1,column2,…)
    • 最左前缀匹配原则:数据是按照 column1 有序方式进行保存,当 column1 相同时,再去比较 column2。所以查询的时候必须带上 col1;若直接使用 col2 进行查询 ,在大部分场景下,该索引会失效。
  • 唯一索引,值不能存在相同的,可以有一个值为 NULL
    CREATE UNIQUE INDEX indexName ON table(column(length))
  • 主键索引,值不能为 NULL 的唯一索引,一个表只有一个主键索引
    primary key
  • 全文索引 ,用于全文查找
  • Hash 索引,键进行 Hash 后保存,仅存在于Memory 存储引擎
  • 空间索引 Spatial Indexes,保存 点位,地理位置信息,仅 InnoDB,MyISAM 支持空间索引
  • 覆盖索引,表示查询列能直接通过节点关键字直接返回,不用回表查询。
    比如创建索引: create index idx_name_phoneNum on users(name,phoneNum);
    查询语句: select name,phoneNum from user where name=?

索引 index 和 key 之前的区别和联系
Key 更多的是约束关系,UNIQUE KEY,PRIMARY KEY,FOREIGN KEY
Index 更多偏向查询

聚集索引与非聚集索引

InnoDB 引擎中特殊存在的索引 参考

  • 聚集索引 Clustered Index:索引键值的逻辑顺序与索引所服务的表中相应行的物理顺序相同的索引。
    • 当 Table 定义了 PRIMARY KEY 时,InnoDB 引擎就会将主键索引作为聚集索引。当 Table 没有定义 PRIMARY KEY,并且没有 UNIQUE 并且 NOT NULL 的列时,MySQL 会自己加一个 Auto-increment column。
    • 当 Table 没有定义 PRIMARY KEY,MySQL 会使用第一个 NOT NULL 的 UNIQUE INDEX 作为聚集索引
    • 当 Table 既没有定义 PRIMARY KEY 也没有合适的 UNIQUE INDEX 时,InnoDB 会在一个合成列上面生成一个名为 GEN_CLUST_INDEX 隐藏的聚集索引
  • 非聚集索引 Secondary Index:顾名思义,就是不是聚集索引。


聚集索引保存着完整行数据,一般是主键索引,所以查询很快;非聚集索引的记录中保存着主键的值,通过非聚集索引查找元素时会先查主键,然后再根据主键查找完整的数据。

索引实现-数据结构

不同存储引擎索引的实现方式不一样
除了空间索引(使用 R-Tree),InnoDB 使用 B+ 树这个数据结构作为索引,示意图如下,其中主键索引的实现就是按照主键排序的方式进行保存。
image.png

  • 每个节点实际为一个 Page,默认大小为 16 KB
  • 非叶子节点关键字的个数等于子树个数,
  • 叶子节点顺序链接,前后叶子节点有指针相互指向

相比于 B 树,MySQL B+ 树有几个优点:

  • 非叶子节点只保留 主键值和子节点指针,相比 B 树,节点能够保存更多的信息,层数更少,一次磁盘 IO 能够获取更多数据
  • 叶子节点前后可以顺序遍历,适合范围查询

    查看索引

    mysql> show index from tblname;
    mysql> show keys from tblname;

  • Table:表的名称

  • Non_unique:如果索引不能包括重复词,则为 0。如果可以,则为 1
  • Key_name:索引的名称
  • Seq_in_index:索引中的列序列号,从 1 开始
  • Column_name:列名称
  • Collation:列以什么方式存储在索引中。在 MySQL 中,有值 ‘A’(升序)或NULL(无分类)。
  • Cardinality:索引中唯一值的数目的估计值。通过运行 ANALYZE TABLE 或 myisamchk -a 可以更新。基数根据被存储为整数的统计数据来计数,所以即使对于小型表,该值也没有必要是精确的。基数越大,当进行联合时, MySQL 使用该索引的机会就越大。
  • Sub_part:如果列只是被部分地编入索引,则为被编入索引的字符的数目。如果整列被编入索引,则为 NULL。
  • Packed:指示关键字如何被压缩。如果没有被压缩,则为 NULL。
  • Null:如果列含有 NULL ,则含有 YES 。如果没有,则该列含有 NO。
  • Index_type:用过的索引方法(BTREE, FULLTEXT, HASH, RTREE)。
  • Comment:更多评注。

    索引使用注意注意项

  • 索引列的数据长度越短,那么每个节点存储的关键字数据就越多,高度也就越小

  • 只为用于搜索、排序或分组的列创建索引,B+ 树分裂很耗时
  • 散列度低的列不适合做索引,因为 B+ 树不知道怎么分裂
  • 随机无序的列不适合做索引,这样会导致 B+ 树插入元素不是顺序的,频繁分裂

索引失效场景:

  • where 列包含 函数,表达式
  • 隐式转换
  • like %xxx
  • 负向查询 <> != NOT In

    MySQL 事物

  • 原子性(Atomicity,或称不可分割性),要么全部执行,要么不执行

  • 一致性(Consistency),执行前后,数据保持完整
  • 隔离性(Isolation),事务操作之间彼此独立和透明互不影响。
  • 持久性(Durability),修改持久化到磁盘,事务一旦提交,其结果就是永久的。即便发生系统故障,也能恢复。

    隔离级别

  • 读未提交(Read Uncommited),所有事物可以看到其它事物未提交的数据。造成脏读(Dirty Read),即读取到脏数据。

  • 读已提交(Read Commited),事物只能看到其它事物提交的结果。这里会存在不可重复读的问题;事物 A 在事物 B 提交前、提交后读取相同的行数据,结果可能不一样。
    每一条 select 都有自己的一致性读 ReadView
  • 可重复读(Repeatable Read),MySQL 默认事物隔离级别,同一事务的多个实例在并发读取数据时,会看到相同的数据行。
    ReadView 是以第一条 select 语句的运行时,作为本事务的一致性读 snapshot 的建立时间点
  • 串行化(Serializable),强制事务排序,使之不可能出现冲突。在读取的数据行上面加上共享锁

可重复读的核心就是一致性读(consistent read);而事务更新数据的时候,只能用当前读。如果当前的记录的行锁被其他事务占用的话,就需要进入锁等待。
而读提交的逻辑和可重复读的逻辑类似,它们最主要的区别是:在可重复读隔离级别下,只需要在事务开始的时候创建一致性视图,之后事务里的其他查询都共用这个一致性视图;在读提交隔离级别下,每一个语句执行前都会重新算出一个新的视图。

MySQL 默认隔离级别是 RR 的原因

MySQL 5.1 之前 Binlog 只有 STATEMENT 格式,在 RC 隔离级别下面,日志格式可能和指向顺序不一致。5.1 之后,Binlog 支持 ROW 模式,主从复制就不会出现这样问题。
所以使用 RR 是为了兼容 5.0 之前的 MySQL 版本 Binlog 日志有问题的场景;
image.png

并发事物带来的问题

  • 脏读(Dirty Read):一个事务可以读取其他事务未提交的执行结果
  • 丢失修改(Lost to modify):第一个事务中修改了这个数据后,第二个事务也修改了这个数据。
  • 不可重复读(Nonrepeatable Read):在同一次事务中,同一个查询在 T1 时间内读取某一行,在 T2 时间重新读取这一行,这一行发生了 UPDATE 或者 DELETE
  • 幻读(Phantom Read):用户读取某一范围的数据行时,另外一个事务在范围内 插入 insert 了新行,用户再次读取时,发现新的幻影行(第二次查询返回了第一次查询没有返回的行)。Phantom Rows

不可重复读重点在于 update 和 delete,数据发生了变化,而幻读的重点在于 insert(行数发生了变化)。
InnoDB 通过多版本并发控制(MVCC,Multiversion Conccurrency Control)解决不可重复读问题,在此基础上通过间隙锁解决幻读问题。

事物的实现

原子性实现:undo log

undo log 属于逻辑日志,记录 SQL 执行相关信息,记录的是与操作相反的操作,insert 对应 delete

持久性实现:redo log

InnoDB 提供 Buffer Pool 作为数据库缓存,读取数据从 Buffer Pool 中读取,若 Buffer Pool 没有再去磁盘中读取并放入 Buffer Pool;当数据发生修改时,会先在 redo log 中记录此次操作,然后修改 Buffer Pool 数据(WAL,Write ahead logging,预写式日志),避免 MySQL 宕机后,Buffer Pool 中数据没有 flush到磁盘中。

隔离性实现

隔离性追求的目标是并发情形下事物之间的操作互不干扰,

  • 事物 A 的写操作对事物 B 的写操作隔离:锁机制
  • 事物 A 的写操作对事物 B 的读操作隔离:MVCC 机制

    一致性实现

    一致性是指事务执行结束后,数据库的完整性约束没有被破坏,事务执行的前后都是合法的数据状态。数据库的完整性约束包括但不限于:实体完整性(如行的主键存在且唯一)、列完整性(如字段的类型、大小、长度要符合要求)、外键约束、用户自定义完整性(如转账前后,两个账户余额的和应该不变)。
    一致性是事物追求的最终目标,实现一致性的措施包括:

  • 保证原子性、持久性和隔离性,如果这些特性无法保证,事务的一致性也无法保证

  • 数据库本身提供保障,例如不允许向整形列插入字符串值、字符串长度不能超过列的限制等
  • 应用层面进行保障,例如如果转账操作只扣除转账者的余额,而没有增加接收者的余额,无论数据库实现的多么完美,也无法保证状态的一致

    多版本并发控制 Multi-Version Concurrency Control(MVCC)

    MVCC 仅在 RC 和 RR 隔离级别下面起作用
    当读取的某一行被其他事务锁定时,它可以从 undo log 中分析出该行记录以前的数据是什么,从而提供该行版本信息,帮助用户实现一致性非锁定读取。
    image.png
    1)隐藏列:InnoDB中每行数据都有隐藏列,隐藏列中包含了本行数据的事务id(DATA_TRX_ID)、指向 undo log 的指针(DATA_ROLL_PTR),若没有主键还会有 DB_ROW_ID
    2)基于undo log 的版本链:前面说到每行数据的隐藏列中包含了指向 undo log 的指针,而每条undo log 也会指向更早版本的 undo log,从而形成一条版本链。
    3)ReadView:通过隐藏列和版本链,MySQL 可以将数据恢复到指定版本;但是具体要恢复到哪个版本,则需要根据 ReadView 来确定。所谓ReadView,是指事务(记做事务A)在某一时刻给整个事务系统(trx_sys)打快照,之后再进行读操作时,会将读取到的数据中的事务 id 与 trx_sys 快照比较,从而判断数据对该 ReadView 是否可见,即对事务 A 是否可见。
    trx_sys中的主要内容,以及判断可见性的方法如下:

  • low_limit_id:表示生成 ReadView 时系统中应该分配给下一个事务的id。如果数据的事务 id 大于等于 low_limit_id ,则对该 ReadView 不可见。

  • up_limit_id:表示生成 ReadView 时当前系统中活跃的读写事务中最小的事务 id。如果数据的事务 id 小于 up_limit_id,则对该 ReadView 可见。
  • rw_trx_ids:表示生成 ReadView 时当前系统中活跃的读写事务的事务 id 列表。如果数据的事务 id 在 low_limit_id 和 up_limit_id 之间,则需要判断事务 id 是否在 rw_trx_ids 中:如果在,说明生成 ReadView 时事务仍在活跃中,因此数据对 ReadView 不可见;如果不在,说明生成 ReadView 时事务已经提交了,因此数据对 ReadView 可见。

    MySQL 日志

    Binlog

    1. show binary logs; // 查看binlog列表
    2. show master status; // 查看最新 binlog 文件
    3. show binlog events in 'mysql-bin.000599'; // 查看当前 binlog 文件内容

    image.png

  • Log_name,当前日志名称

  • Pos,事件开始位置
  • Event_type,事件类型
  • Server_id,服务器 ID
  • End_log_pos,事件结束位置
  • Info,具体事件信息


格式设置:

  • statement模式下,每一条会修改数据的sql都会记录在binlog中。不需要记录每一行的变化,减少了binlog日志量,节约了IO,提高性能。由于sql的执行是有上下文的,因此在保存的时候需要保存相关的信息,同时还有一些使用了函数之类的语句无法被记录复制。
  • row级别下,不记录sql语句上下文相关信息,仅保存哪条记录被修改。记录单元为每一行的改动,基本是可以全部记下来但是由于很多操作,会导致大量行的改动(比如alter table),因此这种模式的文件保存的信息太多,日志量太大。
  • mixed,一种折中的方案,普通操作使用statement记录,当无法使用statement的时候使用row。
    此外,新版的MySQL中对row级别也做了一些优化,当表结构发生变化的时候,会记录语句而不是逐行记录。

    relay log

    在 MySQL 主从同步中,从服务会将主服务发来的 Binlog 记录到 relay log 中。relay-log的结构和binlog非常相似,只不过他多了一个master.info和relay-log.info的文件。
    master.info记录了上一次读取到master同步过来的binlog的位置,以及连接master和启动复制必须的所有信息。relay-log.info记录了文件复制的进度,下一个事件从什么位置开始,由sql线程负责更新。

    undo log

    记录在更新的时候都会同时记录一条回滚操作。记录上的最新值,通过回滚操作,都可以得到前一个状态的值。当系统里没有比这个回滚日志更早的 read-view 的时候,回滚日志会被删除。
    长事务意味着系统里面会存在很老的事务视图。由于这些事务随时可能访问数据库里面的任何数据,所以这个事务提交之前,数据库里面它可能用到的回滚记录都必须保留,这就会导致大量占用存储空间。

    redo log

    当有一条记录需要更新的时候,InnoDB引擎就会先把记录写到 redo log,redo log 大小是固定的。

  • redo log是InnoDB引擎特有的;binlog是MySQL的Server层实现的,所有引擎都可以使用。

  • redo log是物理日志,记录的是“在某个数据页上做了什么修改”;binlog是逻辑日志,记录的是这个语句的原始逻辑,比如“给ID=2这一行的c字段加1 ”。binlog 是二进制的,根据 binlog_format 可以分为 row,stattement,mix。
  • redo log 用于 crash recovery;binlog 用于 point-in-time recovery 基于时间点恢复。
  • redo log是循环写的,空间固定会用完;binlog是可以追加写入的。“追加写”是指binlog文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。
  • 写入时机,redo log 写入时机不固定;binlog 在事物提交时写入。
    • innodb_flush_log_at_trx_commit = 0,表示事务提交时,不进行 redo log buffer 的刷盘操作
    • innodb_flush_log_at_trx_commit = 1 表示将此时事务所对应的 redo log 所在的 redo log block 从内存写入到磁盘,同时调用 fysnc,确保数据落入到磁盘,可以保证 MySQL 异常重启之后数据不丢失。
    • innodb_flush_log_at_trx_commit = 2 表示只是将日志写入到操作系统的缓存,而不进行 fysnc 操作。(进程在向磁盘写入数据时,是先将数据写入到操作系统的缓存中:os cache,再调用 fsync 方法,才会将数据从 os cache 中刷新到磁盘上)

      日志两阶段提交

      日志的两阶段提交,保障了崩溃恢复时, MySQL 主备数据不一致问题,沿用《MySQL 实战 45 讲》 的图:
      image.png
      binlog 日志中,当 binlog_format = statement 时,完整的事物结尾会有 commit 标志;当 binlog_format = ROW 时,完整的事物结尾有 XID event。
      当 redo log 只有 prepare 没有 commit 时,就需要根据 redo log 里面的 xid 关联 binlog 查看事物是否完整。
  1. 若 redo log 事物时完整的,并且有 commit 标志,那么直接恢复
  2. 若 redo log 只有 prepare,那么需要判断 binlog 是否完整
    • 若 binlog 完整,则直接提交事物
    • 若 binlog 不完整,回滚事物

      主从同步

      image.png
      一个事务日志同步的完整过程:
  • 在备库B上通过change master命令,设置主库A的IP、端口、用户名、密码,以及要从哪个位置开始请求binlog,这个位置包含文件名和日志偏移量。
  • 在备库B上执行start slave命令,这时候备库会启动两个线程,就是图中的io_thread和sql_thread。其中io_thread负责与主库建立连接。
  • 主库A校验完用户名、密码后,开始按照备库B传过来的位置,从本地读取binlog,发给B。
  • 备库B拿到binlog后,写到本地文件,称为中转日志(relay log)。
  • sql_thread读取中转日志,解析出日志里的命令,并执行。

再深入一点|binlog和relay-log到底长啥样?