DBDB优化一些收藏

01.MySQL架构与SQL执行流程

2021-08-14  本文已影响0人  javacoo

MySql笔记,笔记分为四个部分:
1.MySQL架构与SQL执行流程
2.MySQL索引原理与使用原则
3.MySQL事务与锁详解
4.MySQL性能优化总结
本节目标:

1、了解 MySQL 语句的执行流程

2、理解 MySQL 的架构与内部模块

3、掌握 InnoDB 存储引擎的磁盘与内存结

一、MySQL简介

MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件之一。

MySQL是一种关系型数据库管理系统,关系数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性。

MySQL所使用的 SQL 语言是用于访问数据库的最常用标准化语言。MySQL 软件采用了双授权政策,分为社区版和商业版,由于其体积小、速度快、总体拥有成本低,尤其是开放源码这一特点,一般中小型网站的开发都选择 MySQL 作为网站数据库。

MySQL发展历史:

时间 里程碑
1996 年 MySQL 1.0 发布。它的历史可以追溯到 1979 年,作者 Monty 用 BASIC 设计 的一个报表工具。
1996 年 10 月 3.11.1 发布。MySQL 没有 2.x 版本。
2000 年 ISAM 升级成 MyISAM 引擎。MySQL 开源。
2003 年 MySQL 4.0 发布,集成 InnoDB 存储引擎。
2005 年 MySQL 5.0 版本发布,提供了视图、存储过程等功能。
2008 年 MySQL AB 公司被 Sun 公司收购,进入 Sun MySQL 时代。
2009 年 Oracle 收购 Sun 公司,进入 Oracle MySQL 时代。
2010 年 MySQL 5.5 发布,InnoDB 成为默认的存储引擎
2016 年 MySQL 发布 8.0.0 版本。为什么没有 6、7?5.6 可以当成 6.x,5.7 可以当 成 7.x

MySQL主要分支:

  1. MariaDB:Oracle收购MySQL之后,MySQL创始人之一Monty担心MySQL发展的未来(开发缓慢、封闭、可能闭源),就创建了一个分支MariaDB(2009年),默认使用Maria存储引擎,它是原MyISAM存储引擎的升级版本。
  2. Percona Server:基于InnoDB存储引擎,提升了性能和易管理性,形成了XtraDB引擎。
  3. 网易的InnoSQL
  4. 极数据云舟的ArkDB

二、一条SQL语句是如何执行的?

SQL执行流程.png
1.通信协议

首先,MySQL 必须要运行一个服务,监听默认的 3306 端口。在我们开发系统跟第三方对接的时候,必须要弄清楚的有两件事。
第一个就是通信协议,比如我们是用 HTTP 还是 WebService 还是 TCP?
第二个是消息格式,比如我们用 XML 格式,还是 JSON 格式,还是定长格式?报文头长度多少,包含什么内容,每个字段的详细含义。

MySQL 是支持多种通信协议的,可以使用同步/异步的方式,支持长连接/短连接。 这里我们拆分来看。第一个是通信类型。

字段 含义
Threads_cached 缓存中的线程连接数
Threads_connected 当前打开的连接数
Threads_created 为处理连接创建的线程数
Threads_running 非睡眠状态的连接数,通常指并发连接数

每产生一个连接或者一个会话,在服务端就会创建一个线程来处理。反过来,如果要杀死会话,就是 Kill 线程。

有了连接数,怎么知道当前连接的状态? 也可以使用 SHOW PROCESSLIST; (root 用户)查看 SQL 的执行状态.

https://dev.mysql.com/doc/refman/5.7/en/show-processlist.htm

连接状态.png

一些常见的状态:https://dev.mysql.com/doc/refman/5.7/en/thread-commands.html

状态 含义
Sleep 线程正在等待客户端,以向它发送一个新语句
Query 线程正在执行查询或往客户端发送数据
Locked 该查询被其它查询锁定
Copying to tmp table on disk 临时结果集合大于 tmp_table_size。线程把临时表从存储器内部格式改 变为磁盘模式,以节约存储器
Sending data 线程正在为 SELECT 语句处理行,同时正在向客户端发送数
Sorting for group 线程正在进行分类,以满足 GROUP BY 要求
Sorting for order 线程正在进行分类,以满足 ORDER BY 要求

