Mysql的面经

 


一、三大范式(规范化)

1. 第一范式(1NF)

  1. 定义:要求数据库表的每一列都是原子的,即列中的值不能再分割成更小的部分。

  2. 示例

    1. ❌ 错误设计:表中存储多个电话号码的字段,如 phone_numbers(例如 123-456, 789-012)。

    2. ✅ 正确设计:每个电话号码都应该是单独的一行,如创建一个 phone_number 表,包含 user_idphone_number 两列。

2. 第二范式(2NF)

  1. 定义:在满足第一范式的基础上,要求每个非主键字段都必须完全依赖于主键。即,不能存在部分依赖。

  2. 示例

    1. ❌ 错误设计:假设一个表包含 order_id, product_id, product_name,其中 product_name 依赖于 product_id,而不是整个 order_idproduct_id 的组合主键。

    2. ✅ 正确设计:将 product_name 移至另一个表中,设计为 product 表,包含 product_idproduct_name,并通过外键引用。

3. 第三范式(3NF)

  1. 定义:在满足第二范式的基础上,要求所有非主键字段必须直接依赖于主键,不能存在传递依赖。

  2. 示例

    1. ❌ 错误设计:一个表中存储 order_id, product_id, product_price,其中 product_price 依赖于 product_id,而 product_id 又依赖于 order_id

    2. ✅ 正确设计:将 product_price 移至 product 表,通过外键关系来引用,避免传递依赖。

  3. 总结

    • 遵守三大范式的好处:

      • 提高数据的一致性和准确性。

      • 减少冗余数据和不一致的更新。

    • 然而,在实际应用中,为了性能和可扩展性,可能会适度违反范式(如使用适当的冗余字段、使用反范式等)。


二、数据库设计常见坑(优化版)

在数据库设计中,主键和外键的设计至关重要,尤其是它们的不可变性,直接影响数据一致性和系统稳定性。以下是优化后的总结,重点强调逻辑外键也应尽量不可变,并给出更贴合实际业务的建议。

1. 主键设计不当

  • 错误做法
    • 使用业务字段(如手机号、身份证号、邮箱)作为主键。
    • 主键设计为可变字段(如用户 ID 可修改)。

  • 🔍 问题
    • 主键是数据的唯一标识,如果主键可变,会导致关联数据失效(如订单、账单等依赖主键的表无法正确关联)。
    • 业务字段可能变更(如用户换号、改邮箱),导致外键关联断裂,数据关系混乱。
    • 索引重建成本高,主键变更可能导致索引失效,影响查询性能。

  • 推荐做法
    • 主键应选择不可变字段,推荐使用自增 ID 或 分布式唯一 ID(如雪花算法)。
    • 业务字段(如手机号、邮箱)不应作为主键,应作为普通字段存储。
    • 如果必须用业务字段(如用户 ID),确保其不可变性(如用户注册后不允许修改)。

2. 缺失或滥用外键

  • 错误做法
    • 滥用物理外键:所有表之间都加外键约束(影响写入性能、加重锁竞争,难以扩展)。
    • 完全不用外键:依赖业务代码保证数据一致性,容易出错。

  • 🔍 问题
    物理外键

    • 影响写入性能(每次更新需检查外键约束)。

    • 加重锁竞争(高并发时可能死锁)。

    • 难以扩展(分库分表时外键难以维护)。
      无外键

    • 数据一致性依赖业务代码,容易因程序 bug 导致脏数据。

  • 推荐做法
    • 核心数据或关系型平台(如订单、用户关系)可以使用物理外键,确保数据一致性。
    • 非核心或高性能场景(如日志表、统计表)建议使用逻辑外键,由业务代码层保障数据一致性。
    • 逻辑外键也应尽量不可变(如 user_id 不应随意修改)。

3. 字段命名与结构不规范

  • 错误做法
    • 命名含糊(如 statustypeflag 没有文档解释)。
    • 一个字段承载多个意义(如 extra_info 存储 JSON 数据,但未明确字段含义)。

  • 🔍 问题
    • 维护困难:后续开发人员难以理解字段含义。
    • 查询效率低:JSON 字段无法高效索引,导致查询变慢。

  • 推荐做法
    • 命名具备上下文(如 order_statusvehicle_type_code)。
    • 结构化字段优于 JSON,除非是灵活的扩展字段(如用户自定义标签)。
    • 如果必须用 JSON,应明确文档说明字段含义,并考虑使用 JSON Schema 校验数据格式。

