Skip to content

Commit 5a9a584

Browse files
committed
docs:完善多篇文章内容(MySQL索引失效/Redis持久化/RabbitMQ面试题/LinkedHashMap源码)
1 parent df19c6a commit 5a9a584

File tree

5 files changed

+189
-73
lines changed

5 files changed

+189
-73
lines changed

docs/database/mysql/mysql-index-invalidation.md

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,12 @@ head:
1919

2020
### SELECT \* 查询(成本权衡)
2121

22-
- **核心定义**`SELECT *` 本身**不会直接导致索引失效**。它是一种“非覆盖索引”查询,如果 `WHERE` 条件命中了索引,索引依然会被初步考虑。
23-
- **回表成本决策**:当查询需要的字段不在索引树中时,MySQL 必须拿着主键回聚簇索引查找整行数据(回表)。优化器会对比“索引扫描 + 回表”与“直接全表扫描”的成本。如果查询结果占总数据量的比例较高(通常阈值在 20%~30%),优化器会认为全表扫描的顺序 IO 效率高于回表的随机 IO,从而**主动放弃索引**
24-
- **落地建议**:严禁在生产环境无脑使用 `SELECT *`。应遵循**覆盖索引**原则,只查询必要的字段,将 `Extra` 列从空值优化为 `Using index`,从而彻底规避回表开销。
25-
26-
**注意**:后文使用 `SELECT *` 仅仅是为了演示方便。
22+
- **核心定义**`SELECT *` 本身**不会直接导致索引失效**。它是一种”非覆盖索引”查询,如果 `WHERE` 条件命中了索引,索引依然会被初步考虑。
23+
- **回表成本决策**:当查询需要的字段不在索引树中时,MySQL 必须拿着主键回聚簇索引查找整行数据(回表)。优化器会对比”索引扫描 + 回表”与”直接全表扫描”的成本。如果查询结果占总数据量的比例较高(通常阈值在 20%~30%),优化器会认为全表扫描的顺序 IO 效率高于回表的随机 IO,从而**主动放弃索引**
24+
- **场景权衡**
25+
- **覆盖索引场景**:如果查询只需索引覆盖的字段,使用覆盖索引可以避免回表,性能最优。
26+
- **回表不可避免时**:如果业务确实需要多个非索引字段,直接 `SELECT 需要的字段` 即可。当需要大部分字段时,代码可读性可能比”省几个字段”的微优化更重要,此时用 `SELECT *` 也无妨。
27+
- **落地建议**:优先 `SELECT 需要的字段`,能覆盖索引最好;如果需要大量字段且回表不可避免,不必教条地”省字段”。
2728

2829
### 违背最左前缀原则
2930

@@ -190,16 +191,20 @@ SELECT * FROM students WHERE s_code NOT IN (1, 2, 3); -- 常量列表,全
190191

191192
**2. 优化器的成本决策(基于 I/O 成本妥协)**
192193

193-
此类问题并非索引本身不可用,而是 MySQL 优化器经过计算后,认为不走普通索引”整体开销反而更小。
194+
此类问题并非索引本身不可用,而是 MySQL 优化器经过计算后,认为不走普通索引”整体开销反而更小**需要特别说明的是:优化器选择全表扫描或回表查询,往往是正确的成本决策,而非”性能问题”**
194195

195-
- **无脑 `SELECT \*` 导致回表成本超载**:查询大量非索引覆盖列时,若命中数据量较大(通常超 20%~30%),优化器会判定全表扫描的顺序 I/O 优于频繁回表的随机 I/O,从而主动放弃索引。
196+
- **回表查询是正常现象**:当查询需要非索引覆盖的字段时,回表是不可避免的正常操作。索引过滤 + 回表获取业务字段是标准查询模式,并非”性能不佳”的表现。只有当回表次数过多(如命中数据量超过 20%~30%)且存在更优的全表扫描方案时,才需要关注。
197+
- **全表扫描可能是最优选择**:优化器选择全表扫描通常是基于成本计算的理性决策。当索引选择率低(命中数据量大)时,顺序 IO 的全表扫描往往比随机 IO 的索引回表更高效。这不是索引”失效”,而是优化器选择了更优的执行路径。
198+
- **`SELECT *` 的场景权衡**:优先 `SELECT 需要的字段`,能命中覆盖索引最好。如果需要大量非索引字段且回表不可避免,不必教条地"省字段"——当需要大部分字段时,代码可读性可能比"少传几个字段"的微优化更重要。
196199
- **`OR` 条件导致全表扫描**:只要 `OR` 连接的任意一侧条件没有对应索引,就会触发全表扫描。即使两侧都有索引,若 Index Merge(索引合并)的预期成本过高,依然会被放弃。
197200
- **`IN` 列表过长引发估算失真**:当 `IN` 列表长度超过系统阈值(默认 200)时,优化器会从精准的深入探测(Index Dive)切换为粗略的统计估算,极易因统计信息陈旧而产生执行成本的误判。
198201