MySQL 服务允许的最大连接数是多少呢?在 5.7 版本中默认是 151 个,最大可以设置成 16384(2^14)。

show variables like 'max_connections';
最大连接数.png

show 的参数说明:

  1. 级别:会话 session 级别(默认);全局 global 级别

  2. 动态修改:set,重启后失效;永久生效,修改配置文件/etc/my.cnf

    set global max_connections = 1000;
    
2.通信方式
通信方式.png

MySQL 使用了半双工的通信方式?

要么是客户端向服务端发送数据,要么是服务端向客户端发送数据,这两个动作不能同时发生。所以客户端发送 SQL 语句给服务端的时候,(在一次连接里面)数据是不能分成小块发送的,不管你的 SQL 语句有多大,都是一次性发送。 比如我们用 MyBatis 动态 SQL 生成了一个批量插入的语句,插入 10 万条数据,values 后面跟了一长串的内容,或者 where 条件 in 里面的值太多,会出现问题。 这个时候我们必须要调整 MySQL 服务器配置 max_allowed_packet 参数的值(默认 是 4M),把它调大,否则就会报错。

show variables like 'max_allowed_packet';

另一方面,对于服务端来说,也是一次性发送所有的数据,不能因为你已经取到了想要的数据就中断操作,这个时候会对网络和内存产生大量消耗。 所以,我们一定要在程序里面避免不带 limit 的这种操作,比如一次把所有满足条件的数据全部查出来,一定要先 count 一下。如果数据量的话,可以分批查询。

执行一条查询语句,客户端跟服务端建立连接之后呢?下一步要做什么?

3.查询缓存

MySQL 内部自带了一个缓存模块。 缓存的作用我们应该很清楚了,把数据以 KV 的形式放到内存里面,可以加快数据的读取速度,也可以减少服务器处理的时间。但是 MySQL 的缓存我们好像比较陌生,从来没有去配置过,也不知道它什么时候生效?

比如 user_innodb 有 500 万行数据,没有索引。我们在没有索引的字段上执行同样的查询,大家觉得第二次会快吗?

select * from user_innodb where name='青山'

缓存没有生效,为什么?MySQL 的缓存默认是关闭的。

show variables like 'query_cache%';
查询缓存.png

默认关闭的意思就是不推荐使用,为什么 MySQL 不推荐使用它自带的缓存呢?

主要是因为 MySQL 自带的缓存的应用场景有限:

第一个是它要求 SQL 语句必须一 模一样,中间多一个空格,字母大小写不同都被认为是不同的的 SQL。

第二个是表里面任何一条数据发生变化的时候,这张表所有缓存都会失效,所以对于有大量数据更新的应用,也不适合。 所以缓存这一块,我们还是交给 ORM 框架(比如 MyBatis 默认开启了一级缓存), 或者独立的缓存服务,比如 Redis 来处理更合适。

在 MySQL 8.0 中,查询缓存已经被移除了。

4.语法解析和预处理(Parser & Preprocessor)

我们没有使用缓存的话,就会跳过缓存的模块,下一步我们要做什么呢? OK,这里我会有一个疑问,为什么我的一条 SQL 语句能够被识别呢?假如我随便执行一 个字符串 javacoo,服务器报了一个 1064 的错:

异常查询.png

它是怎么知道我输入的内容是错误的?

这个就是 MySQL 的 Parser 解析器和 Preprocessor 预处理模块。 这一步主要做的事情是对语句基于 SQL 语法进行词法和语法分析和语义的解析。

任何数据库的中间件,比如 Mycat,Sharding-JDBC(用到了 Druid Parser),都必须要有词法和语法分析功能,在市面上也有很多的开源的词法解析的工具(比如 LEX,Yacc)。

