[{"data":1,"prerenderedAt":559},["ShallowReactive",2],{"\u002Fjava-interview\u002Fmysql":3,"java-interview-search:\u002Fjava-interview\u002Fmysql":515},{"id":4,"title":5,"body":6,"category":501,"cover":502,"date":503,"description":504,"extension":505,"group":506,"meta":507,"navigation":508,"order":509,"path":510,"seo":511,"sitemap":512,"stem":513,"__hash__":514},"javaInterview\u002Fjava-interview\u002Fmysql.md","MySQL：索引、事务与慢查询优化",{"type":7,"value":8,"toc":488},"minimark",[9,13,95,100,103,106,109,112,116,127,130,152,155,159,174,188,205,208,212,215,218,231,234,238,241,244,247,261,268,312,334,341,351,355,361,364,393,400,403,407,410,413,416,419,422,435,438,441,484],[10,11,12],"p",{},"MySQL 题如果只答「加索引」，很难拿到高分。面试官更想听的是：为什么这条 SQL 用不上索引、这条更新加了什么锁、RR 到底解决了什么又没解决什么。以下默认 InnoDB、MySQL 8.0。",[14,15,16,32],"table",{},[17,18,19],"thead",{},[20,21,22,26,29],"tr",{},[23,24,25],"th",{},"场景",[23,27,28],{},"关键机制",[23,30,31],{},"常见误判",[33,34,35,47,58,73,84],"tbody",{},[20,36,37,41,44],{},[38,39,40],"td",{},"按主键查一行",[38,42,43],{},"聚簇索引，叶子即行数据",[38,45,46],{},"以为二级索引也要回表",[20,48,49,52,55],{},[38,50,51],{},"按普通索引查整行",[38,53,54],{},"二级索引 → 主键 → 回表",[38,56,57],{},"忽略回表成本",[20,59,60,63,66],{},[38,61,62],{},"查询列都在索引里",[38,64,65],{},"覆盖索引，免回表",[38,67,68,69],{},"以为必须 ",[70,71,72],"code",{},"SELECT *",[20,74,75,78,81],{},[38,76,77],{},"范围条件 + 索引过滤",[38,79,80],{},"索引下推（ICP）",[38,82,83],{},"以为过滤一定回表后做",[20,85,86,89,92],{},[38,87,88],{},"高并发读写同一行",[38,90,91],{},"行锁 + 一致性读",[38,93,94],{},"以为 RR 下读都不加锁",[96,97,99],"h2",{"id":98},"_1-innodb-的索引结构是什么聚簇索引和二级索引有什么区别","1. InnoDB 的索引结构是什么？聚簇索引和二级索引有什么区别？",[10,101,102],{},"InnoDB 用 B+ 树组织索引。主键索引是聚簇索引，叶子节点直接存放整行数据，所以按主键查最快；如果没有主键，会选第一个非空唯一索引，再没有就生成隐藏的行 ID。",[10,104,105],{},"二级索引的叶子存放的是索引列加主键值。查整行时需要先查二级索引拿到主键，再回聚簇索引取数据，这个动作叫回表，也是「索引很多但查询仍然慢」的常见原因。",[10,107,108],{},"B+ 树的非叶子节点只存键和指针，扇出大、层数少，通常三到四层就能覆盖千万级数据。页大小默认 16KB，顺序主键插入能减少页分裂，随机 UUID 主键则会带来更多分裂和碎片。",[10,110,111],{},"MyISAM 的索引和数据分离，索引叶子存的是行地址，没有聚簇概念，也不支持事务和行锁。",[96,113,115],{"id":114},"_2-哪些写法会导致索引失效","2. 哪些写法会导致索引失效？",[10,117,118,119,122,123,126],{},"最常见的是在索引列上做函数或运算，例如 ",[70,120,121],{},"WHERE DATE(create_time) = '2026-10-01'","、",[70,124,125],{},"WHERE id + 1 = 10","。应改成范围条件，或建立函数索引（MySQL 8.0.13 起支持）。",[10,128,129],{},"隐式类型转换也会失效：字符串列与数字比较时，MySQL 会把列转成数字，索引无法使用。参数类型要和列定义一致。",[10,131,132,133,136,137,140,141,122,144,147,148,151],{},"其他情况包括：",[70,134,135],{},"LIKE '%关键字'"," 前导通配；复合索引不满足最左前缀；",[70,138,139],{},"OR"," 两侧有一侧没有索引；",[70,142,143],{},"!=",[70,145,146],{},"NOT IN"," 在选择性差时被优化器放弃；对索引列使用 ",[70,149,150],{},"IS NULL"," 并非一定失效，要看数据分布。",[10,153,154],{},"最后要理解「失效」很多时候是优化器的成本选择：当回表行数占比很高，全表扫描反而更便宜，强制走索引未必更快。",[96,156,158],{"id":157},"_3-什么是覆盖索引和索引下推","3. 什么是覆盖索引和索引下推？",[10,160,161,162,165,166,169,170,173],{},"覆盖索引指查询需要的列全部能在索引里拿到，不需要回表。",[70,163,164],{},"EXPLAIN"," 的 ",[70,167,168],{},"Extra"," 会显示 ",[70,171,172],{},"Using index","。",[10,175,176,177,180,181,184,185,187],{},"例如给 ",[70,178,179],{},"(user_id, status, created_at)"," 建联合索引，查询 ",[70,182,183],{},"SELECT status, created_at FROM orders WHERE user_id = ?"," 就能覆盖；如果再加 ",[70,186,72],{},"，就得回表。",[10,189,190,191,194,195,198,199,201,202,173],{},"索引下推（ICP）是在存储引擎层用索引中的列提前过滤，减少回表次数。例如联合索引 ",[70,192,193],{},"(name, age)","，条件 ",[70,196,197],{},"WHERE name LIKE '张%' AND age = 20","，age 可以在索引上先判断，",[70,200,168],{}," 显示 ",[70,203,204],{},"Using index condition",[10,206,207],{},"覆盖索引和索引下推解决的是不同问题：前者省掉回表，后者减少回表。设计联合索引时，把等值条件放前面、范围条件放后面，通常能同时兼顾过滤和排序。",[96,209,211],{"id":210},"_4-mvcc-是怎么实现的rr-能完全解决幻读吗","4. MVCC 是怎么实现的？RR 能完全解决幻读吗？",[10,213,214],{},"InnoDB 每行有隐藏的事务 ID 和回滚指针，回滚指针把旧版本串成 undo 链。读取时根据 ReadView 判断哪个版本可见：当前事务 ID 小于最小活跃事务 ID 的版本可见，大于等于最大事务 ID 的不可见，处于活跃列表中的也不可见。",[10,216,217],{},"RC 每条语句生成一个新的 ReadView，所以能读到别人已提交的修改；RR 在事务第一次快照读时生成，之后复用，因此同一事务内多次查询结果一致。",[10,219,220,221,122,224,122,227,230],{},"但 RR 不能在所有场景下消除幻读：快照读靠一致性视图，当前读（",[70,222,223],{},"SELECT ... FOR UPDATE",[70,225,226],{},"UPDATE",[70,228,229],{},"DELETE","）会读最新版本并加锁。如果先快照读、再当前读，或者两个事务以不同顺序读写，仍可能看到「幻影」。",[10,232,233],{},"间隙锁和 next-key lock 是 RR 下当前读防幻读的手段，代价是更大的锁范围和更高的死锁概率。",[96,235,237],{"id":236},"_5-innodb-有哪些锁间隙锁和-next-key-lock-怎么用","5. InnoDB 有哪些锁？间隙锁和 next-key lock 怎么用？",[10,239,240],{},"按粒度分表锁、行锁和意向锁；按模式分共享锁和排他锁。行锁实际加在索引记录上，所以「没有索引的更新」可能退化成锁全表。",[10,242,243],{},"记录锁锁定单条索引记录；间隙锁锁定两条记录之间的开区间；next-key lock 是记录锁加前面的间隙锁。插入时还会有插入意向锁，用来表示「准备插入这个间隙」，它与间隙锁冲突。",[10,245,246],{},"RC 隔离级别下基本只用记录锁；RR 下范围查询会使用 next-key lock。这也是为什么线上常用 RC：锁范围更小、死锁更少，代价是同一事务内可能出现不可重复读。",[10,248,249,250,165,253,256,257,260],{},"死锁排查看 ",[70,251,252],{},"SHOW ENGINE INNODB STATUS",[70,254,255],{},"LATEST DETECTED DEADLOCK","，MySQL 8 还可以查 ",[70,258,259],{},"performance_schema.data_lock_waits","。降低死锁的关键是短事务、固定加锁顺序、WHERE 条件走索引。",[96,262,264,265,267],{"id":263},"_6-explain-重点看哪些列慢-sql-怎么优化","6. ",[70,266,164],{}," 重点看哪些列？慢 SQL 怎么优化？",[10,269,270,271,274,275,122,278,122,281,122,284,287,288,291,292,295,296,299,300,303,304,307,308,311],{},"先看 ",[70,272,273],{},"type","：",[70,276,277],{},"const",[70,279,280],{},"eq_ref",[70,282,283],{},"ref",[70,285,286],{},"range"," 较好，",[70,289,290],{},"index"," 是全索引扫描，",[70,293,294],{},"ALL"," 是全表扫描。再看 ",[70,297,298],{},"key"," 是否命中预期索引、",[70,301,302],{},"key_len"," 是否符合联合索引的前缀长度、",[70,305,306],{},"rows"," 和 ",[70,309,310],{},"filtered"," 估算的扫描量。",[10,313,314,316,317,319,320,322,323,326,327,307,330,333],{},[70,315,168],{}," 信息量很大：",[70,318,172],{}," 是覆盖索引；",[70,321,204],{}," 是索引下推；",[70,324,325],{},"Using where"," 表示还需要在服务层过滤；",[70,328,329],{},"Using temporary",[70,331,332],{},"Using filesort"," 往往意味着排序或分组没有用上索引。",[10,335,336,337,340],{},"优化顺序通常是：确认 SQL 语义和返回量 → 补或改索引 → 重写查询（避免函数、拆 OR、减少回表）→ 调整分页和批量大小 → 最后才考虑优化器提示。MySQL 8.0.18 起可以用 ",[70,338,339],{},"EXPLAIN ANALYZE"," 看实际执行耗时。",[10,342,343,344,347,348,173],{},"不要迷信 ",[70,345,346],{},"FORCE INDEX","，它会把优化器的判断固化成风险；统计信息不准时先 ",[70,349,350],{},"ANALYZE TABLE",[96,352,354],{"id":353},"_7-深分页为什么慢怎么优化","7. 深分页为什么慢？怎么优化？",[10,356,357,360],{},[70,358,359],{},"LIMIT 1000000, 20"," 需要先扫描并丢弃前 100 万行，再取 20 行，扫描量和偏移量成正比。即使命中索引，也要付出大量回表或遍历成本。",[10,362,363],{},"第一种方案是延迟关联：先在覆盖索引上查出主键，再关联原表取数据。子查询只扫索引，回表只发生在最终 20 行。",[365,366,371],"pre",{"className":367,"code":368,"language":369,"meta":370,"style":370},"language-sql shiki shiki-themes github-dark","SELECT o.*\nFROM orders o\nJOIN (SELECT id FROM orders ORDER BY id LIMIT 1000000, 20) t ON o.id = t.id;\n","sql","",[70,372,373,381,387],{"__ignoreMap":370},[374,375,378],"span",{"class":376,"line":377},"line",1,[374,379,380],{},"SELECT o.*\n",[374,382,384],{"class":376,"line":383},2,[374,385,386],{},"FROM orders o\n",[374,388,390],{"class":376,"line":389},3,[374,391,392],{},"JOIN (SELECT id FROM orders ORDER BY id LIMIT 1000000, 20) t ON o.id = t.id;\n",[10,394,395,396,399],{},"第二种是游标分页：记住上一页最后一条的排序键，下一页用 ",[70,397,398],{},"WHERE id > ? ORDER BY id LIMIT 20","。它稳定且高效，但只能顺序翻页，不适合随机跳页。",[10,401,402],{},"如果业务确实需要跳页，考虑限制最大页数、改为搜索条件缩小范围，或用搜索引擎\u002F数仓承接这类查询。",[96,404,406],{"id":405},"_8-大表-ddl-和分库分表要注意什么","8. 大表 DDL 和分库分表要注意什么？",[10,408,409],{},"MySQL 8 的 DDL 分 INSTANT、INPLACE 和 COPY 三类：加列、改默认值等在满足条件时可以秒级完成；改字符集、重建索引往往要 COPY 或 INPLACE，期间仍会消耗大量 IO。即使算法支持在线，也要先确认是否持锁、是否影响主从延迟。",[10,411,412],{},"执行前检查长事务和未提交事务，避免拿不到元数据锁导致后续请求排队。大表变更常用 gh-ost 或 pt-online-schema-change 做影子表切换，并安排低峰期和回滚预案。",[10,414,415],{},"分库分表不是第一选择：先考虑索引优化、归档冷数据、读写分离和分区表。确需拆分时，分片键要兼顾均匀性和查询命中率；跨库 join、分页、聚合和事务都会变复杂，通常要配套全局 ID、数据同步和路由层。",[10,417,418],{},"扩容是分库分表最容易低估的部分，提前设计好双写、迁移和校验方案，比事后补救成本低得多。",[96,420,421],{"id":421},"易错点与自测",[423,424,425,429,432],"ul",{},[426,427,428],"li",{},"「建了索引就一定走」和「走了索引就一定快」都不对，要看回表量和选择性。",[426,430,431],{},"RR 的「可重复读」只对快照读成立，当前读看到的是最新已提交数据。",[426,433,434],{},"间隙锁只在 RR 及以上、且涉及范围条件时明显，RC 下不要照搬 RR 的锁分析。",[10,436,437],{},"自测：① 给出一个联合索引并解释最左前缀；② 用 ReadView 的四个字段判断某个版本是否可见；③ 写出深分页的延迟关联写法并说明为什么更快。",[96,439,440],{"id":440},"参考资料",[423,442,443,453,461,476],{},[426,444,445,452],{},[446,447,451],"a",{"href":448,"rel":449},"https:\u002F\u002Fdev.mysql.com\u002Fdoc\u002Frefman\u002F8.4\u002Fen\u002Finnodb-locking.html",[450],"nofollow","MySQL 8.4 参考手册：InnoDB 锁"," — 记录锁、间隙锁与 next-key lock",[426,454,455,460],{},[446,456,459],{"href":457,"rel":458},"https:\u002F\u002Fdev.mysql.com\u002Fdoc\u002Frefman\u002F8.4\u002Fen\u002Finnodb-multi-versioning.html",[450],"MySQL 8.4 参考手册：InnoDB 多版本并发控制"," — 一致性读与隔离级别",[426,462,463,468,469,122,471,122,473,475],{},[446,464,467],{"href":465,"rel":466},"https:\u002F\u002Fdev.mysql.com\u002Fdoc\u002Frefman\u002F8.4\u002Fen\u002Fexplain-output.html",[450],"MySQL 8.4 参考手册：EXPLAIN 输出"," — ",[70,470,273],{},[70,472,298],{},[70,474,168],{}," 各列含义",[426,477,478,483],{},[446,479,482],{"href":480,"rel":481},"https:\u002F\u002Fdev.mysql.com\u002Fdoc\u002Frefman\u002F8.4\u002Fen\u002Finnodb-online-ddl.html",[450],"MySQL 8.4 参考手册：在线 DDL"," — INSTANT\u002FINPLACE\u002FCOPY 的边界",[485,486,487],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":370,"searchDepth":383,"depth":383,"links":489},[490,491,492,493,494,495,497,498,499,500],{"id":98,"depth":383,"text":99},{"id":114,"depth":383,"text":115},{"id":157,"depth":383,"text":158},{"id":210,"depth":383,"text":211},{"id":236,"depth":383,"text":237},{"id":263,"depth":383,"text":496},"6. EXPLAIN 重点看哪些列？慢 SQL 怎么优化？",{"id":353,"depth":383,"text":354},{"id":405,"depth":383,"text":406},{"id":421,"depth":383,"text":421},{"id":440,"depth":383,"text":440},"java-interview",null,"2026-10-24","以 InnoDB 8.0 为基线，讲清聚簇索引、索引失效、MVCC 与锁，并给出 EXPLAIN、深分页和大表变更的实战方法","md","框架与数据库",{},true,"80","\u002Fjava-interview\u002Fmysql",{"title":5,"description":504},{"loc":510},"java-interview\u002Fmysql","eyt0MprGiF-WhhgLbSLh4jZ1SG2HblXh9CUpUMt30WY",[516,519,523,527,531,535,539,543,547,551,555],{"id":510,"title":5,"titles":517,"content":518,"level":377},[],"以 InnoDB 8.0 为基线，讲清聚簇索引、索引失效、MVCC 与锁，并给出 EXPLAIN、深分页和大表变更的实战方法 MySQL 题如果只答「加索引」，很难拿到高分。面试官更想听的是：为什么这条 SQL 用不上索引、这条更新加了什么锁、RR 到底解决了什么又没解决什么。以下默认 InnoDB、MySQL 8.0。 场景 关键机制 常见误判 按主键查一行 聚簇索引，叶子即行数据 以为二级索引也要回表 按普通索引查整行 二级索引 → 主键 → 回表 忽略回表成本 查询列都在索引里 覆盖索引，免回表 以为必须 SELECT * 范围条件 + 索引过滤 索引下推（ICP） 以为过滤一定回表后做 高并发读写同一行 行锁 + 一致性读 以为 RR 下读都不加锁",{"id":520,"title":99,"titles":521,"content":522,"level":383},"\u002Fjava-interview\u002Fmysql#_1-innodb-的索引结构是什么聚簇索引和二级索引有什么区别",[5],"InnoDB 用 B+ 树组织索引。主键索引是聚簇索引，叶子节点直接存放整行数据，所以按主键查最快；如果没有主键，会选第一个非空唯一索引，再没有就生成隐藏的行 ID。 二级索引的叶子存放的是索引列加主键值。查整行时需要先查二级索引拿到主键，再回聚簇索引取数据，这个动作叫回表，也是「索引很多但查询仍然慢」的常见原因。 B+ 树的非叶子节点只存键和指针，扇出大、层数少，通常三到四层就能覆盖千万级数据。页大小默认 16KB，顺序主键插入能减少页分裂，随机 UUID 主键则会带来更多分裂和碎片。 MyISAM 的索引和数据分离，索引叶子存的是行地址，没有聚簇概念，也不支持事务和行锁。",{"id":524,"title":115,"titles":525,"content":526,"level":383},"\u002Fjava-interview\u002Fmysql#_2-哪些写法会导致索引失效",[5],"最常见的是在索引列上做函数或运算，例如 WHERE DATE(create_time) = '2026-10-01'、WHERE id + 1 = 10。应改成范围条件，或建立函数索引（MySQL 8.0.13 起支持）。 隐式类型转换也会失效：字符串列与数字比较时，MySQL 会把列转成数字，索引无法使用。参数类型要和列定义一致。 其他情况包括：LIKE '%关键字' 前导通配；复合索引不满足最左前缀；OR 两侧有一侧没有索引；!=、NOT IN 在选择性差时被优化器放弃；对索引列使用 IS NULL 并非一定失效，要看数据分布。 最后要理解「失效」很多时候是优化器的成本选择：当回表行数占比很高，全表扫描反而更便宜，强制走索引未必更快。",{"id":528,"title":158,"titles":529,"content":530,"level":383},"\u002Fjava-interview\u002Fmysql#_3-什么是覆盖索引和索引下推",[5],"覆盖索引指查询需要的列全部能在索引里拿到，不需要回表。EXPLAIN 的 Extra 会显示 Using index。 例如给 (user_id, status, created_at) 建联合索引，查询 SELECT status, created_at FROM orders WHERE user_id = ? 就能覆盖；如果再加 SELECT *，就得回表。 索引下推（ICP）是在存储引擎层用索引中的列提前过滤，减少回表次数。例如联合索引 (name, age)，条件 WHERE name LIKE '张%' AND age = 20，age 可以在索引上先判断，Extra 显示 Using index condition。 覆盖索引和索引下推解决的是不同问题：前者省掉回表，后者减少回表。设计联合索引时，把等值条件放前面、范围条件放后面，通常能同时兼顾过滤和排序。",{"id":532,"title":211,"titles":533,"content":534,"level":383},"\u002Fjava-interview\u002Fmysql#_4-mvcc-是怎么实现的rr-能完全解决幻读吗",[5],"InnoDB 每行有隐藏的事务 ID 和回滚指针，回滚指针把旧版本串成 undo 链。读取时根据 ReadView 判断哪个版本可见：当前事务 ID 小于最小活跃事务 ID 的版本可见，大于等于最大事务 ID 的不可见，处于活跃列表中的也不可见。 RC 每条语句生成一个新的 ReadView，所以能读到别人已提交的修改；RR 在事务第一次快照读时生成，之后复用，因此同一事务内多次查询结果一致。 但 RR 不能在所有场景下消除幻读：快照读靠一致性视图，当前读（SELECT ... FOR UPDATE、UPDATE、DELETE）会读最新版本并加锁。如果先快照读、再当前读，或者两个事务以不同顺序读写，仍可能看到「幻影」。 间隙锁和 next-key lock 是 RR 下当前读防幻读的手段，代价是更大的锁范围和更高的死锁概率。",{"id":536,"title":237,"titles":537,"content":538,"level":383},"\u002Fjava-interview\u002Fmysql#_5-innodb-有哪些锁间隙锁和-next-key-lock-怎么用",[5],"按粒度分表锁、行锁和意向锁；按模式分共享锁和排他锁。行锁实际加在索引记录上，所以「没有索引的更新」可能退化成锁全表。 记录锁锁定单条索引记录；间隙锁锁定两条记录之间的开区间；next-key lock 是记录锁加前面的间隙锁。插入时还会有插入意向锁，用来表示「准备插入这个间隙」，它与间隙锁冲突。 RC 隔离级别下基本只用记录锁；RR 下范围查询会使用 next-key lock。这也是为什么线上常用 RC：锁范围更小、死锁更少，代价是同一事务内可能出现不可重复读。 死锁排查看 SHOW ENGINE INNODB STATUS 的 LATEST DETECTED DEADLOCK，MySQL 8 还可以查 performance_schema.data_lock_waits。降低死锁的关键是短事务、固定加锁顺序、WHERE 条件走索引。",{"id":540,"title":496,"titles":541,"content":542,"level":383},"\u002Fjava-interview\u002Fmysql#_6-explain-重点看哪些列慢-sql-怎么优化",[5],"先看 type：const、eq_ref、ref、range 较好，index 是全索引扫描，ALL 是全表扫描。再看 key 是否命中预期索引、key_len 是否符合联合索引的前缀长度、rows 和 filtered 估算的扫描量。 Extra 信息量很大：Using index 是覆盖索引；Using index condition 是索引下推；Using where 表示还需要在服务层过滤；Using temporary 和 Using filesort 往往意味着排序或分组没有用上索引。 优化顺序通常是：确认 SQL 语义和返回量 → 补或改索引 → 重写查询（避免函数、拆 OR、减少回表）→ 调整分页和批量大小 → 最后才考虑优化器提示。MySQL 8.0.18 起可以用 EXPLAIN ANALYZE 看实际执行耗时。 不要迷信 FORCE INDEX，它会把优化器的判断固化成风险；统计信息不准时先 ANALYZE TABLE。",{"id":544,"title":354,"titles":545,"content":546,"level":383},"\u002Fjava-interview\u002Fmysql#_7-深分页为什么慢怎么优化",[5],"LIMIT 1000000, 20 需要先扫描并丢弃前 100 万行，再取 20 行，扫描量和偏移量成正比。即使命中索引，也要付出大量回表或遍历成本。 第一种方案是延迟关联：先在覆盖索引上查出主键，再关联原表取数据。子查询只扫索引，回表只发生在最终 20 行。 SELECT o.*\nFROM orders o\nJOIN (SELECT id FROM orders ORDER BY id LIMIT 1000000, 20) t ON o.id = t.id; 第二种是游标分页：记住上一页最后一条的排序键，下一页用 WHERE id > ? ORDER BY id LIMIT 20。它稳定且高效，但只能顺序翻页，不适合随机跳页。 如果业务确实需要跳页，考虑限制最大页数、改为搜索条件缩小范围，或用搜索引擎\u002F数仓承接这类查询。",{"id":548,"title":406,"titles":549,"content":550,"level":383},"\u002Fjava-interview\u002Fmysql#_8-大表-ddl-和分库分表要注意什么",[5],"MySQL 8 的 DDL 分 INSTANT、INPLACE 和 COPY 三类：加列、改默认值等在满足条件时可以秒级完成；改字符集、重建索引往往要 COPY 或 INPLACE，期间仍会消耗大量 IO。即使算法支持在线，也要先确认是否持锁、是否影响主从延迟。 执行前检查长事务和未提交事务，避免拿不到元数据锁导致后续请求排队。大表变更常用 gh-ost 或 pt-online-schema-change 做影子表切换，并安排低峰期和回滚预案。 分库分表不是第一选择：先考虑索引优化、归档冷数据、读写分离和分区表。确需拆分时，分片键要兼顾均匀性和查询命中率；跨库 join、分页、聚合和事务都会变复杂，通常要配套全局 ID、数据同步和路由层。 扩容是分库分表最容易低估的部分，提前设计好双写、迁移和校验方案，比事后补救成本低得多。",{"id":552,"title":421,"titles":553,"content":554,"level":383},"\u002Fjava-interview\u002Fmysql#易错点与自测",[5],"「建了索引就一定走」和「走了索引就一定快」都不对，要看回表量和选择性。 RR 的「可重复读」只对快照读成立，当前读看到的是最新已提交数据。 间隙锁只在 RR 及以上、且涉及范围条件时明显，RC 下不要照搬 RR 的锁分析。 自测：① 给出一个联合索引并解释最左前缀；② 用 ReadView 的四个字段判断某个版本是否可见；③ 写出深分页的延迟关联写法并说明为什么更快。",{"id":556,"title":440,"titles":557,"content":558,"level":383},"\u002Fjava-interview\u002Fmysql#参考资料",[5],"MySQL 8.4 参考手册：InnoDB 锁 — 记录锁、间隙锁与 next-key lock MySQL 8.4 参考手册：InnoDB 多版本并发控制 — 一致性读与隔离级别 MySQL 8.4 参考手册：EXPLAIN 输出 — type、key、Extra 各列含义 MySQL 8.4 参考手册：在线 DDL — INSTANT\u002FINPLACE\u002FCOPY 的边界 html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",1793263559738]