mirror of
https://github.com/CyC2018/Interview-Notebook.git
synced 2026-10-31 02:53:42 +00:00
Update MySQL.md
为面试中常问的小节加上高频标记;补充联合索引最左前缀的示意图和范围条件带等号时的说明,补充 sync_binlog 为 0 时的刷盘行为。
This commit is contained in:
+21
-19
@@ -2,13 +2,13 @@
|
||||
<!-- GFM-TOC -->
|
||||
* [MySQL](#mysql)
|
||||
* [一、索引](#一索引)
|
||||
* [B+ Tree 原理](#b-tree-原理)
|
||||
* [MySQL 索引](#mysql-索引)
|
||||
* [B+ Tree 原理](#b-tree-原理) 🔥🔥
|
||||
* [MySQL 索引](#mysql-索引) 🔥
|
||||
* [索引的优点](#索引的优点)
|
||||
* [索引的使用条件](#索引的使用条件)
|
||||
* [索引优化](#索引优化)
|
||||
* [索引失效](#索引失效)
|
||||
* [二、查询性能优化](#二查询性能优化)
|
||||
* [索引的使用条件](#索引的使用条件) 🔥🔥
|
||||
* [索引优化](#索引优化) 🔥
|
||||
* [索引失效](#索引失效) 🔥
|
||||
* [二、查询性能优化](#二查询性能优化) 🔥🔥
|
||||
* [一条 SQL 的执行过程](#一条-sql-的执行过程)
|
||||
* [使用 Explain 进行分析](#使用-explain-进行分析)
|
||||
* [慢查询的定位与优化](#慢查询的定位与优化)
|
||||
@@ -24,7 +24,7 @@
|
||||
* [浮点数](#浮点数)
|
||||
* [字符串](#字符串)
|
||||
* [时间和日期](#时间和日期)
|
||||
* [五、切分](#五切分)
|
||||
* [五、切分](#五切分) 🔥
|
||||
* [水平切分](#水平切分)
|
||||
* [垂直切分](#垂直切分)
|
||||
* [Sharding 策略](#sharding-策略)
|
||||
@@ -32,7 +32,7 @@
|
||||
* [六、复制](#六复制)
|
||||
* [主从复制](#主从复制)
|
||||
* [读写分离](#读写分离)
|
||||
* [七、日志](#七日志)
|
||||
* [七、日志](#七日志) 🔥
|
||||
* [redo log](#redo-log)
|
||||
* [binlog](#binlog)
|
||||
* [两阶段提交](#两阶段提交)
|
||||
@@ -52,7 +52,7 @@
|
||||
|
||||
## 一、索引
|
||||
|
||||
### B+ Tree 原理
|
||||
### B+ Tree 原理 🔥🔥
|
||||
|
||||
#### 1. 数据结构
|
||||
|
||||
@@ -91,7 +91,7 @@ B+ Tree 是一种多路平衡查找树:每个节点可以有很多个子节点
|
||||
|
||||
LSM 树(Log-Structured Merge Tree)先把写入放进内存中的有序表,攒满后顺序写成一个有序文件,后台再合并这些文件,RocksDB、HBase 采用这种结构。它的写入快,但读一个 key 可能要依次查内存和多层文件。B+ Tree 原地更新,读一行固定走 3~4 层,更适合读多、要求查询延迟稳定的场景。
|
||||
|
||||
### MySQL 索引
|
||||
### MySQL 索引 🔥
|
||||
|
||||
索引是在存储引擎层实现的,不同存储引擎支持的索引类型和实现不同。下面按数据结构介绍几种索引;按用途还可以分为主键索引、唯一索引、普通索引、联合索引和前缀索引。
|
||||
|
||||
@@ -149,7 +149,7 @@ MEMORY 引擎支持哈希索引,InnoDB 不能显式创建哈希索引,但有
|
||||
- 帮助服务器避免排序和分组时创建临时表:B+Tree 索引是有序的,可以直接用于 ORDER BY 和 GROUP BY;
|
||||
- 将随机 I/O 变为顺序 I/O:B+Tree 索引是有序的,相邻的数据存储在一起。
|
||||
|
||||
### 索引的使用条件
|
||||
### 索引的使用条件 🔥🔥
|
||||
|
||||
- 对于非常小的表,大部分情况下简单的全表扫描比建立索引更高效;
|
||||
- 对于中到大型的表,索引就非常有效;
|
||||
@@ -164,7 +164,7 @@ MEMORY 引擎支持哈希索引,InnoDB 不能显式创建哈希索引,但有
|
||||
- 已经有联合索引 (a, b) 时,单独的索引 (a) 是冗余的,可以删掉;
|
||||
- 主键用较短的自增整数,原因见上文的 [B+Tree 索引](#1-btree-索引)。
|
||||
|
||||
### 索引优化
|
||||
### 索引优化 🔥
|
||||
|
||||
#### 1. 独立的列
|
||||
|
||||
@@ -205,7 +205,7 @@ MySQL 8.0.13 起,缺少最左列时还可能使用跳跃扫描(Skip Scan)
|
||||
|
||||
索引的选择性是指:不重复的索引值和记录总数的比值。最大值为 1,此时每个记录都有唯一的索引与其对应。选择性越高,每个记录的区分度越高,查询效率也越高。
|
||||
|
||||
需要注意的是,这个规则的前提是这些列都以等值条件出现。范围条件之后的列不能再用来缩小扫描的范围,所以经常用于范围查询的列(例如时间)应该放在联合索引的后面。
|
||||
需要注意的是,这个规则的前提是这些列都以等值条件出现。范围条件之后的列一般不能再用来缩小扫描的范围(范围条件带等号时的例外见[索引失效](#索引失效)),所以经常用于范围查询的列(例如时间)应该放在联合索引的后面。
|
||||
|
||||
例如下面显示的结果中 customer_id 的选择性比 staff_id 更高,因此最好把 customer_id 列放在多列索引的前面。
|
||||
|
||||
@@ -246,7 +246,7 @@ SELECT user_id, status, create_time, id FROM orders WHERE user_id = 1001;
|
||||
|
||||
它的优点是只读取索引,读取的数据量小,并且避免了回表带来的随机 I/O。
|
||||
|
||||
### 索引失效
|
||||
### 索引失效 🔥
|
||||
|
||||
索引失效指的是查询条件中有索引列,执行计划却没有使用这个索引,或者只用到了联合索引的一部分。下面是在一张 200 万行的订单表 orders 上用 Explain 查看的结果(MySQL 8.0,见[附录 A2](#a2-索引的执行计划)),表上有联合索引 idx_user_status_time(user_id, status, create_time)、单列索引 idx_create_time(create_time) 和 idx_order_no(order_no),其中 order_no 是 VARCHAR 类型:
|
||||
|
||||
@@ -270,7 +270,7 @@ SELECT user_id, status, create_time, id FROM orders WHERE user_id = 1001;
|
||||
|
||||
IS NULL、IS NOT NULL、NOT IN 本身不会让索引失效:order_no IS NULL 的 type 为 ref;NOT IN 和 != 一样,走不走索引取决于匹配的行数(见[附录 A6](#a6-联合索引的扫描范围))。
|
||||
|
||||
联合索引中出现范围条件时,范围条件之后的列不能再用来缩小扫描的范围。Explain 的 key_len 表示用到了索引的前几列,user_id 为 INT,占 4 字节,status 为 TINYINT,占 1 字节,create_time 为 DATETIME,占 5 字节:
|
||||
联合索引中出现范围条件时,范围条件之后的列一般不能再用来缩小扫描的范围,范围条件带等号时的例外见下文。Explain 的 key_len 表示用到了索引的前几列,user_id 为 INT,占 4 字节,status 为 TINYINT,占 1 字节,create_time 为 DATETIME,占 5 字节:
|
||||
|
||||
```sql
|
||||
WHERE user_id = 5 AND status = 1 AND create_time > '2025-01-05' -- key_len = 10,三列都用上了
|
||||
@@ -279,13 +279,15 @@ WHERE user_id = 5 AND status > 0 AND create_time > '2025-01-05' -- key_len = 5
|
||||
|
||||
不过第二条语句的 Extra 为 Using index condition,也就是索引下推:create_time 虽然不能缩小扫描范围,但仍然在索引上过滤,不满足条件的行不需要回表,所以范围条件之后的列并不是完全没有作用。
|
||||
|
||||
<div align="center"> <img src="pics/index-leftmost.png" width="760px"/> </div><br>
|
||||
|
||||
另外,范围条件带等号(>=、<=、BETWEEN)时,后面的列还能参与确定扫描的起点和终点。例如 user_id BETWEEN 1001 AND 1006 AND status = 2 的 key_len 为 5,关闭索引下推后读了 100 个索引项;改为 user_id > 1000 AND user_id < 1007 后 key_len 为 4,要读 120 个(见[附录 A6](#a6-联合索引的扫描范围))。起点和终点之间的索引项仍然要逐条判断 status。
|
||||
|
||||
例如 `user_id = 1001 AND create_time >= '2025-01-20'` 跳过了 status,只能用 user_id 定位(key_len = 4),user_id 为 1001 的 20 行中只有 3 行满足时间条件。有索引下推时只需回表 3 次,关闭索引下推后要回表 20 次,再由 Server 层过滤。
|
||||
|
||||
需要注意的是,是否使用索引是优化器按成本估算决定的。走二级索引的每一行都要回表,匹配的行越多回表越多,超过一定比例后全表扫描反而更快:`create_time > '2025-01-23'` 匹配 9.9 万行,走 idx_create_time;`create_time > '2025-01-02'` 匹配 191 万行,就选择了全表扫描。IN 也一样,`user_id IN` 5 个值、1 万个值时都走索引,2 万个值时就改为全表扫描。因此同一种写法有时走索引有时不走,可能是因为匹配的数据量不同。
|
||||
|
||||
## 二、查询性能优化
|
||||
## 二、查询性能优化 🔥🔥
|
||||
|
||||
### 一条 SQL 的执行过程
|
||||
|
||||
@@ -573,7 +575,7 @@ MySQL 提供了 FROM_UNIXTIME() 函数把 UNIX 时间戳转换为日期,并提
|
||||
|
||||
TIMESTAMP 比 DATETIME 少占用 1 字节,但是只能表示到 2038 年,需要表示更大的时间范围时应当使用 DATETIME。
|
||||
|
||||
## 五、切分
|
||||
## 五、切分 🔥
|
||||
|
||||
### 水平切分
|
||||
|
||||
@@ -678,7 +680,7 @@ MySQL 默认是异步复制,主服务器提交事务后不等从服务器,
|
||||
- 使用半同步复制:至少一个从库把 binlog 写入中继日志后,主库才向客户端返回提交成功,减少主库宕机时丢失的数据,但并不保证从库已经执行完这些修改;
|
||||
- 监控从库的延迟(SHOW REPLICA STATUS 中的 Seconds_Behind_Source),延迟过大的从库暂时不分配读请求。
|
||||
|
||||
## 七、日志
|
||||
## 七、日志 🔥
|
||||
|
||||
InnoDB 执行一次修改时,会写下面三种日志:
|
||||
|
||||
@@ -739,7 +741,7 @@ redo log 与 binlog 的区别:
|
||||
|
||||
崩溃重启时,对于处于 prepare 状态的事务,到 binlog 中查找它的 Xid:找到了说明 binlog 已经写完,就提交这个事务;找不到就回滚。这样主库的数据与 binlog 总是一致的,从库按照 binlog 复制,也就与主库一致。
|
||||
|
||||
innodb_flush_log_at_trx_commit 和 sync_binlog 的默认值都是 1,也就是每次提交时两种日志都会刷盘,常被称为“双 1”配置。
|
||||
innodb_flush_log_at_trx_commit 和 sync_binlog 的默认值都是 1,也就是每次提交时两种日志都会刷盘,常被称为“双 1”配置。sync_binlog 为 0 时由操作系统决定何时把 binlog 刷盘,为 N 时每 N 组提交刷盘一次;机器断电时可能丢失还没有刷盘的 binlog,从库和用 binlog 恢复的数据会缺少这些事务。
|
||||
|
||||
### 更新语句的执行过程
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 197 KiB |
Reference in New Issue
Block a user