5.查询优化(Query Optimizer)与查询执行计划
优化器章节.png

但是优化器也不是万能的,并不是再垃圾的 SQL 语句都能自动优化,也不是每次都能选择到最优的执行计划,大家在编写 SQL 语句的时候还是要注意。

如果我们想知道优化器是怎么工作的,它生成了几种执行计划,每种执行计划的 cost 是多少,应该怎么做?

6.存储引擎

得到执行计划以后,SQL 语句是不是终于可以执行了?

问题又来了:

  1. 从逻辑的角度来说,我们的数据是放在哪里的,或者说放在一个什么结构里面?
  2. 执行计划在哪里执行?是谁去执行?
查看存储引擎.png

或者通过 DDL 建表语句来查看。

在 MySQL 里面,我们创建的每一张表都可以指定它的存储引擎,而不是一个数据库只能使用一个存储引擎。存储引擎的使用是以表为单位的。而且,创建表之后还可以修 改存储引擎。

我们说一张表使用的存储引擎决定我们存储数据的结构,那在服务器上它们是怎么存储的呢?我们先要找到数据库存放数据的路径:

show variables like 'datadir';

默认情况下,每个数据库有一个自己文件夹,以test 数据库为例。 任何一个存储引擎都有一个 frm 文件,这个是表结构定义文件。

不同的存储引擎存放数据的方式不一样,产生的文件也不一样,innodb 是 1 个, memory 没有,myisam 是两个。

这些存储引擎的差别在哪呢?

这些数据库支持的存储引擎,分别有什么特性呢?https://dev.mysql.com/doc/refman/5.7/en/storage-engines.html

MyISAM(3 个文件)

These tables have a small footprint. Table-level locking limits the performance in read/write workloads, so it is often used in read-only or read-mostly workloads in Web and data warehousing configurations.

应用范围比较小。表级锁定限制了读/写的性能,因此在 Web 和数据仓库配置中, 它通常用于只读或以读为主的工作。

特点:

支持表级别的锁(插入和更新会锁表)。不支持事务。

拥有较高的插入(insert)和查询(select)速度。

存储了表的行数(count 速度更快)。

(怎么快速向数据库插入 100 万条数据?我们有一种先用 MyISAM 插入数据,然后修改存储引擎为 InnoDB 的操作。)

适合:只读之类的数据分析的项目。

InnoDB(2 个文件)

the default storage engine in MySQL 5.7. InnoDB is a transaction-safe (ACID compliant) storage engine for MySQL that has commit, rollback, and crash-recovery capabilities to protect user data. InnoDB row-level locking (without escalation to coarser granularity locks) and Oracle-style consistent nonlocking reads increase multi-user concurrency and performance. InnoDB stores user data in clustered indexes to reduce I/O for common queries based on primary keys. To maintain data integrity, InnoDB also supports FOREIGN KEY referential-integrity constraints.

mysql 5.7 中的默认存储引擎。InnoDB 是一个事务安全(与 ACID 兼容)的 MySQL 存储引擎,它具有提交、回滚和崩溃恢复功能来保护用户数据。InnoDB 行级锁(不升级为更粗粒度的锁)和 Oracle 风格的一致非锁读提高了多用户并发性和性能。InnoDB 将用户数据存储在聚集索引中,以减少基于主键的常见查询的 I/O。为了保持数据完整性,InnoDB 还支持外键引用完整性约束。

特点:

支持事务,支持外键,因此数据的完整性、一致性更高。

支持行级别的锁和表级别的锁。

支持读写并发,写不阻塞读(MVCC)。

特殊的索引存放方式,可以减少 IO,提升查询效率。

适合:经常更新的表,存在并发读写或者有事务处理的业务系统。

Memory(1 个文件)

Stores all data in RAM, for fast access in environments that require quick lookups of non-critical data. This engine was formerly known as the HEAP engine. Its use cases are decreasing; InnoDB with its buffer pool memory area provides a general-purpose and durable way to keep most or all data in memory, and NDBCLUSTER provides fast key-value lookups for huge distributed data sets.