4. 表设计不支持“历史数据归档”

  • 错误做法
    • 直接物理删除历史数据(如删除用户订单记录)。

  • 推荐做法
    • 软删:设计 is_deleted 字段标记删除状态。
    • 历史表:重要数据归档到历史表(如 orders_archive)。
    • 操作日志:记录关键操作(如 audit_log 表)。

5. 忽视查询效率和索引设计

  • 错误做法
    • 没有设计联合索引,仅靠单列索引。
    • 频繁使用 %LIKE% 查询,导致无法使用索引。

  • 推荐做法
    • 覆盖索引:确保最常用的查询路径能命中索引(避免回表)。
    • 分析执行计划(EXPLAIN)优化慢查询。
    • 避免 %LIKE% 查询,改用全文索引(如 FULLTEXT)。

6. 不考虑并发写和事务一致性

  • 错误做法
    • 高并发场景下直接统计字段总和(如用户余额),不加事务或乐观锁。

  • 推荐做法
    • 核心资产类字段(如余额、库存)加锁控制(乐观锁/悲观锁)。
    • 事件日志 + 定时汇总:避免频繁实时计算(如电商订单统计)。

7. 数据冗余设计不合理

  • 错误做法
    • 重复存储太多字段(如订单表存客户手机号、姓名,而用户表已有)。

  • 推荐做法
    • 少量冗余字段可接受,但需有更新策略(如用户改名时同步更新订单表)。
    • 或者通过关联查询获取数据,而非冗余存储。

8. 忽略数据迁移和版本演进

  • 错误做法
    • 一次性上线数据库结构,不考虑后期扩展。

  • 推荐做法
    • 保留扩展字段(如 ext_info JSON)。
    • 表结构版本化,便于未来变更(如 v1_ordersv2_orders)。
    • 使用 Flyway/Liquibase 等工具管理数据库版本。


总结(重点强调逻辑外键不可变性)

坑点错误做法推荐做法
主键设计用业务字段(手机号)作主键自增 ID / 雪花算法
外键设计滥用物理外键核心业务用物理外键,非核心用逻辑外键
字段命名模糊命名(status)明确命名(is_active)
历史数据直接物理删除软删 + 历史表
索引设计无索引 / %LIKE%覆盖索引 + EXPLAIN 优化
并发控制无锁更新乐观锁 / 悲观锁
数据冗余过度冗余少量冗余 + 更新策略
扩展性无版本控制JSON 扩展 + 表版本化

✅ 主键和外键(包括逻辑外键)应尽量不可变,避免因字段变更导致数据混乱。
✅ 物理外键适用于核心业务,逻辑外键适用于高性能场景,但都应保证关联字段稳定。


三、MySQL 索引机制

  1. 索引分类

    • 按数据结构分类

      • B+ 树索引:最常用的索引类型,InnoDB 和 MyISAM 均采用 B+ 树结构,支持范围查询和排序。

      • 哈希索引:仅支持等值查询,不支持范围查询,适用于精确匹配的场景。MyISAM 提供此类型,InnoDB 默认不使用。

      • R 树索引:主要用于空间数据类型(如地理位置),常用于地理信息系统(GIS)中。

      • 全文索引:用于支持全文搜索的特殊索引,主要用于文本字段的内容搜索。

    • 按存储方式分类

      • 聚簇索引:InnoDB 中的主键索引属于聚簇索引,数据存储和索引结构融合在一起,叶子节点直接存储数据行。

      • 非聚簇索引:数据存储与索引结构分离,叶子节点存储的是数据的地址(如行号、主键值等),查询时需要回表获取数据。

    • 按列数分类

      • 单列索引:只有一个列的索引,通常用于对单个字段进行快速查找。

      • 联合索引(组合索引):由多个列组成的索引,支持多个字段的联合查询。遵循最左前缀原则。

    • 按约束分类

      • 唯一索引:值必须唯一,不允许重复,通常用于数据完整性约束(如邮箱、身份证号等)。

      • 普通索引:常规的索引,没有唯一性要求,通常用于提高查询效率。

      • 全文索引:专门用于全文检索,支持对文本字段的高效检索。

    • 其他特殊索引类型

      • 空间索引(Spatial Index):专为地理数据设计,支持二维空间数据的快速查询。

      • 位图索引(Bitmap Index):通常用于列取值数量较少(低基数)的情况,如性别(男、女)字段。

      • 反向索引(Reverse Index):在一些特殊场景下(如处理 DNS 查询),采用反向索引。

  2. InnoDB vs MyISAM 索引结构对比