199202
**实战建议**
200203

201-
1. **养成 `EXPLAIN` 分析习惯**:在编写复杂 SQL 后,务必使用 `EXPLAIN` 分析执行计划,重点关注 `type``key``rows``Extra` 字段。
202-
2. **遵循覆盖索引原则**:尽量避免 `SELECT *`,只查询必要字段,让索引覆盖查询需求,减少回表开销。
204+
1. **养成 `EXPLAIN` 分析习惯**:在编写复杂 SQL 后,务必使用 `EXPLAIN` 分析执行计划,重点关注 `type``key``rows``Extra` 字段。**注意**`type: ALL` 不一定是问题,可能是优化器的正确决策。
205+
2. **根据场景选择查询策略**
206+
- 如果查询字段能被索引覆盖,优先使用覆盖索引避免回表
207+
- 如果必须获取多个非索引字段,避免为了"省字段"而拆分多次查询,减少网络往返
203208
3. **规范数据类型使用**:保持查询条件与字段类型一致,避免隐式类型转换。
204209
4. **合理设计联合索引**:按照查询频率和选择性安排字段顺序,优先满足高频查询场景。
205210
5. **大规模模糊搜索考虑 ES**:对于前后模糊查询(`%keyword%`),建议使用 Elasticsearch 等搜索引擎。