将所有数据存储在 RAM 中,以便在需要快速查找非关键数据的环境中快速访问。这个引擎以前被称为堆引擎。其使用案例正在减少;InnoDB 及其缓冲池内存区域提供了一 种通用、持久的方法来将大部分或所有数据保存在内存中,而 ndbcluster 为大型分布式 数据集提供了快速的键值查找。

特点:

把数据放在内存里面,读写的速度很快,但是数据库重启或者崩溃,数据会全部消 失。

只适合做临时表。

将表中的数据存储到内存中。

CSV(3 个文件)

Its tables are really text files with comma-separated values. CSV tables let you import or dump data in CSV format, to exchange data with scripts and applications that read and write that same format. Because CSV tables are not indexed, you typically keep the data in InnoDB tables during normal operation, and only use CSV tables during the import or export stage.

它的表实际上是带有逗号分隔值的文本文件。csv表允许以csv格式导入或转储数据, 以便与读写相同格式的脚本和应用程序交换数据。因为 csv 表没有索引,所以通常在正常操作期间将数据保存在 innodb 表中,并且只在导入或导出阶段使用 csv 表。

特点:

不允许空行,不支持索引。

格式通用,可以直接编辑,适合在不同数据库之间导入导出。

Archive(2 个文件)

These compact, unindexed tables are intended for storing and retrieving large amounts of seldom-referenced historical, archived, or security audit information.

这些紧凑的未索引的表用于存储和检索大量很少引用的历史、存档或安全审计信息。

特点:

不支持索引,不支持 update delete

这是 MySQL 里面常见的一些存储引擎,我们看到了,不同的存储引擎提供的特性都 不一样,它们有不同的存储机制、索引方式、锁定水平等功能。 我们在不同的业务场景中对数据操作的要求不同,就可以选择不同的存储引擎来满 足我们的需求,这个就是 MySQL 支持这么多存储引擎的原因。

7.执行引擎(Query Execution ),返回结果

OK,存储引擎分析完了,它是我们存储数据的形式,继续第二个问题,是谁使用执 行计划去操作存储引擎呢?

这就是我们的执行引擎,它利用存储引擎提供的相应的 API 来完成操作。

为什么我们修改了表的存储引擎,操作方式不需要做任何改变?因为不同功能的存 储引擎实现的 API 是相同的。

最后把数据返回给客户端,即使没有结果也要返回。

三,MySQL 体系结构总结

mysql模块.png
模块详解

1、 Connector:用来支持各种语言和 SQL 的交互,比如 PHP,Python,Java 的 JDBC;

2、 Management Serveices & Utilities:系统管理和控制工具,包括备份恢复、MySQL 复制、集群等等;

3、 Connection Pool:连接池,管理需要缓冲的资源,包括用户密码权限线程等 等;

4、 SQL Interface:用来接收用户的 SQL 命令,返回用户需要的查询结果

5、 Parser:用来解析 SQL 语句;

6、 Optimizer:查询优化器;

7、 Cache and Buffer:查询缓存,除了行记录的缓存之外,还有表缓存,Key 缓 存,权限缓存等等;

8、 Pluggable Storage Engines:插件式存储引擎,它提供 API 给服务层使用, 跟具体的文件打交道。

架构分层

总体上,我们可以把 MySQL 分成三层,跟客户端对接的连接层,真正执行操作的服 务层,和跟硬件打交道的存储引擎层(参考 MyBatis:接口、核心、基础)。


架构分层.png

四,一条更新 SQL是如何执行的?

在数据库里面,我们说的 update 操作其实包括了更新、插入和删除。如果大家有看 过 MyBatis 的源码,应该知道 Executor 里面也只有 doQuery()和 doUpdate()的方法, 没有 doDelete()和 doInsert()。 更新流程和查询流程有什么不同呢? 基本流程也是一致的,也就是说,它也要经过解析器、优化器的处理,最后交给执行器。 区别就在于拿到符合条件的数据之后的操作。

缓冲池 Buffer Pool