特性InnoDBMyISAM
主键索引聚簇索引(叶子节点存数据)非聚簇(叶子节点存地址)
二级索引非聚簇,叶子节点存主键值非聚簇,叶子节点存地址
是否支持事务✅ 是❌ 否
锁粒度行级锁表级锁
  1. 无主键时的特殊处理

    • InnoDB 会自动生成一个隐藏的 row_id 作为聚簇索引主键。

  2. 索引优缺点

    • 优点:加快查询速度、减少 I/O、支持排序/分组优化。

    • 缺点:占用空间、插入更新时维护开销大。

  3. 可能导致索引失效的场景

    • LIKE '%abc'

    • 字段隐式转换(如 int = '123'

    • 使用函数包裹字段:WHERE YEAR(time) = 2023

    • 违反最左前缀原则(联合索引)

  4. 覆盖索引(索引覆盖查询)

    • 查询字段全部被索引覆盖,无需回表,可极大提升查询效率。

  5. 索引下推(Index Condition Pushdown, ICP)

    • 默认索引查找后需“回表”判断 WHERE 条件是否满足,ICP 将部分 WHERE 条件下推至存储引擎层的索引扫描阶段。

    • 优势:减少回表次数,提高查询效率。

    • 使用条件:MySQL 5.6+ 且查询条件能被索引匹配。

  6. 联合索引(组合索引)

    • 遵循最左前缀原则。

    • 索引 (a, b, c) 支持以下组合:aa,ba,b,c,但不支持 b,c

    • 字段顺序不同代表不同索引结构。

  7. 聚簇 vs 非聚簇索引实现差异

    • 聚簇索引(InnoDB 主键):B+ 树叶子节点存储整行数据。

    • 非聚簇索引

      • InnoDB 叶子节点存主键 → 再通过主键回表

      • MyISAM 叶子节点存数据地址

  8. 其他重要概念

    • 唯一索引:值不可重复,用于唯一约束。

    • 哈希索引:仅适合等值查询,不支持范围。

    • 索引下推 ICP:WHERE 条件提前在索引阶段过滤。

    • 索引排序ORDER BY 字段必须满足最左匹配才能用索引排序。

    • GROUP BY/ORDER BY:可配合联合索引使用,提高分组/排序效率。


四、MySQL 锁机制详解笔记

本笔记系统整理了 MySQL 中锁的分类、属性与底层机制,尤其聚焦于 InnoDB 引擎的行级锁实现细节,便于开发者理解并合理运用锁机制避免并发冲突和性能问题。

  1. 按锁粒度分类

    锁类型说明
    全局锁锁定整个数据库实例,如 FLUSH TABLES WITH READ LOCK,常用于全库备份
    表级锁锁定整张表,如 LOCK TABLES ... READ/WRITE,MyISAM 中广泛使用,InnoDB 特殊情况使用(如 DDL)
    行级锁锁定表中的单行数据,仅 InnoDB 支持,粒度最小、并发性最好
  2. 按使用模式分类

    锁类型场景说明
    乐观锁(Optimistic)适合多读少写,如版本号控制:UPDATE ... WHERE id=? AND version=?
    悲观锁(Pessimistic)假设冲突频繁,访问数据前加锁,如:SELECT ... FOR UPDATE
  3. 按锁属性分类(InnoDB 行锁类型)

    锁类型简写说明
    共享锁S 锁允许多个事务同时加锁,只能读不能写。如:SELECT ... LOCK IN SHARE MODE
    排他锁X 锁仅允许一个事务持有,其他事务不能读也不能写。如:SELECT ... FOR UPDATE
    意向共享锁IS表示事务准备对表中某些记录加 S 锁,由 InnoDB 自动加在表级,用于加速锁冲突检测
    意向排他锁IX表示事务准备对表中某些记录加 X 锁,由 InnoDB 自动加在表级

    锁兼容性说明

    当前持有 \ 请求S 锁X 锁IS 锁IX 锁
    S 锁
    X 锁
    IS 锁
    IX 锁
  4. 按底层实现算法分类(InnoDB 特有)

    锁类型说明
    记录锁锁住索引上的某条记录,仅作用于主键或唯一索引上的行
    间隙锁锁住两个索引记录之间的“空隙”,防止幻读
    临键锁记录锁 + 间隙锁的组合。在默认 RR 隔离级别下,InnoDB 使用它防止幻读
  5. 典型使用场景与注意事项

    • 共享锁(S 锁):多个事务可以并发读取,提高并发能力,但不能更新该记录。

    • 排他锁(X 锁):适用于更新或删除场景,确保操作期间数据不被他人读取或修改。

    • IS/IX 锁:属于表级锁,不会直接阻塞行访问,但用于快速判断是否存在冲突,提高加锁效率。

    • 记录锁:最常见的行锁形式,用于锁住主键或唯一索引的具体记录。

    • 间隙锁:用于防止“幻读”问题,但也可能造成“锁定未命中记录”导致误阻塞。

    • 临键锁:默认行为,结合间隙锁 + 记录锁,兼顾安全与一致性。

  6. 锁机制常见问题与优化建议

    • 锁升级风险:避免使用不带索引的 WHERE 条件,否则可能锁全表。

    • 死锁处理:InnoDB 自动检测并回滚“牺牲”事务,建议合理控制加锁顺序与范围。

    • 事务隔离级别调整:在允许的业务条件下,可降低为 READ COMMITTED,减少间隙锁干扰。

    • 分析工具:使用 SHOW ENGINE INNODB STATUS 查看死锁日志,定位冲突根因。


五、MySQL 事务机制详解笔记

  1. 事务的四大特性(ACID)

    特性说明
    原子性一组操作要么全部执行成功,要么全部失败。通过 undo log 回滚未完成操作。
    一致性事务执行前后,数据保持一致,如账户转账后两边总金额不变。
    隔离性多个事务并发执行时,互不干扰。通过锁或 MVCC 保证。
    持久性事务提交后对数据库的修改是永久的。通过 redo log 保证写入磁盘。
  2. 事务隔离级别(Isolation Levels)

    隔离级别脏读不可重复读幻读InnoDB 默认
    读未提交(RU)
    读已提交(RC)
    可重复读(RR)
    串行化(SZ)
    • 读未提交(脏读):它的主要意思就是,A线程可以读到B线程没有提交的数据。比如A线程刚开始查询的数据为500,此时B线程更改了数据为800,但是并没有去提交线程,此时A再去查询数据得到为800。如果B线程提交后发生了错误需要回滚为500.此时A拿到的数据800就是一个脏数据。

    • 读已提交(不可重复读):这个隔离级别解决了上个级别的问题,就是A线程读不到B线程没有提交的更改数据的值。也就是B线程如果没有提交的话,A线程读到的还是500,但是B线程提交了后读到的就是800,这样子就会导致,对于A线程来说,他这个事务时间里,两次读到数据的结果不一样。

    • 可重复读(幻读):这个隔离级别就解决了上个级别的问题,他是通过快照的形式,在事务开始时,将此时的数据通过快照的形式保存下来,这个事务的时间段,无论其他线程做了什么操作,我查询数据都是到快照里查询,这样子就解决了不可重复读的问题,但是呢又会导致一个新的问题,就是说对于其他线程来说这个数据可能已经被更改为其他值,但是A线程还是之前原来的值。举个例子来说的话:就付钱的时候,你保存了你之前的钱包500块,在你还没有扣减钱包的时候,其他线程转了你300块,并提交了事务,此时你的钱包应该是800块,但是对于这个支付线程来说,你的钱包还是500块,然后扣减之后在更改数据,就会导致你钱少了,那300块不见了。(也就是幻读,自己线程看见的和其他线程不一样)。

      两种解决方案:一是加乐观锁的方式,通过比对版本号。

      二是悲观锁的方式,就是所有更新操作底层都会加写锁,它会去找最新版本的数据来读。

      比如说update acoount set balance=money where id =1;

      我给他改成update account set balance=balance-200 where id=1;

      这样子的话,在数据更新的时候他就会去找最新版本的balance的值,而不是去快照读。就不会算错钱。

    • 串行化:通过对查询语句增加读锁(共享锁),也就是sql语句后加lock in share mode语句。

  3. 事务隔离级别下的典型行为

    • 读未提交(Read Uncommitted)

      • 事务可以读取其他事务未提交的修改,导致脏读。

    • 读已提交(Read Committed)

      • 每次查询都读取最新已提交数据;避免脏读,但可能产生不可重复读。

    • 可重复读(Repeatable Read)

      • 事务期间快照一致,多次查询返回一致结果;可防止脏读、不可重复读,但可能产生幻读。

    • 串行化(Serializable)

      • 所有操作加锁,强一致性;并发性能差,几乎不用。

  4. 事务并发控制机制:乐观锁 / 悲观锁

    类型说明
    乐观锁基于版本号或时间戳控制,不加数据库锁,适合多读少写场景。
    悲观锁显式加锁,如 SELECT ... FOR UPDATE,适合高并发写入场景。
  5. MVCC(多版本并发控制)【详细实现原理】

    • 作用:在 RC 和 RR 隔离级别下,InnoDB 使用 MVCC 提供非阻塞读(快照读);实现事务的隔离性且避免加锁,提高性能。

    • 底层机制

      • 每行记录包含两个隐藏字段:

        • DB_TRX_ID(事务 ID):最后修改该行的事务编号。

        • DB_ROLL_PTR(回滚指针):指向一条 undo log 的地址,形成回滚链条(版本链)。

      • 事务开始时生成一个快照版本号(Read View)。

    • Undo Log 链结构

      • 插入:生成“反向 DELETE” undo。

      • 更新:旧数据写入 undo log,构建版本链。

      • 删除:生成“反向 INSERT” undo。

      • 查询时沿 undo 链查找对当前事务可见的版本。

    • 不同隔离级别下的快照读取策略

      隔离级别读取版本方式
      RC每次读都生成新的 Read View
      RR整个事务只生成一个 Read View
    • 版本链示意

      当前行
       ├─ trx_id = 100
       ├─ roll_ptr → undo_log_100 (x=500)
           └─ trx_id = 80
           └─ roll_ptr → undo_log_80 (x=400)
               └─ trx_id = 70
      

      若当前事务版本为 75(RR),只能看到 x=400。

  6. 事务日志机制:Undo Log & Redo Log

    日志类型用途作用存储方式
    Undo Log回滚操作实现原子性、MVCC 支持每个事务独立管理
    Redo Log恢复数据实现持久性物理日志,分为 prepare/commit
    • 二阶段提交:undo 与 redo 日志配合提交,保证 ACID。

  7. 事务死锁与检测机制

    • 死锁:多个事务持有锁并等待对方释放,形成循环。

    • InnoDB 处理

      • 自动死锁检测;

      • 回滚“影响最小”事务;

      • innodb_deadlock_detect = ON

    • 排查工具SHOW ENGINE INNODB STATUS 查看最近死锁信息。

    • 避免策略

      • 统一加锁顺序;

      • 控制事务范围,尽快提交;

      • 使用索引避免全表锁;

      • 显式锁定策略(FOR UPDATE, LOCK IN SHARE MODE)。

  8. 是否开启事务的判断

    • 统计报表:需要一致性快照,使用 REPEATABLE READ

    • 日常读写:性能优先,可用 READ COMMITTED

    • 实时扣款:应用悲观锁(FOR UPDATE)或 CAS + 乐观锁。


六、MySQL 深度机制笔记(执行流程 + 存储结构 + 索引原理 + 日志系统 + 分布式扩展)

  1. SQL 查询语句执行全过程(以 InnoDB 为例)

    SELECT * FROM user WHERE id = 1;
    

    执行流程:

    1. Server 层解析与优化

      • SQL 词法/语法解析 → 生成语法树 → 查询计划优化(选择最优索引) → 调用执行器下发执行计划

      • 明白了,你想要了解 SQL 查询执行顺序的补充。确实,SQL 查询的执行过程按照一定的顺序进行,虽然在书写时顺序可能是 SELECTFROMWHEREJOIN 等,但实际执行顺序是有所不同的。具体执行顺序如下:

        SQL 执行顺序(实际执行顺序 vs 书写顺序)

      • FROM 子句

        • 作用:查询首先从 FROM 子句指定的表或数据源开始。这里是基础数据源的选择,首先加载所有相关表的数据(如果有多个表),准备参与后续操作。

        • 优化:如果涉及到多个表的连接(JOIN),此时也会处理表连接的逻辑,确定数据的基础集。

      • JOIN 操作

        • 作用:在 FROM 子句指定的数据源基础上,如果有连接(JOIN),这时就开始执行连接操作。JOIN 操作会基于给定的条件(如 ONUSING)将多个表的数据合并为一个结果集。

        • 优化

          • 连接顺序:在多表连接时,优化器会根据表的大小、索引使用情况等来选择最优的连接顺序。

          • 连接类型:选择适当的连接类型(如内连接 INNER JOIN、左连接 LEFT JOIN 等)可以影响查询的性能。

          • 索引优化:对于连接操作中涉及的字段,合理使用索引可以大幅提升连接效率。

      • WHERE 子句

        • 作用:在连接操作完成后,WHERE 子句会进行数据筛选。此时,查询会根据 WHERE 条件过滤出符合要求的记录,丢弃不符合条件的行。

        • 优化

          • 索引选择:在 WHERE 子句中,如果涉及到大量数据的筛选,使用合适的索引可以减少全表扫描,提高查询性能。

          • 筛选顺序:优化器在执行 WHERE 子句时,会尝试使用最优的筛选顺序,以减少需要扫描的数据量。

      • GROUP BY 子句

        • 作用:如果查询包含 GROUP BY,在 WHERE 子句筛选后的数据会被分组。每一组的数据会执行聚合操作(如 SUM()COUNT()AVG() 等)。

        • 优化

          • 索引优化:如果分组字段有索引,MySQL 可以更高效地进行分组操作。

          • 内存分配:在大数据量的分组时,合理配置内存缓冲区(如 sort_buffer_size)有助于提高性能。

      • HAVING 子句

        • 作用HAVING 子句用于对 GROUP BY 后的结果进行过滤,和 WHERE 子句不同,HAVING 是针对分组后的数据进行条件过滤。

        • 优化:在聚合后的数据上使用 HAVING 条件时,可以减少无效的数据返回。合理使用 HAVING 来优化性能。

      • SELECT 子句

        • 作用:执行查询时,选择要返回的列数据。在此阶段,所有的查询操作(如聚合、连接等)都已完成,MySQL 会根据 SELECT 子句的要求返回结果。

        • 优化

          • 字段选择:只选择需要的字段,避免返回不必要的数据列,减轻内存和 I/O 的压力。

          • 使用索引覆盖:如果查询的字段完全可以由索引返回,使用索引覆盖查询可以减少回表操作,提升性能。

      • ORDER BY 子句

        • 作用ORDER BY 用于对查询结果进行排序。在此阶段,数据已经被筛选并且准备好返回,但仍需要根据排序字段进行排序。

        • 优化

          • 索引排序:如果排序字段有合适的索引,MySQL 可以直接利用索引进行排序,避免额外的内存排序。

          • 限制排序的数据量:对于大型数据集,排序是昂贵的操作,可以结合 LIMIT 来减少排序的数据量,从而提高效率。

      • LIMIT 子句

        • 作用LIMIT 用于限制查询返回的行数,通常与 ORDER BY 一起使用,确保查询结果的分页或按需返回。

        • 优化

          • 优化偏移量:当使用分页查询时,可以通过优化偏移量(如使用 JOIN 或索引优化分页查询)减少查询的复杂度。

    2. 执行器调用存储引擎(InnoDB)

    3. Buffer Pool 缓存检查

      • 命中:直接读取缓存

      • 未命中:从磁盘加载数据页到 Buffer Pool

    4. 更新类操作流程

      1. 写 Undo Log(逻辑日志)→ 保证原子性与 MVCC

      2. 更新 Buffer Pool 中的数据

      3. 写 Redo Log(物理日志)→ 顺序写入日志缓冲区

      4. Prepare 阶段:刷 Redo Log 到磁盘(持久化)

      5. 写 Binlog(Server 层),再写 Commit 标记到 Redo Log

      6. 提交成功(两阶段提交机制)

      7. 最终数据异步刷新到磁盘

    5. 崩溃恢复:若中途宕机,只要 Redo Log 存在提交标记,即可恢复。

  2. 日志系统全景图

    日志类型所属层级作用用于恢复支持复制
    Undo LogInnoDB回滚、MVCC 支持
    Redo LogInnoDB持久化、崩溃恢复
    BinlogServer主从复制、数据恢复
    Relay LogSlave接收主库 binlog 执行
    • Redo Log 刷盘机制

      • 顺序写入 WAL 提升性能

      • innodb_flush_log_at_trx_commit 决定刷盘策略(0/1/2)

    • Binlog 刷盘策略

      • sync_binlog=0:不主动 fsync

      • sync_binlog=1:每次 commit fsync

      • sync_binlog=N:每 N 次 commit 才 fsync

  3. InnoDB 存储结构

    • 表空间(.ibd 文件)

      • 段(Segment)→ 区(Extent,1MB,包含64页)→ 页(Page,16KB)→ 行(Row)

    • 行格式

      格式说明
      Redundant5.0 之前版本,冗余结构,淘汰
      Compact默认格式,变长字段紧凑存储
      Dynamic大字段溢出页存储,节省页内空间
      Compressed页内数据压缩,减少磁盘空间
    • Null 值存储:Compact 格式使用 Null 位图表示哪些字段为 Null

    • VARCHAR 存储:变长字段长度列表记录实际长度(1~2 字节)

    • 行溢出处理

      • 大字段(TEXT/BLOB)保留 20 字节指针,其余存溢出页

  4. InnoDB 与 MyISAM 对比

    特性InnoDBMyISAM
    事务支持✅ 支持(ACID)❌ 不支持事务
    锁粒度行级锁表级锁
    主键类型聚簇索引非聚簇索引
    外键支持✅ 支持❌ 不支持
    崩溃恢复能力强(支持 redo)弱(需工具修复)
    适用场景高并发写、大数据一致性读多写少场景
  5. 索引机制与结构

    • 聚簇索引:数据存储即为主键索引(InnoDB)

    • 二级索引:索引值指向主键,再通过主键回表

    • 哈希索引:哈希值匹配快速查找,不支持范围查询

    • 全文索引:支持文本自然语言处理的匹配

    • 哈希索引局限:哈希冲突,性能下降;不支持范围查找/排序/下推

    • B+ 树 vs B 树

      特性B 树B+ 树(InnoDB)
      数据节点所有节点存数据仅叶子节点存数据
      范围查询✅ 叶子节点链表支持范围查找
      I/O 性能中等✅ 更快范围/排序扫描
    • 联合索引原理

      CREATE INDEX idx_name_age_pos ON user(name, age, position);
      
      • 底层为多字段排序的 B+ 树,遵循最左前缀原则:可命中 (name), (name, age), 但不支持 (age, position)

  6. 主键设计建议(推荐)

    推荐原因说明
    自增整形主键B+ 树插入效率高,维护有序链表开销低
    ❌ UUID无序、占空间大,页分裂频繁,性能差
    ❌ 字符串主键CPU 耗费大,空间占用多
  7. MySQL COUNT 性能对比

    SQL含义性能排序
    COUNT(*)全表记录数(含 NULL)✅ 最快
    COUNT(1)类似于 COUNT(*)✅ 近似
    COUNT(column)统计非 NULL 的指定列❌ 较慢
  8. 分布式数据库设计扩展

    以下整合了雪花算法详解及时间回拨问题,结合分布式数据库实战笔记,格式化清晰,适合导出为 Markdown 或 PDF。

    一、分库分表设计策略

    • 目标:解决单库单表容量瓶颈、提升并发能力与可扩展性。

    • 1️⃣ 水平分库 & 水平分表

      类型说明
      水平分库按主键范围或哈希将数据分散到多个数据库实例
      水平分表在同一库中将大表拆分为多张子表
    • 2️⃣ 常用分片策略

      策略说明示例
      范围分片按 ID/时间范围切分id <1000→db1,≥1000→db2
      哈希分片对主键哈希取模hash(uid)%N
      枚举分片显式配置分片规则用户1–100→db1
      混合分片联合条件切分时间+用户ID
    • 3️⃣ 数据路由实现方式

      • 静态路由:硬编码表/库名,灵活性差

      • 中间件路由:如 ShardingSphere、MyCat

      • ORM 拦截器:如 MyBatis 插件定制

    二、全局唯一 ID 生成策略

    • 目标:多库多表环境下生成全局唯一、趋势递增、高性能主键。

    • 常见方案对比

      策略特点优缺点
      雪花算法(Snowflake)64-bit 自增 ID,含时间戳+机房ID+序列号✅ 唯一性好、趋势递增;❌ 强依赖时钟
      数据库自增表中央库生成自增序列✅ 简单;❌ 单点瓶颈
      Redis INCR使用 Redis INCR 实现✅ 快速;❌ Redis 宕机影响可用
      UUID无状态唯一字符串✅ 无中心依赖;❌ 不可排序、占空间
      美团 Leaf号段模式/Snowflake 服务✅ 性能高、容错好;❌ 实现复杂
    • 雪花算法结构图

      符号位时间戳(41bit)数据中心ID(5bit)机器ID(5bit)序列号(12bit)
      1bit基于时间递增支持32个机房每机房32台机器每毫秒最多4096个ID

    三、雪花算法详解与时间回拨问题

    • 核心优势

      • 高性能:本地生成,避免集中写

      • 唯一性:含时间戳、机房、序号

      • 趋势递增:便于聚簇索引

    • 时间回拨问题

      • 若系统时钟回拨,会导致序列号重复 ➔ 重复 ID。

    • 应对策略

      方案描述优缺点
      等待时钟追平(阻塞)时间回拨时 sleep 直到追平安全但阻塞业务
      设置容忍窗口小于 X ms 容忍回拨简单实用,但仍有窗口
      逻辑时钟(TiDB TSO)每节点维护递增逻辑时间最可靠,复杂度高
      引入版本号高位加一位版本号增加复杂度
      中心服务生成 IDLeaf/UidGenerator 模式强中心依赖,易控
    • Java 示例

      if (currentTimestamp < lastTimestamp) {
          long offset = lastTimestamp - currentTimestamp;
          if (offset < MAX_DRIFT) {
              Thread.sleep(offset);
          } else {
              throw new RuntimeException("Clock moved backwards. Refusing to generate ID");
          }
      }
      

    四、跨库事务一致性处理

    • 问题根源:多数据源无法用单机事务 ACID 保证一致性。

    • 解决方案

      模式描述优缺点
      XA 两阶段提交MySQL 支持 XA 协议,先 prepare 后 commit❌ 性能差、易阻塞
      TCC 模式应用层定义 Try/Confirm/Cancel 接口✅ 最终一致,适合补偿场景
      可靠消息 + 最终一致性MQ + 本地事务完成业务提交✅ 电商支付主流方案

    五、分布式索引设计策略

    • 问题:分片后全局索引缺失,导致跨库排序、聚合、模糊查询效率低。

    • 解决方案

      1. ES 索引同步:将核心字段同步到 Elasticsearch,实现分页、排序、全文检索。

      2. 预聚合 + 缓存:常用统计结果存 Redis,减少跨库查询。

      3. 逻辑索引中台:汇聚多库数据至索引服务层,提供统一查询接口。

    六、Binlog 同步与数据一致性(CDC)

    • 适用场景:多写架构、消息驱动、数据同步。

    • 常见工具对比

      工具用途特点
      Canal解析 MySQL binlog → MQ/ES支持主从同步,广泛使用
      MaxwellJava 编写,轻量级同步适合中小场景
      DebeziumKafka Connect 插件适合 CDC + 流处理平台

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值