文章目录
- 1. MySQL框架
- 2. 执行流程
- 2.1. 连接池:
- 2.2. SQL 前端(SEVER)
- 2.2.0. 查询缓存
- 2.2.1. SQL 接口
- 2.2.2. SQL 解析器
- 2.2.3. SQL 执行器
- 2.2.4. INNODB 中读写操作
- 2.3. 数据的保存形式
- 3.其他重要概念
- 3.1. 索引
- 3.1.1. 简单概念
- 3.1.2. 索引优化:
- 1. Using filesort
- 2. Using temporary
- 3. Using index
- 4. Using where
- 5. Using index condition
- 6. Using join buffer
- 7. Impossible WHERE
- 8. Select tables optimized away
- 3.2. 页
- 2. 读写效率
- 3. 缓冲池管理
- 4. 事务日志
- 5. MVCC 支持
- 6. 索引管理
- 7. 空间管理
- 3.2. 段
- 功能和优势
1. MySQL框架
虽然大部分人都会只用 mysql,但是大部分对于 mysql 的大致框架都不慎了解,虽然我也使用了很久,但是对 mysql 的了解仅仅是皮毛,这里简单对 MySQL 的重要模块进行简单剖析。
这里以 INNODB 为视角,简单介绍一下 MySQL 的重要某块和简单执行流程。
2. 执行流程
2.1. 连接池:
首先我们需要知道,为什么 MySQL 要有连接池。
客户端一般通过 JDBC 之类的驱动和 Mysql 进行链接。
Mysql 里一般为重要数据,为了可靠性,mysql 和 客户端的交互一般通过 TCP 协议。
不过数据库的连接并非全双工,而是半双工
那么问题来了,TCP 一般要维护链接状态,客户端一般和 MYSQL 需要频繁的数据交互。
- 反复重新断开和重建连接的代价是比较高的,
- 同样 如果保活所有链接又不现实,因为活跃客户端并非很多。
又因为客户端相对不容易变化,所以 Mysql 使用连接池 作为一个客户端与 Mysql 连接数的一个权衡,作为提升连接效率的方案。
连接池核心参数如下,设计思路和线程池是有些类似的。
-
最大连接数(Max Connections):
- 这个参数指定了连接池中允许的最大活跃连接数。当连接池中的活跃连接达到此数值时,新的请求将等待,直到有可用连接。
-
最小空闲连接数(Min Idle Connections):
- 这是连接池中应保持空闲状态的最小连接数。连接池会尝试保持至少这么多的连接处于空闲状态,以备不时之需。
-
初始化连接数(Initial Connections):
- 启动连接池时,立即创建的连接数量。这可以帮助应用程序快速启动,避免在第一次请求时承受创建连接的延迟。
在 MYSQL 内部大概分为了两层,一层是 SEVER 层,核心是 解析器,优化器,执行器,相当于 MYSQL 的前端;
一层是,存储引擎,如 INNODB 和 MYSAM,相当于 MYSQL 的后端。
2.2. SQL 前端(SEVER)
2.2.0. 查询缓存
查询缓存,一般在进入 SQL 的第一步进行哈希查询,如果存在 该 SQL 的 key 就直接返回,不过 MYSQL 中未默认开启工这个功能,且在 8.0 后该功能已经被移除。
原因主要如下:
- 会造成过多性能浪费
- 有专业的redis去替代
- redis 的手动,一致性查询更加灵活
2.2.1. SQL 接口
SQL 接口是一种逻辑概念。MYSQL 为客户端提供了包括创建,修改 表,增删改查表等一系列功能。而这些暴露给客户端统一的进行 sql 操作的逻辑接口。
这些操作具体在进入MYSQL 会进行其他的加工操作。
2.2.2. SQL 解析器
- SQL 解析(Parsing):
- 词法分析:此阶段主要将输入的 SQL 语句分解成一系列的令牌(tokens),例如关键字、操作符、标识符等。
- 语法分析:解析器根据 MySQL 的语法规则检查 SQL 语句的结构是否正确。如果语法有误,将在这一步骤中抛出错误。
- 预处理(Preprocessing):
- 在这一步,预处理器会进行一些额外的检查和转换。它会验证 SQL 语句中引用的数据库对象(如表和列)是否存在,以及用户是否有权执行该语句。预处理器还可能调整语句的结构,以便更有效地执行。
- 优化(Optimization):
- 查询优化器:这是一个核心组件,它负责选择执行查询的最有效路径。优化器会考虑多种可能的查询执行计划,并根据成本估算模型选择一个预期成本最低的计划。
查询优化器通过成本模型(cost model)来估算不同查询执行计划的成本,从而选择最有效的执行方案。这个成本通常是基于估计的资源消耗来计算的,包括磁盘I/O、内存使用、CPU 使用和网络开销等
- 执行计划生成:优化器产生一个详细的步骤计划,称为执行计划,它详细说明了如何访问数据库中的数据(例如选择哪些索引,连接表的顺序等)
2.2.3. SQL 执行器
- 根据优化器提供的执行计划,执行引擎负责执行具体的数据库操作。这包括数据的读取、计算和最终的数据返回。
- 执行过程中,执行引擎通过存储引擎的Handler 与 之交互,存储引擎负责管理如何在磁盘上存储和检索数据。
2.2.4. INNODB 中读写操作
在 INNODB 中一般有读写两种原子操作。
写入操作一般复杂一些。
MySQL 的读写操作都是面向 Buffer Pool 的。
但是在写的时候一般有 Redo log 和 Undo log 的辅助。
一般在事务提交后未刷入数据页的故障需要重做,事务未提交的故障需要回滚。
- redo 日志简解:
Redo log 日志是为了保证 buffer 区数据的意外恢复的,由于 Mysql 的数据处理都是面向 Buffer 的,由于是在内存中,没有持久化到硬盘中,这是不安全的,redo log 在意外停电等情况下用于buffer 区内容的恢复。
为什么 redo log 区需要刷盘? 那为什么不直接把buffer 区内容直接刷入数据页?
答: redo 日志的buffer 默认在内存也不够安全,需要通过刷盘策略刷入到硬盘。 由于redo log 日志保存是顺序保存,比直接随机刷入数据页要快。
- undo 日志简解:
Undo 日志是事务回滚的关键,并且和 INNODB 的核心并发机制 MVCC 有紧密的关联。
MVCC 机制
- 创建 Read View
当事务开始执行读操作时,数据库会为这个事务创建一个read view
。这个视图包含了以下几个关键信息:
- 活动事务列表:这个列表包含了在当前事务开始时所有活跃的事务的事务ID。
- 最小活动事务ID(min-active trx id):所有未完成事务中最小的事务ID。
- 创建时刻的事务ID(creator trx id):当前事务的ID。
- 读取数据时处理版本链
当事务尝试读取某个记录时,数据库系统会遵循以下步骤来确定应该返回哪个版本的数据:
- 遍历版本链:每条记录可能有多个版本,每个版本由不同的事务在不同时间创建。系统会从最新版本开始,遍历这个记录的版本链。
- 检查版本可见性:对于链中的每个版本,系统会检查创建该版本的事务ID(
trx_id
)。- 如果
trx_id
小于read view
中的最小活动事务ID,并且创建该版本的事务已提交,则这个版本是可见的。 - 如果
trx_id
属于read view
中的活动事务列表,则该版本不可见,因为创建它的事务还未提交。 - 如果
trx_id
等于当前事务的ID,这意味着当前事务自己修改了这条记录,所以这个版本是可见的。
- 如果
- 选择可见的最新版本:从版本链中选择最新的、对当前事务可见的版本作为读取结果。
在处理删除和插入操作时,版本链的处理会稍有不同。例如,如果一条记录被标记为删除,并且删除操作是在当前事务的
read view
创建之后发生的,则这条记录对当前事务不可见。
2.3. 数据的保存形式
以 INNODB 为例
.frm 文件
- 用途:
.frm
文件用于存储表的结构信息。这包括表的定义,如字段名、数据类型、默认值以及其他表结构相关的元数据。 - 特点:
- 每个表都有一个对应的
.frm
文件。 .frm
文件格式相对独立于存储引擎,这意味着无论表使用的是 InnoDB、MyISAM 或其他存储引擎,.frm
文件都是必需的。- 在 MySQL 5.7 及之前的版本中,
.frm
文件是表结构存储的唯一方式。
- 每个表都有一个对应的
.ibd 文件
- 用途:
.ibd
文件是 InnoDB 存储引擎特有的文件,用于存储表的数据和索引。 - 特点:
.ibd
文件允许 InnoDB 实现表级别的物理文件存储,这种方式称为“文件-表空间”(file-per-table tablespace)。- 使用
.ibd
文件可以使得数据库备份、恢复、迁移以及维护操作更加灵活和高效。 - 在
innodb_file_per_table
配置选项启用时,每个 InnoDB 表都会有一个独立的.ibd
文件。如果该选项未启用,所有表的数据和索引将存储在共享的表空间中,通常是ibdata1
文件。 .ibd
文件支持高级功能,如压缩、行格式选择等。
MySQL 8.0 及以后的变化
从 MySQL 8.0 开始,
.frm
文件被淘汰,表的结构信息改由数据字典管理,这是一个集中在 InnoDB
引擎内部的系统表集合。这个变化意味着在最新版本的 MySQL
中,表的定义信息不再存储在独立的文件中,而是直接嵌入到数据库引擎的内部结构中。这样的变化带来了更好的性能和更简单的管理。
3.其他重要概念
3.1. 索引
3.1.1. 简单概念
索引可谓是重中之重,在面试中为常问考点。在 INNODB 中索引的数据结构是 B+ 树,一个类似二叉排序树的,一个节点包含更多数据的二叉树。
由于 B+ 树的特性,在 MySQL 中,索引的使用遵循左前缀匹配的原则。
3.1.2. 索引优化:
tyoe类型
- system
- 含义:表仅有一行(系统表)。这是可能的最优
type
,因为 MySQL 实际上把整个表转换为一个常数。
- const
- 含义:通过索引一次性查找一行数据。这通常用于比较主键或唯一索引的查询。因为只返回一行数据,所以效率非常高。
- eq_ref
- 含义:在 JOIN 操作中,对于每个索引键,表中只有一行与之匹配。这通常发生在使用主键或唯一索引作为连接条件时。
- ref
- 含义:这是 JOIN 类型的一种,它返回匹配某个单个值的所有行。
ref
可以用于非唯一索引,或者是唯一索引的非唯一部分。
- fulltext
- 含义:查询使用了全文索引。
- ref_or_null
- 含义:这个类型类似于
ref
,但 MySQL 会额外搜索包含 NULL 值的行。这在处理包含 NULL 值的联合查询中特别有用。
- index_merge
- 含义:这种类型表示查询使用了两个或更多的索引合并策略。MySQL 会搜索几个索引,并使用索引合并的方法来生成结果集。
- unique_subquery
- 含义:这种类型用在 IN 查询中,其中子查询返回不重复的值,通常涉及到主键或唯一索引。
- index_subquery
- 含义:这种类型也用在 IN 查询中,但子查询可以返回重复的值。使用索引来查找每个键值。
- range
- 含义:MySQL 使用一个索引来检索给定范围的行,比如使用操作符 BETWEEN、<、>等。
- index
- 含义:MySQL 将遍历整个索引来查找匹配的行。这比全表扫描要好,因为索引通常是压缩的,所以IO成本较低。
- ALL
- 含义:全表扫描,MySQL 将遍历全表来找到匹配的行。这通常是最慢的一种类型,应该尽量避免。
EXTRA 关键字
1. Using filesort
- 含义:MySQL 需要进行外部排序来解决查询,常见于
ORDER BY
操作,表明索引没有被用于排序。 - 优化策略:尝试调整索引以覆盖排序和查询条件,或重新设计查询以利用索引进行排序。
2. Using temporary
- 含义:执行查询时,MySQL 需要创建一个临时表,通常在执行复杂的
JOIN
、排序或者子查询时发生。 - 优化策略:优化查询逻辑,减少需要使用临时表的操作。例如,简化
SELECT
子句中的表达式,或调整JOIN
的顺序以减少处理的数据量。
3. Using index
- 含义:查询能够只通过索引来完成,没有读取表的其他部分,这是一个非常高效的访问方式。
- 优化策略:这通常是一个良好的指标,表明查询已经很好地利用了索引。确保常用查询维持这种状态。
4. Using where
- 含义:MySQL 在检索行后还需要进行额外的 WHERE 条件过滤。
- 优化策略:尽可能地通过索引来满足 WHERE 条件,减少需要后处理的行数。
5. Using index condition
- 含义:使用了索引条件推送(Index Condition Pushdown),这在 MySQL 5.6 及以后版本中提供,它允许更高效地使用索引来过滤记录。
- 优化策略:此特性通常自动启用,确保你的 MySQL 版本支持此优化,并且索引适用于查询条件。
6. Using join buffer
- 含义:用于
JOIN
操作,表明没有使用索引,MySQL 需要使用缓冲区来处理JOIN
。 - 优化策略:调整
JOIN
语句或增加适当的索引,以尽量避免使用大量的JOIN
缓冲。
7. Impossible WHERE
- 含义:WHERE 子句的条件永远不为真,查询不会返回任何结果。
- 优化策略:检查逻辑错误,确保查询条件正确并能返回预期的结果。
8. Select tables optimized away
- 含义:优化器识别出只需要从优化的表中读取很少的数据或无需实际访问表数据。
- 优化策略:通常不需要进一步优化,这显示查询已经非常高效。
3.2. 页
在硬盘中一个磁盘块为4k,一个页通常为连续的4个磁盘块,在一次加载的时候加载一个页,由于是顺序读取一般情况下比较快,
- 数据存储组织
- 存储结构:InnoDB 将数据存储在大小固定的页中,通常每页的大小为 16KB(虽然这个大小可以配置)。这种方式允许数据库高效地读取、写入和管理数据。
2. 读写效率
- I/O 效率:通过以页为单位进行数据读写,数据库能够减少磁盘 I/O 操作的次数。因为即使需要访问的只是页中的少量数据,整个页的数据也会一次性被加载到内存中,这样对同一页面的后续访问不再需要磁盘 I/O。
3. 缓冲池管理
- 缓冲池(Buffer Pool):InnoDB 使用缓冲池来缓存数据页和索引页,从而提高数据访问速度。这意味着频繁访问的数据可以保留在内存中,提高数据库的响应速度和整体性能。
4. 事务日志
- Redo 日志:InnoDB 使用页作为 Redo 日志记录的基本单位。这意味着每次事务修改页内容时,修改的内容(或差异)被记录到 Redo 日志中。这有助于确保在数据库崩溃后能够恢复数据。
5. MVCC 支持
- 多版本并发控制:InnoDB 页中存储了行的多个版本,支持 MVCC。这允许多个事务同时访问同一数据页的不同版本,从而支持无锁读取,提高并发性。
6. 索引管理
- B+树索引:InnoDB 使用 B+树索引结构来加速数据访问。在这种结构中,索引数据也是按页组织的。索引的叶节点页直接包含或指向实际的数据页,而非叶节点页存储键值和指向子页的指针,从而支持快速数据查找和范围查询。
7. 空间管理
- 表空间:InnoDB 将所有的页组织在表空间中,表空间可以是单个文件或多个文件,其中包括数据和索引页。这种组织方式为数据文件的管理和扩展提供了灵活性。
3.2. 段
段的概念允许数据库以更高层次的逻辑结构管理数据,而不是单纯依赖于物理存储细节。在 InnoDB 中,常见的段类型包括:
- 数据段:存储表数据的页。
- 索引段:存储索引数据的页,每个索引(如主键或二级索引)都会有自己的索引段。
- 回滚段:存储用于事务回滚和多版本并发控制(MVCC)的旧数据。
功能和优势
逻辑上的段组织带来了几个关键优势:
- 性能优化:通过逻辑组织段,数据库能够更有效地管理数据和索引的存储,优化访问模式和查询性能。
- 数据管理:段使得数据和索引的管理变得更为灵活,例如,在增加或删除数据时,数据库可以更有效地分配和回收空间。
- 事务处理:段的使