首先,InnnoDB 的数据都是放在磁盘上的,InnoDB 操作数据有一个最小的逻辑单位,叫做页(索引页和数据页)。我们对于数据的操作,不是每次都直接操作磁盘,因为磁盘的速度太慢了。InnoDB 使用了一种缓冲池的技术,也就是把磁盘读到的页放到一 块内存区域里面。这个内存区域就叫 Buffer Pool。

缓冲池.png

下一次读取相同的页,先判断是不是在缓冲池里面,如果是,就直接读取,不用再次访问磁盘。

修改数据的时候,先修改缓冲池里面的页。内存的数据页和磁盘数据不一致的时候, 我们把它叫做脏页。InnoDB 里面有专门的后台线程把 Buffer Pool 的数据写入到磁盘, 每隔一段时间就一次性地把多个修改写入磁盘,这个动作就叫做刷脏。

Buffer Pool 是 InnoDB 里面非常重要的一个结构,它的内部又分成几块区域。这里我们趁机到官网来认识一下 InnoDB 的内存结构和磁盘结构

InnoDB 内存结构和磁盘结构

内存结构和磁盘结构.png

Buffer Pool 默认大小是 128M(134217728 字节),可以调整。 查看参数(系统变量):

  SHOW VARIABLES like '%innodb_buffer_pool%';

这些参数都可以在官网查到详细的含义,用搜索功能。 https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html

BufferPool参数.png

内存的缓冲池写满了怎么办?(Redis 设置的内存满了怎么办?)InnoDB 用 LRU 算法来管理缓冲池(链表实现,不是传统的 LRU,分成了 young 和 old),经过淘汰的数据就是热点数据。

内存缓冲区对于提升读写性能有很大的作用。思考一个问题: 当需要更新一个数据页时,如果数据页在 Buffer Pool 中存在,那么就直接更新好了。 否则的话就需要从磁盘加载到内存,再对内存的数据页进行操作。也就是说,如果没有命中缓冲池,至少要产生一次磁盘 IO,有没有优化的方式呢?

最后把 Change Buffer 记录到数据页的操作叫做 merge。什么时候发生 merge? 有几种情况:在访问这个数据页的时候,或者通过后台线程、或者数据库 shut down、 redo log 写满时触发。 如果数据库大部分索引都是非唯一索引,并且业务是写多读少,不会在写数据后立刻读取,就可以使用 Change Buffer(写缓冲)。写多读少的业务,调大这个值:

SHOW VARIABLES LIKE 'innodb_change_buffer_max_size';
change_buffer_max_size.png

代表 Change Buffer 占 Buffer Pool 的比例,默认 25%。

RedoLog.png

这个文件就是磁盘的 redo log(叫做重做日志),对应于/var/lib/mysql/目录下的 ib_logfile0 和 ib_logfile1,每个 48M。 这 种 日 志 和 磁 盘 配 合 的 整 个 过 程 , 其 实 就 是 MySQL 里 的 WAL 技 术 (Write-Ahead Logging),它的关键点就是先写日志,再写磁盘。

show variables like 'innodb_log%';
innodb_log.png
含义
innodb_log_file_size 指定每个文件的大小,默认 48
innodb_log_files_in_group 指定文件的数量,默认为 2
innodb_log_group_home_dir 指定文件所在路径,相对或绝对。如果不指定,则为 datadir 路径。

问题: 同样是写磁盘,为什么不直接写到 db file 里面去?为什么先写日志再写磁盘?

我们先来了解一下随机 I/O 和顺序 I/O 的概念。

磁盘的最小组成单元是扇区,通常是 512 个字节。

操作系统和内存打交道,最小的单位是页 Page。

操作系统和磁盘打交道,读写磁盘,最小的单位是块 Block。

磁盘.png

如果我们所需要的数据是随机分散在不同页的不同扇区中,那么找到相应的数据需要等到磁臂旋转到指定的页,然后盘片寻找到对应的扇区,才能找到我们所需要的一块数据,一次进行此过程直到找完所有数据,这个就是随机 IO,读取数据速度较慢。