docs/database/redis/redis-persistence.md

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -296,9 +296,19 @@ Redis 7.0 版本之后,AOF 重写机制得到了优化改进。下面这段内
296296
297297
**相关 issue**[Redis AOF 重写描述不准确 #1439](https://github.com/Snailclimb/JavaGuide/issues/1439)
298298

299-
### AOF 校验机制了解吗
299+
### AOF 文件如何验证数据完整性
300300

301-
纯 AOF 模式下,Redis 不会对整个 AOF 文件使用校验和(如 CRC64),而是通过逐条解析文件中的命令来验证文件的有效性。如果解析过程中发现语法错误(如命令不完整、格式错误),Redis 会终止加载并报错,从而避免错误数据载入内存。
301+
**核心结论**:纯 AOF 文件**没有**校验和机制,仅通过逐条命令解析验证;CRC64 校验和仅存在于混合持久化文件的 **RDB 部分**
302+
303+
#### 纯 AOF 模式:无校验和,仅语法解析
304+
305+
纯 AOF 文件不会对整体或单条命令计算 CRC64 校验和,而是通过逐条解析文件中的命令来验证有效性。
306+
307+
**为什么没有校验和?**
308+
309+
AOF 是高频追加写入的文本日志。如果每次追加命令都要重新计算整个文件的 CRC64 校验和,会对主线程的 CPU 和磁盘 I/O 造成严重拖累。因此 Redis 选择了更轻量的方式:重启加载时逐条读取并解析命令语法。
310+
311+
如果解析过程中发现语法错误(如命令不完整、格式错误),Redis 会终止加载并报错。
302312

303313
> **尾部截断容灾(自动恢复)**
304314
>
@@ -327,31 +337,46 @@ Redis 7.0 版本之后,AOF 重写机制得到了优化改进。下面这段内
327337
- **检测阶段**:根据 AOF 文件格式逐一读取命令,判断命令参数个数、参数字符串长度等,提供错误/不完整命令的文件位置
328338
- **修复阶段**:从错误位置截断后续文件内容(**注意:会丢失截断点之后的所有数据**),原文件会被备份为 `appendonly.aof.broken`
329339
330-
**人工修补**(高级用户):
340+
#### 混合持久化模式:分段校验策略
331341
332-
- 如果不想通过截断来修复 AOF 文件,可以尝试人工修补
333-
- 使用文本编辑器打开 AOF 文件(纯文本格式),手动删除或修复错误命令
334-
- 适用于明确知道错误位置的特定场景
342+
**混合持久化模式**(Redis 4.0 引入)下,AOF 文件采用"分段治理"的校验策略:
335343
336-
**混合持久化模式**(Redis 4.0 引入)下,AOF 文件由两部分组成:
344+
```
345+
┌─────────────────────────────────────────────────────────┐
346+
│ 混合持久化文件结构 │
347+
├─────────────────────────────────────────────────────────┤
348+
│ RDB 快照部分(二进制) ← CRC64 校验和保护这部分 │
349+
│ ├── "REDIS" 头部 │
350+
│ ├── 数据库编号、键值对... │
351+
│ ├── EOF 标志 │
352+
│ └── CRC64 校验和(8 字节) ← 校验边界在这里 │
353+
├─────────────────────────────────────────────────────────┤
354+
│ AOF 增量部分(文本) ← 无校验和,仅语法解析 │
355+
│ ├── *3\r\n$3\r\nSET\r\n... │
356+
│ └── ... │
357+
└─────────────────────────────────────────────────────────┘
358+
```
359+
360+
- **RDB 快照部分**:以固定的 `REDIS` 字符开头,存储某一时刻的内存数据快照,并在快照数据末尾附带一个 CRC64 校验和。这个校验和**严格卡在 RDB 数据块的末尾**,仅保障这部分二进制快照的完整性。
361+
- **AOF 增量部分**:紧随 RDB 快照之后,记录增量写命令。这部分**依然没有校验和**,采用与纯 AOF 相同的逐条语法解析验证。
362+
363+
**加载时的校验流程**
337364

338-
- **RDB 快照部分**:文件以固定的 `REDIS` 字符开头,存储某一时刻的内存数据快照,并在快照数据末尾附带一个 CRC64 校验和(位于 RDB 数据块尾部、AOF 增量部分之前)
339-
- **AOF 增量部分**:紧随 RDB 快照部分之后,记录 RDB 快照生成后的增量写命令。这部分增量命令以 Redis 协议格式逐条记录,无整体或全局校验和
365+
1. Redis 首先校验 RDB 快照部分:计算该部分数据的 CRC64 校验和,与存储的校验和值比较。如果不匹配,Redis 拒绝启动
366+
2. RDB 部分校验通过后,逐条解析 AOF 增量命令。解析出错则停止加载后续命令(但此时 RDB 快照数据已成功加载)
340367

341-
RDB 文件结构的核心部分如下:
368+
#### 配置项说明
342369

343-
| **字段** | **解释** |
344-
| ----------------- | ---------------------------------------------- |
345-
| `"REDIS"` | 固定以该字符串开始 |
346-
| `RDB_VERSION` | RDB 文件的版本号 |
347-
| `DB_NUM` | Redis 数据库编号,指明数据需要存放到哪个数据库 |
348-
| `KEY_VALUE_PAIRS` | Redis 中具体键值对的存储 |
349-
| `EOF` | RDB 文件结束标志 |
350-
| `CHECK_SUM` | 8 字节确保 RDB 完整性的校验和 |
370+
| 配置项 | 作用域 | 说明 |
371+
| -------------------- | -------------------------------------- | -------------------------------------------------- |
372+
| `rdbchecksum` | RDB 文件、混合持久化的 RDB 部分 | 控制是否计算 CRC64 校验和,对纯 AOF 增量部分不生效 |
373+
| `aof-load-truncated` | 纯 AOF 文件、混合持久化的 AOF 增量部分 | 控制尾部截断时是否自动丢弃并继续启动 |
351374

352-
Redis 启动并加载 AOF 文件时,首先会校验文件开头 RDB 快照部分的数据完整性,即计算该部分数据的 CRC64 校验和,并与紧随 RDB 数据之后、AOF 增量部分之前存储的 CRC64 校验和值进行比较。如果 CRC64 校验和不匹配,Redis 将拒绝启动并报告错误。
375+
**人工修补**(高级用户):
353376

354-
RDB 部分校验通过后,Redis 随后逐条解析 AOF 部分的增量命令。如果解析过程中出现错误(如不完整的命令或格式错误),Redis 会停止继续加载后续命令,并报告错误,但此时 Redis 已经成功加载了 RDB 快照部分的数据。
377+
- 如果不想通过截断来修复 AOF 文件,可以尝试人工修补
378+
- 使用文本编辑器打开 AOF 文件(纯文本格式),手动删除或修复错误命令
379+
- 适用于明确知道错误位置的特定场景
355380

356381
## 新版本优化
357382

0 commit comments

Comments
 (0)