假设我们已经找到了第一块数据,并且其他所需的数据就在这一块数据后边,那么就不需要重新寻址,可以依次拿到我们所需的数据,这个就叫顺序 IO。

刷盘是随机 I/O,而记录日志是顺序 I/O,顺序 I/O 效率更高。因此先把修改写入日 志,可以延迟刷盘时机,进而提升系统吞吐。 当然 redo log 也不是每一次都直接写入磁盘,在 Buffer Pool 里面有一块内存区域 (Log Buffer)专门用来保存即将要写入日志文件的数据,默认 16M,它一样可以节省 磁盘 IO。

logbuffer.png
SHOW VARIABLES LIKE 'innodb_log_buffer_size';

需要注意:redo log 的内容主要是用于崩溃恢复。磁盘的数据文件,数据来自 buffer pool。redo log 写入磁盘,不是写入数据文件。

那么,Log Buffer 什么时候写入 log file?

在我们写入数据到磁盘的时候,操作系统本身是有缓存的。flush 就是把操作系统缓冲区写入到磁盘。

log buffer 写入磁盘的时机,由一个参数控制,默认是 1。

SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit';

https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_flush_log_at_trx_commit

含义
0(延迟写) log buffer 将每秒一次地写入 log file 中,并且 log file 的 flush 操作同时进行。 该模式下,在事务提交的时候,不会主动触发写入磁盘的操作。
1(默认,实时写,实时刷) 每次事务提交时 MySQL 都会把 log buffer 的数据写入 log file,并且刷到磁盘中去。
2(实时写,延迟刷) 每次事务提交时 MySQL 都会把 log buffer 的数据写入 log file。但是 flush 操作并不会同时进行。该模式下,MySQL 会每秒执行一次 flush 操作。
logbuffer写入磁盘.png

这是内存结构的第 4 块内容,redo log,它又分成内存和磁盘两部分。redo log 有 什么特点?

1、redo log 是 InnoDB 存储引擎实现的,并不是所有存储引擎都有。

2、不是记录数据页更新之后的状态,而是记录这个页做了什么改动,属于物理日志。

3、redo log 的大小是固定的,前面的内容会被覆盖。

redologfile.png

check point 是当前要覆盖的位置。如果 write pos 跟 check point 重叠,说明 redo log 已经写满,这时候需要同步 redo log 到磁盘中。

这是 MySQL 的内存结构,总结一下,分为: Buffer pool、change buffer、Adaptive Hash Index、 log buffer。

磁盘结构里面主要是各种各样的表空间,叫做 Table space。

InnoDB写入磁盘.png

如果存储引擎正在写入页的数据到磁盘时发生了宕机,可能出现页只写了一部分的 情况,比如只写了 4K,就宕机了,这种情况叫做部分写失效(partial page write),可能会导致数据丢失。

show variables like 'innodb_doublewrite';

我们不是有 redo log 吗?但是有个问题,如果这个页本身已经损坏了,用它来做崩溃恢复是没有意义的。所以在对于应用 redo log 之前,需要一个页的副本。如果出现了 写入失效,就用页的副本来还原这个页,然后再应用 redo log。这个页的副本就是 double write,InnoDB 的双写技术。通过它实现了数据页的可靠性。

跟 redo log 一样,double write 由两部分组成,一部分是内存的 double write,一个部分是磁盘上的 double write。因为 double write 是顺序写入的,不会带来很大的 开销。

在默认情况下,所有的表共享一个系统表空间,这个文件会越来越大,而且它的空间不会收缩。

独占表空间 file-per-table tablespaces

我们可以让每张表独占一个表空间。这个开关通过 innodb_file_per_table 设置,默 认开启。

SHOW VARIABLES LIKE 'innodb_file_per_table';

开启后,则每张表会开辟一个表空间,这个文件就是数据目录下的 ibd 文件(例如 /var/lib/mysql/gupao/user_innodb.ibd),存放表的索引和数据。 但是其他类的数据,如回滚(undo)信息,插入缓冲索引页、系统事务信息,二次 写缓冲(Double write buffer)等还是存放在原来的共享表空间内。

通用表空间 general tablespaces

通用表空间也是一种共享的表空间,跟 ibdata1 类似。 可以创建一个通用的表空间,用来存储不同数据库的表,数据路径和文件可以自定 义。语法:

create tablespace ts2673 add datafile '/var/lib/mysql/ts2673.ibd' file_block_size=16K engine=innodb;

在创建表的时候可以指定表空间,用 ALTER 修改表空间可以转移表空间

create table t2673(id integer) tablespace ts2673;

不同表空间的数据是可以移动的。

删除表空间需要先删除里面的所有表:

drop table t2673;
drop tablespace ts2673;

临时表空间 temporary tablespace

存储临时表的数据,包括用户创建的临时表,和磁盘的内部临时表。对应数据目录 下的 ibtmp1 文件。当数据服务器正常关闭时,该表空间被删除,下次重新产生。 Redo log 磁盘结构里面的 redo log,在前面已经介绍过了。

undo log tablespace

https://dev.mysql.com/doc/refman/5.7/en/innodb-undo-tablespaces.html

https://dev.mysql.com/doc/refman/5.7/en/innodb-undo-logs.html

undo log(撤销日志或回滚日志)记录了事务发生之前的数据状态(不包括 select)。 如果修改数据时出现异常,可以用 undo log 来实现回滚操作(保持原子性)。 在执行 undo 的时候,仅仅是将数据从逻辑上恢复至事务之前的状态,而不是从物 理页面上操作实现的,属于逻辑格式的日志。 redo Log 和 undo Log 与事务密切相关,统称为事务日志。 undo Log 的数据默认在系统表空间 ibdata1 文件中,因为共享表空间不会自动收 缩,也可以单独创建一个 undo 表空间。

show global variables like '%undo%';

有了这些日志之后,我们来总结一下一个更新操作的流程,这是一个简化的过程。 name 原值是 qingshan。

update user set name = 'penyuyan' where id

1、事务开始,从内存或磁盘取到这条数据,返回给 Server 的执行器;

2、执行器修改这一行数据的值为 penyuyan;

3、记录 name=qingshan 到 undo log;

4、记录 name=penyuyan 到 redo log;

5、调用存储引擎接口,在内存(Buffer Pool)中修改 name=penyuyan;

6、事务提交。

内存和磁盘之间,工作着很多后台线程。

https://dev.mysql.com/doc/refman/5.7/en/binary-log.html

binlog 以事件的形式记录了所有的 DDL 和 DML 语句(因为它记录的是操作而不是数据值,属于逻辑日志),可以用来做主从复制和数据恢复。

跟 redo log 不一样,它的文件内容是可以追加的,没有固定大小限制。

在开启了 binlog 功能的情况下,我们可以把 binlog 导出成 SQL 语句,把所有的操作重放一遍,来实现数据的恢复。

binlog 的另一个功能就是用来实现主从复制,它的原理就是从服务器读取主服务器的 binlog,然后执行一遍。 配置方式和主从复制的实现原理在 Mycat 第二节课中有讲述。 有了这两个日志之后,我们来看一下一条更新语句是怎么执行的:

SQL更新流程.png

例如一条语句:update teacher set name='段老师' where id=1;

1、先查询到这条数据,如果有缓存,也会用到缓存。

2、把 name 改成'段老师,然后调用引擎的 API 接口,写入这一行数据到内存,同时 记录 redo log。这时 redo log 进入 prepare 状态,然后告诉执行器,执行完成了,可以随时提交。

3、执行器收到通知后记录 binlog,然后调用存储引擎接口,设置 redo log为 commit 状态。

4、更新完成。

这张图片的重点:

一些信息
路漫漫其修远兮,吾将上下而求索
码云:https://gitee.com/javacoo
QQ:164863067
作者/微信:javacoo
邮箱:xihuady@126.com
上一篇下一篇

猜你喜欢

热点阅读