Mysql相关面试题
GQ MYSQL
MySQL 中的数据排序是怎么实现的?
MySQL 排序有两种方式:索引排序 和 文件排序(filesort)。
最理想情况是用索引排序,如果 ORDER BY 的字段跟索引顺序一致,MySQL 会顺着索引天然有序地取数据,这样最省资源。
如果排序字段不在索引里,或者顺序不匹配,就会走 filesort 文件排序。
filesort 又分 内存排序 和 磁盘排序:
- 数据量小能放进 sort_buffer 就做 内存排序
- 超过了 sort_buffer 就会写临时磁盘做 外部排序,性能就下降了
内存排序里 MySQL 会根据 max_length_for_sort_data 选择单路排序或双路排序,双路会更占 IO。
实际优化建议是:
- 能用索引排序就用索引排序,比如 (a,b) 索引配合 ORDER BY a,b
- 避免 ORDER BY 随机字段 / 函数 破坏索引
- 控制排序数据量,比如分页时 LIMIT 搭配索引
- 必要时可以调大 sort_buffer_size
总结一句:
MySQL 优先索引排序;无法使用索引时走 filesort,内存够就在内存排序,内存不够回落到磁盘排序。
MySQL 的 Change Buffer 是什么?它有什么作用?
什么是 Change Buffer?
它是位于系统表空间的一块持久化结构,是 InnoDB 用来暂存“非唯一二级索引页”的改动。当要改的那个二级索引页不在内存时,不立刻把页从磁盘读上来,而是先把这次改动记到 Change Buffer;等以后这个索引页被读入或后台机会到了,再批量合并(merge)进去。
为什么要这么干?(好处)
为了省随机 I/O。直接去读一个不在内存的索引页是一笔随机读,代价高;把很多零散的插入/更新/删除先攒起来,后面一次性合并,吞吐量会更高。
怎么配置?
- 打开范围:innodb_change_buffering=all|inserts|deletes|changes|none(默认 all)。
- 占用大小:innodb_change_buffer_max_size(默认 25% 的 Buffer Pool,上限 50%)。
- 如果业务是读多写少或大量唯一索引,可以考虑调小或关闭。
一句话总结:
Change Buffer = “非唯一二级索引改动的攒写账本”:先记账、后合并,用写换读,减少随机 I/O,对写密集场景很有用。
面试官追问
为什么唯一索引不用?
要实时检查是否冲突,必须把目标页读上来,攒不住。
和 Buffer Pool 的关系?
Change Buffer 是在系统表空间的持久化结构;它的页也会像普通页一样被缓存到 buffer pool,但本体不在 buffer pool。
什么是批量数据入库?相比单条插入有什么优势?
批量入库就是把多条数据一次性写进数据库(一次 SQL 或一次事务),而不是一条一条插。
好处:更少的网络往返、更少的事务/日志开销、写入更快
一句话总结
批量入库 = 把多条变一次:少网络、少事务、少日志 → 吞吐更高。
控制好批次大小 + 事务提交 + 约束/索引策略,既快又稳。
什么是数据全量同步和增量同步?它们各有什么优缺点?
- 全量同步:每次把所有数据都拷到目标端,像“全表重刷一遍”。
- 增量同步:只把这段时间新改动的数据同步过去(新增/更新/删除)。
优缺点:
- 全量:简单、稳,但慢、压力大;适合小数据量/初始化/灾备恢复。
- 增量:高效、实时,但实现复杂、易漏数据;适合大数据量/实时链路/数据仓库 ETL。
总结:
全量 = “一锅端,稳但慢”;增量 = “只搬变化,快但复杂”。
实战里先全量打底,再增量追平,并做好幂等、顺序、对账与补偿。
什么是游标 Cursor 分页?相比传统 LIMIT OFFSET 分页有什么优势?
游标/Keyset 分页:用“上一页最后一条的排序值”当起点,下一页用 WHERE sort_key > last_value ORDER BY sort_key LIMIT M 取数据。
优点:不扫丢弃的 N 行,更快更稳;数据有新增/删除时不漂移。
对比 OFFSET:LIMIT M OFFSET N 会先数 N 行再取 M 行,页数越大越慢,还会因为新增记录导致“同一页内容变了”。
缺点:不能跳页/不易算总页数,必须有可索引的唯一排序。
详细描述一条 SQL 语句在 MySQL 中的执行过程。
SQL 语句在 MySQL 中执行的过程:
-
连接数据库:首先,客户端和 MySQL 建立连接,验证权限和账号密码。
-
查询缓存:接下来,MySQL 会检查该 SQL 是否能从缓存中找到结果,如果能找到,直接返回缓存数据。
-
解析 SQL:如果缓存没有,SQL 会进入解析阶段,MySQL 会检查 SQL 的语法是否正确,然后生成查询的解析树(Parse Tree)。
-
优化 SQL:接着进入优化阶段,MySQL 会生成不同的执行计划,选择最优的方案,比如选择索引、表连接顺序、排序方式等。
-
执行计划:优化完成后,执行器根据优化好的计划去执行 SQL 操作。执行过程包括读取数据、执行计算、更新数据等。
-
返回结果:最后,MySQL 执行完毕后,会把结果返回给客户端。
简单总结:SQL 语句的执行就是:连接数据库 → 查询缓存 → 解析SQL → 优化SQL → 执行 SQL → 返回结果。
对于复杂查询,MySQL 会特别注意优化(例如:选择最优索引、减少 I/O、优化连接顺序等)以提高执行效率。
面试官可能追问
问:什么情况下查询会使用缓存?
答:如果 SQL 完全一样,且数据库没有更新,则会命中缓存。MySQL 5.7 之前支持查询缓存,MySQL 8.0 后已废弃该功能。
问:MySQL 如何优化 SQL?
答:优化器会考虑很多因素,如选择索引、表连接顺序、是否进行排序,甚至会在执行时避免不必要的计算和 I/O 操作。
MySQL 的存储引擎有哪些?它们之间有什么区别?
1.InnoDB
InnoDB 是一种兼顾高可靠性和高性能的通用存储引擎,在 MySQL 5.5 之后,InnoDB 是默认的 MySQL 引擎
特点:
- DML 操作遵循 ACID 模型,支持事务
- 行级锁,提高并发访问性能
- 支持外键约束,保证数据的完整性和正确性
适合场景
高并发、需要事务安全、数据一致性要求高的业务(如订单、支付系统)
2.MyISAM
MyISAM 是 MySQL 早期的默认存储引擎。
特点:
- 不支持事务,不支持外键
- 支持表锁,不支持行锁
- 访问速度快
适合场景
读多写少、不需要事务(如日志、文章内容、商品评论等)
3.Memory
Memory 引擎的表数据是存储在内存中的,受硬件问题、断电问题的影响,只能将这些表作为临时表或缓存使用。
特点:
- 存放在内存中,速度快
- hash 索引(默认)
适合场景
缓存、临时数据(如临时计算结果、会话数据)
4.存储引擎的选择
InnoDB → 事务多、并发高、需要数据完整性(电商订单、支付、库存)
MyISAM → 读多写少、事务要求不高(新闻、评论、日志)
Memory → 临时数据、缓存(在线排行榜、临时分析表)
电商示例:
- 用户订单表 → InnoDB(保证事务和一致性)
- 商品评论表 → MyISAM(读多写少,性能高)
- 热门搜索词缓存表 → Memory(速度快)
5.总结:
| 特性 | InnoDB | MyISAM | Memory |
|---|---|---|---|
| 存储限制 | 64TB | 有 | 有 |
| 事务安全 | ✅ | ❌ | ❌ |
| 锁机制 | 行锁 | 表锁 | 表锁 |
| B+Tree 索引 | ✅ | ✅ | ✅ |
| Hash 索引 | ❌ | ❌ | ✅ |
| 全文索引 | ✅(5.6+) | ✅ | ❌ |
| 空间使用 | 高 | 低 | N/A |
| 内存使用 | 高 | 低 | 中等 |
| 批量插入速度 | 低 | 高 | 高 |
| 支持外键 | ✅ | ❌ | ❌ |
MySQL 的索引类型有哪些?
mysql的索引类型有哪些?
1.按照索引结构分:
- B+Tree:最常见的索引类型,大部分引擎都支持 B + 树索引
- Hash:底层数据结构是用哈希表实现,只有精确匹配索引列的查询才有效,不支持范围查询
- R-Tree (空间索引):空间索引是 MyISAM 引擎的一个特殊索引类型,主要用于地理空间数据类型,通常使用较少
- Full-Text (全文索引/倒排索引):是一种通过建立倒排索引,快速匹配文档的方式,类似于 Lucene, Solr, ES
2.在 InnoDB 存储引擎中,根据索引的存储形式,又可以分为以下两种:
- 聚集索引 (Clustered Index):将数据存储与索引放一块,索引结构的叶子节点保存了行数据
- 二级索引 (Secondary Index):即非聚集索引,将数据与索引分开存储,索引结构的叶子节点关联的是对应的主键

3.按照索引性质分类:
- 主键索引:针对于表中主键创建的索引
- 唯一索引:避免同一个表中某数据列中的值重复
- 全文索引:全文索引查找的是文本中的关键词,而不是比较索引中的值
- 普通索引:快速定位特定数据(二级索引,辅助索引)
MySQL InnoDB 引擎中的聚簇索引和非聚簇索引有什么区别?
- 聚簇索引的非叶子节点 存储的是索引值叶子节点 存储的是 完整的数据记录,一个表只能有一个 聚簇索引,一般是表的主键,主要用于范围查询和排序。
- 非聚簇索引的非叶子节点 存储的也是索引值,但是叶子节点 存储的是数据行的主键和 对应的索引列一个表可以有 多个非聚簇索引非聚簇索引又称为 辅助索引或二级索引等,主要用于快速定位要查找的列。
简单来说:
聚簇索引简单理解就是把索引和数据记录放在一起
了,通过索引就可以直接找到数据行了,而非聚簇
索引,还需要通过 回表找到相应的数据记录

回表查询过程:先走二级索引(非聚簇索引)找到对应的主键值,再到聚集索引(聚簇索引)当中拿到这一行的行数据
MySQL 中的回表是什么?
在 InnoDB 中,主键索引是聚簇索引,存放了整行数据;而 二级索引 里只存储索引字段和对应的主键值。
当我们通过二级索引查询时,如果查询的字段不全在索引里,就需要再回到主键索引根据主键值取完整数据,这个过程叫做回表。
回表的缺点是会增加随机 I/O,导致性能下降。
为了避免回表,可以使用覆盖索引(Covering Index),即查询的字段全部包含在索引中,这样 MySQL 就不需要再去主键索引中取数据了。
在实际优化中,像日志表、查询频繁的表,可以通过联合索引或覆盖索引减少回表次数,从而显著提升查询性能。
MySQL 索引的最左前缀匹配原则是什么?
最左前缀法则
-
定义:如果索引关联了多个列(即联合索引),查询时必须从索引的最左列开始,且不能跳过任何列。如果跳过某一列,后面的列索引将会失效。
-
规则:最左列(A)必须出现在查询条件中,且不跳过任何列。
-
例子:假设有一个联合索引 (A, B, C),如果查询条件是 WHERE A = ? AND C = ?,那么 B 列索引会失效。
范围查询
-
在联合索引中,如果查询条件涉及范围查询(例如 >, <, BETWEEN),那么索引会从范围查询条件右侧的列开始失效。
-
解决方法:可以使用 >= 或 <= 代替 > 或 <,以避免索引失效。
-
例子:假设索引是 (A, B, C),如果查询条件是 WHERE A = ? AND B > ?,那么 C 列的索引将失效。
MySQL 的覆盖索引是什么?
覆盖索引是指二级索引包含了要查询的所有数据,不需要回表,就能够得到所有要查询的数据。
为什么覆盖索引能提升性能?
因为不需要加载所有的数据到内存中,所以也减少了随机IO,提高了查询速度,节约了内存。
补充
问:覆盖索引和普通索引的区别?
答:普通索引只包含查询条件的字段,而覆盖索引不仅包含查询条件的字段,还包括查询的返回字段。覆盖索引让查询只通过索引就能获取结果,而普通索引可能还需要回到表中获取剩余的字段。
问:覆盖索引能解决所有问题吗?
答:覆盖索引非常适用于只读的查询,但它会占用更多存储空间,且索引过多可能导致更新操作性能下降。在实际应用时需要平衡查询性能和存储成本。
MySQL 的索引下推是什么
索引下推:在执行查询时,MySQL 将查询条件下推到索引层,减少回表操作,直接在索引中过滤数据,提升查询性能。
怎么做的?
假设你有一个 users 表,id 和 age 都有索引,如果你执行查询:
1 | SELECT * FROM users WHERE id = 1 AND age > 30; |
在没有索引下推的情况下,MySQL 会先根据 id = 1 找到一条记录,然后回表再检查 age > 30 是否成立;
索引下推时,MySQL 在使用 id = 1 查找数据时,直接在索引层面同时应用 age > 30 的过滤条件,从而减少了回表操作。
优点:
- 减少回表次数,特别是对大表和复合索引非常有效,能显著提高查询性能;
- 提高查询速度,减少了不必要的 I/O 操作,特别是在查询的字段和过滤条件都能被索引覆盖时。
- 节省存储和内存:避免了从表中读取不需要的列。
在 MySQL 中建索引时需要注意哪些事项?
一、核心回答
建索引的核心原则是:提高查询效率的同时,尽量减少维护成本。
因此,索引不是越多越好,而是要建在合适的字段、合适的场景下。
二、适合建索引的场景
1.频繁出现在 WHERE、JOIN、ORDER BY、GROUP BY 中的字段
因为这些语句通常会涉及过滤、排序或分组,索引可以显著减少扫描行数,加快查询。
举例
-
WHERE user_id = ?
-
ORDER BY create_time DESC
-
GROUP BY category_id
2.用于表连接(JOIN)的关联字段
如果两张表通过某字段关联,建议在连接字段上建立索引,否则每次连接都要做全表扫描。
3.用于统计或聚合的字段
比如对 school 字段执行 COUNT()、SUM() 等聚合时,索引能加快统计速度(因为B+树中值是有序的)。
4.区分度高(唯一性强)的字段
例如用户ID、手机号等,能显著减少扫描范围。
三、不适合建索引的场景
1.字段频繁更新
因为索引也要维护B+树结构,频繁更新会导致性能下降。
- 举例:库存、计数器类字段不适合建索引。
2.字段取值重复率高、区分度低
比如“性别”“状态”这种字段,索引区分不了太多数据,反而占空间。
- 长文本字段(如 TEXT、LONGTEXT、BLOB)
这类字段索引开销很大,通常只会对前缀建立索引(如 VARCHAR(100) 前缀索引)。
- 在查询中参与计算或函数的列
例如:WHERE YEAR(create_time)=2024 或 age + 1 = 20,会导致索引失效。
- 小表数据量
如果表只有几百行数据,MySQL 可能直接全表扫描更快,不需要索引。
四.建索引的常见误区
- 索引不是越多越好:
索引需要占用磁盘空间,还会增加写入、更新成本。
通常一个表控制在 3~5 个有效索引即可。
- 联合索引要注意“最左前缀原则”:
比如 (a,b,c) 索引,能支持 a、a,b、a,b,c,但不支持单独 b、c 查询。
- 合理选择索引类型:
InnoDB 默认使用 B+Tree 索引;如果是文本搜索,可考虑全文索引(FullText);哈希索引适合等值查询。
五、总结
简单来说,建索引要遵循三句话:
- 查得多、改得少;
2.区分度高;
3.能配合查询条件使用。
同时通过 EXPLAIN 查看执行计划,可以判断索引是否真正生效。
实际开发中,我们通常会在性能测试阶段用 EXPLAIN + 慢查询日志 来评估索引效果,再决定是否保留或调整索引。
六.面试参考回答:
索引不是越多越好,建索引主要是为了加快查询,所以一般要建在查询频繁、出现在 where、join、order by、group by 的字段上。
如果字段更新频繁、取值重复率高,比如性别、状态这些,就不建议建索引,会拖慢更新性能。
另外像 长文本、参与函数或计算的列,索引也容易失效。
建联合索引要注意最左前缀原则,区分度高的字段放在前面。
总的来说就是:查得多、改得少、区分度高的字段适合建索引,建完后可以用 EXPLAIN 看索引是否生效。
一句收尾总结:
建索引的核心是“查得快、改得少、用得上”,不要盲目多建。
MySQL 中使用索引一定有效吗?如何排查索引效果?
一、核心回答(简洁版口述)
索引并不是一定会生效的,MySQL 优化器会根据数据量、索引选择度、SQL 写法等情况来判断是否使用索引。
有时候即使建了索引,MySQL 也可能选择全表扫描,因为它认为那样更快。
常见导致索引失效的场景主要有以下几类:
- 联合索引不满足最左前缀原则
比如索引是 (a, b, c),查询只用了 b 或 c,那索引可能不会被使用。
- 在索引列上做了计算或函数操作
如:where age + 1 = 30 或 where date(create_time) = ‘2025-10-24’
——这样会让索引失效,因为存储引擎无法直接使用原始索引值。
- 使用了模糊查询前置通配符
如 like ‘%abc’ 会导致全表扫描,而 like ‘abc%’ 则可以走索引。
- 字段类型不匹配导致隐式转换
例如列是 varchar,查询条件是整数 where name = 123,MySQL 需要隐式转换,会导致索引失效。
- 使用了 OR、IS NOT NULL、!=、<> 等条件
这些情况会让优化器放弃索引,选择全表扫描。
- 数据量太小或索引选择度太低
比如只有几百行数据,MySQL 会直接全表扫描,因为使用索引的开销反而更大。
二、如何排查索引是否生效?
可以通过 EXPLAIN 命令来查看执行计划。

三、即使用了索引也可能慢的原因:
-
优化器选错了索引(例如有多个索引时)
-
索引字段过大或重复值太多(选择度低)
-
频繁随机 I/O 导致性能下降
四、总结:
所以总结来说,MySQL 使用索引并不是绝对的,它会由优化器根据成本估算自动决定是否走索引。
我们可以通过 EXPLAIN 查看执行计划,判断是否真正使用到了预期的索引。
同时写 SQL 时要注意避免计算、函数、隐式转换、以及不符合最左前缀等情况,否则索引会失效。
MySQL 中的索引数量是否越多越好?为什么?
问题:MySQL 中索引是不是越多越好?
回答:不是,索引不是越多越好。
索引就像书的目录。目录能帮我们快速找到章节,但如果每一段话都建一个目录,书会又厚又难维护,每次修改内容还得更新所有目录。所以,目录多了反而拖慢效率。
-
增删改性能下降:
每当执行 INSERT、UPDATE、DELETE 操作时,不仅要修改数据本身,还要维护所有相关的索引,造成额外的写入开销。 -
占用额外磁盘空间:
每个索引都要存储在磁盘上,尤其是复合索引或重复索引,会浪费大量空间。 -
增加维护成本:
索引多会让优化器在选择执行计划时面临更多判断,反而可能选到次优索引,影响性能。
总结:
“索引能提高查询效率,但会降低写入性能和增加存储开销。一般遵循‘查询频繁的字段建索引、更新频繁的字段慎建索引’的原则,索引不是越多越好,而是要建得合理、用得精确。”
补充:
如果面试官继续追问“那怎么判断要不要加索引”,你可以补一句:
“我会结合实际的查询场景,用 EXPLAIN 分析 SQL 执行计划,如果扫描行数过多或查询频繁,再考虑为对应字段建立合适的单列或复合索引。”
请详细描述 MySQL 的 B+ 树中查询数据的全过程
MySQL 的索引底层是 B+ 树结构,查询过程可以分四步:
-
从根节点开始查找:
根节点存放索引键和页指针,根据目标键值判断向左或向右子树走。 -
逐层向下定位:
每层节点都使用二分查找快速定位范围,直到找到目标叶子节点。 -
在叶子节点中精确查找:
每个叶子节点就是一个数据页(16KB),内部通过页目录(Page Directory)定位槽(Slot),
再在槽内线性查找具体的数据行。 -
返回数据:
找到匹配的记录后返回结果。若是范围查询,则通过叶子节点间的双向链表顺序遍历。
一句话总结:
B+树查询是“逐层二分 + 页内定位”的过程,叶子节点存数据,非叶节点存索引。
15 秒口语速答版
查询从根节点开始,逐层比较键值定位到叶子节点,
在叶子页中通过页目录找到目标槽位,再线性查找具体记录。
一句话:从上到下二分查找,页内再精确匹配。
为什么 MySQL 选择使用 B+ 树作为索引结构?
面试官您好,其实 InnoDB 选择 B+Tree 主要是为了兼顾查询的多样性和性能。
-
首先,相比二叉树,B+Tree 的高度更低,所以磁盘 I/O 次数少,查询效率高。
-
其次,相比 B-Tree,B+Tree 非叶子节点不存储数据,这样一页存储更多索引值,树的高度进一步降低。叶子节点又通过链表连接,非常适合做范围查询和排序操作。
-
最后,虽然 Hash 索引在精确匹配时很快,但它不支持范围查询,也不能直接做排序,这在 SQL 里是很常见的需求。所以 InnoDB 默认选择了 B+Tree 索引结构,这样既能做精确查找,也能做范围、排序、前缀匹配等多种查询。
B+树和B树的区别
首先是B树的结构

然后是B+树的结构

我们可以看到,两部分:
- 绿色框框起来的部分,是索引部分,仅仅起到索引数据的作用,不存储数据。
- 红色框框起来的部分,是数据存储部分,在其叶子节点中要存储具体的数据。
与 B-Tree 的区别:
-
数据存储不同:B+ 树只有叶子节点存放数据,非叶子节点只存索引(单页可存更多 key,树高度低);B 树每个节点都存数据。
-
叶子节点结构不同:B+ 树的叶子节点通过链表相连(范围查询快),支持高效的范围查询;B 树没有链表,只能逐层遍历。
-
查询特性不同:B+ 树的查询路径长度固定(都需要从根节点扫描到叶子节点),性能更稳定;B 树可能在中间节点就返回结果,路径不固定。
MySQL 是如何实现事务的?
MySQL 的事务通过 锁、Redo Log、Undo Log、MVCC 实现。
锁机制控制并发修改,保证隔离性;
Redo Log 记录提交后的修改,用于崩溃恢复,保证持久性;
Undo Log 保存修改前的旧值,实现事务回滚,保证原子性;
MVCC 通过 Undo Log 版本链实现非锁定读,提升并发性并保证隔离性。
一致性通过原子性、隔离性、持久性达到.四者协同保证事务的 ACID 特性。
举例理解:
假设执行一条事务:
1 | BEGIN; |
执行流程如下:
- 写入 Undo Log,保存旧数据;
- 修改内存数据页;
- 写入 Redo Log(记录新数据);
- 提交事务时,将 Redo Log 持久化;
- 若崩溃 → Redo Log 负责恢复;
- 若回滚 → Undo Log 负责恢复旧值;
- MVCC + 锁 保证读写隔离。
MySQL 中长事务可能会导致哪些问题?
长事务问题:
- 锁竞争和死锁:长时间持有锁会阻塞其他事务,导致锁竞争,可能导致死锁(多个事务互相等待对方的锁,最终无法执行)。
- 主库延迟:长时间运行的事务会占用主库的资源,影响其他事务,导致写入延迟。
- 资源占用:长事务通常会占用大量的内存和 I/O,如果回滚,可能需要释放大量资源。
解决方案:拆分事务、优化索引和查询、合理选择隔离级别。
面试官可能追问
问:长事务怎么拆分?
答:可以根据业务场景,将长事务拆成多个小事务,按业务逻辑逐步执行,避免长时间占用锁。
例如,可以按时间段、数据范围拆分查询,每次处理一个小范围的数据,避免一个事务占用过长时间。
问:如何避免死锁?
答:避免不同事务按不同的顺序访问数据;使用行级锁而非表级锁;及时提交事务,避免事务过长时间占用资源。
MySQL 中的 MVCC 是什么?
MVCC,全称是 多版本并发控制(Multi-Version Concurrency Control)
- 多版本:是指数据库为同一行数据维护多个历史版本,每个事务根据自己的可见性规则读取合适的版本
- 并发控制:是指在多个事务同时操作某一行记录时,Mysql会从多个版本的行记录中的返回某个版本,并发控制的目的是在高并发下既保证一致性,又尽量避免锁竞争
是 InnoDB 为了提高并发性能、减少锁竞争的一种机制。
它的核心思想是:让读操作不加锁,也能看到符合事务隔离级别的数据版本。
一、为什么需要 MVCC?
在并发环境下,数据库会遇到三种情况:读读、写写、读写。
写写可以用锁解决,但读写同时发生时,如果用锁会导致性能大幅下降。MVCC 就是为了解决“读写冲突”的。
二、MVCC 的核心组成(实现原理)
在 InnoDB 中,MVCC 依赖三大组件实现:
1️⃣ 隐藏字段:每行记录包含事务ID和回滚指针,用于追踪版本
- db_trx_id:最后修改该行的事务 ID
- db_roll_ptr:指向 Undo Log 的回滚指针
- db_row_id:隐藏主键
结合上述字段,共同构成了undo log 版本链
2️⃣ Undo Log 版本链
每次事务修改数据时,会把旧版本写入 Undo Log,并形成一个链表。
这样数据库就能根据需要“沿着版本链”找到历史版本。
3️⃣ Read View(读视图)
当事务执行快照读(普通 SELECT)时,会生成一个 Read View。
它保存当前系统中“活跃事务的 ID 集合”,根据规定的版本链数据的访问规则来决定当前事务能看到哪个版本的数据,帮助事务在快照读取时访问正确的数据版本。
在不同隔离级别下,MVCC 的行为不同:
- RC:每次查询生成新快照;
- RR:事务开始时生成一次快照并复用,实现可重复读。
| 隔离级别 | Read View 生成时机 | 行为 |
|---|---|---|
| RC | 每次查询都新建一个快照 | 每次都能看到最新提交的数据 |
| RR | 事务开始时生成一次快照 | 整个事务都复用同一个快照,实现可重复读 |
在 RR 下,如果当前版本不可见,就会通过 Undo Log 找历史版本,直到找到可见的数据为止。
总结
MVCC 是 InnoDB 的多版本并发控制机制,通过隐藏字段、Undo Log 和 Read View 来实现。
它让普通的 SELECT 可以在不加锁的情况下让读写操作互不阻塞,实现高并发一致性读。
MySQL 二级索引有 MVCC 快照吗?
MySQL 二级索引有 MVCC 快照吗?
在 MySQL 中,二级索引并不直接支持 MVCC 快照。这是因为二级索引的结构只有索引列值和主键值,并不包含 InnoDB 隐藏的列,如 trx_id(事务 ID)和 roll_pointer(回滚指针)。这些列是实现 MVCC(多版本并发控制) 快照功能的关键,只有主键索引才会存储这些信息。
速答:
二级索引没有 MVCC 快照,因为它只包含索引列和主键值,不包含 trx_id 和 roll_pointer 等 InnoDB 隐藏列。因此,无法像主键索引那样判断事务快照,二级索引只能依赖主键索引来获取数据。
如果 MySQL 中没有 MVCC,会有什么影响?
没有 MVCC 会有什么影响?
-
缺乏并发控制:没有 MVCC,数据库就不能保证每个事务都能看到自己的数据版本。不同的事务可能读取到不一致的数据,从而影响数据库的正确性和一致性。
-
锁竞争和死锁:如果没有 MVCC,每次查询都会需要加锁,多个事务会因为竞争锁而导致等待,严重时还会导致死锁问题(多个事务互相等待对方释放锁)。
-
性能下降:由于没有 MVCC,事务之间的隔离性差,查询会受到其他事务的影响,导致查询结果不稳定,系统性能显著下降。
-
无法实现高效的隔离级别:MVCC 是实现** 隔离级别**(如可重复读、串行化)的重要机制。如果没有 MVCC,数据库无法高效地实现这些隔离级别,影响并发执行效率。
总结:没有 MVCC 时,MySQL 就无法有效控制并发操作,无法保证数据一致性和隔离性,最终导致系统的性能和稳定性大幅下降。
速答:
没有 MVCC,MySQL 就无法有效控制并发,会导致查询不一致、锁竞争、死锁等问题,严重影响系统性能。MVCC 通过多版本控制提供了事务的隔离性,确保每个事务能够看到一致的数据版本,从而提升并发性能。
补充:
问:MVCC 如何提高并发性能?
答:MVCC 通过为每个事务提供独立的数据版本,避免了事务之间的锁竞争,使得数据库可以在事务并发执行时不会互相阻塞,从而提升了并发性能。
MySQL 中的事务隔离级别有哪些?
我们都知道,数据库是是支持多个事务同时执行的,而这些同时执行的事务能不能看到别的事务更改的数据呢?能看到哪些数据?这就是事务隔离级别来规定的。
所谓事务隔离级别,是数据库管理系统(DBMS)中用于定义事务处理过程中不同事务之间可见性和相互影响程度的一套标准。
事务隔离级别的主要目的是平衡数据的一致性和系统的并发性,一般来说更高的隔离级别虽然能提高数据的一致性,但同时也可能降低系统的并发能力。
SQL-92 标准定义了 4 种隔离级别来解决脏读、幻读、不可重复读等这些异常情况,从高到底依次为:可串行化(Serializable)、可重复读(Repeatable reads)、提交读(Read committed)、未提交读(Read uncommitted)。
-
未提交读(Read uncommitted)是最低的隔离级别。通过名字我们就可以知道,在这种事务隔离级别下,一个事务可以读到另外一个事务未提交的数据。这种隔离级别下会存在幻读、不可重复读和脏读的问题。
-
提交读(Read committed)也可以翻译成读已提交,通过名字也可以分析出,在一个事务修改数据过程中,如果事务还没提交,其他事务不能读该数据。所以,这种隔离级别是可以避免脏读的发生的。
-
可重复读(Repeatable reads),由于提交读隔离级别会产生不可重复读的读现象。所以,比提交读更高一个级别的隔离级别就可以解决不可重复读的问题。这种隔离级别就叫可重复读。但是这种隔离级别没办法彻底解决幻读。
-
可串行化(Serializable)是最高的隔离级别,前面提到的所有的隔离级别都无法解决的幻读,在可串行化的隔离级别中可以解决。(通过锁机制实现,性能低)

MySQL 默认的事务隔离级别是什么?为什么选择这个级别?
- 可重复读是MySQL默认的事务隔离级别,它确保一个事务中的查询始终返回一致的数据,避免了脏读和不可重复读的问题。选择这个级别的主要原因是为了在保证事务一致性的同时,尽量避免性能损失。
- 可重复读通过使用MySQL的行级锁和多版本并发控制(MVCC)来实现数据隔离。虽然MVCC能有效管理并发数据访问,防止脏读和不可重复读问题,但它不能完全消除幻读(即读取数据的行数不同)。因此,在可重复读级别下,MySQL还使用间隙锁(Gap Locks)和临键锁(Next-Key Locks)来减少幻读现象。
- 此外,MySQL使用statement格式记录binlog,可能导致主从同步时出现事务执行顺序不一致的问题,进而引发数据不一致。可重复读通过避免事务多次更新相同数据,减轻了这一问题。
- 与其他较高隔离级别(如串行化)相比,可重复读在数据一致性和性能之间达到了良好的平衡,因此它成为了大多数应用场景下的默认隔离级别。
数据库的脏读、不可重复读和幻读分别是什么?
数据库中主要有三种常见的并发问题:脏读、不可重复读和幻读。
第一个是脏读(Dirty Read),简单理解就是——被别人骗了。一个事务读到了另一个事务还没提交的数据,如果那个事务最后回滚了,那你读到的数据就不是真的。
第二个是不可重复读(Non-repeatable Read),可以理解为—— 谁改我数据了?事务里前后两次读同一行数据,结果不一样。因为在两次读取之间,别的事务修改并提交了这条数据。
第三个是幻读(Phantom Read),就像闹鬼了一样。你第一次查出10条记录,第二次在同样条件下查,又多出一条。原因是别的事务在你查询的范围内插入或删除了数据。
这三种问题的区别可以这样理解:
- 脏读 → 读到了别人‘还没提交’的数据。
- 不可重复读 → 读到了别人‘提交后修改’的数据。
- 幻读 → 读到了别人‘提交后新增或删除’的数据。
一般来说,MySQL的可重复读隔离级别(Repeatable Read)能避免前两种问题,并通过间隙锁减少幻读的发生。
MySQL 中有哪些锁类型?
MYSQL 按锁的粒度可分为:
- 全局锁:锁住整个数据库(相当于给整个大楼给封了),一个命令下来整个库谁也别想写数据
- 作用:显而易见用于全库备份时使用,全库备份需要保证数据一动不动,这就是全局锁的作用了
- 表锁:锁住整张表(相当于锁一个房间),像老一点的 MyISAM 引擎默认就是表锁。
- 好处:简单直接,开销小
- 坏处:只要有一个人在房间操作其他所有人都要在门口排队,即并发差
- 行锁:锁住当行数据(相当于只锁一个抽屉),这也是目前 InnoDB 用的
- 好处:并发高,你给你的数据我改我的数据只要不是同一行数据咱两就互不影响
- 坏处:开销大(管一堆钥匙,要比只管大门钥匙要麻烦很多),可能死锁(你拿着我的钥匙我拿着你的钥匙两人就卡着不动了)
MYSQL 锁的派别
- 悲观锁:它认为只要我改数据肯定就有人给我抢,所以它在干活前就用 select…for update 语句来锁住把数据锁的死死的,谁也别想动
- 场景:用在秒杀,抢库存这一类高并发的场景中(写多读少),并发写操作多,冲突概率极大必须先下手为强
- 乐观锁:它是乐天派,认为改数据时没那么多人跟我抢,所以它不加锁,直接去操作数据,只在最后要提交的时候,用版本号或者时间戳来比对一下,看看我干活的这期间有没有人都过我的数据,如果有人改了,这次提交就失败
- 场景:适合像改文章,改商品信息这一类读多写少的场景,因为它省去了加锁的开销,性能就会好很多
MYSQL 中锁属性之分
相当于在图书馆看书时
-
共享锁(S 锁 / 读锁):它就等于大家一起看书,一本书可以被很多人同时看,只要大家不在上面写字就行,即读读共享,读写互斥,在 mysql 中可以用 lock in share mode 这个语句加的就是一个读锁
-
排他锁(X 锁 / 写锁):当我要改书的时候必须要把这本书拿走,自己一个人改,在你改完还回来之前,别人既不能看也不能改,即写写互斥,读写互斥,在 mysql 中可以用 for update 这个语句加的就是一个读锁
MYSQL 中意向锁
把它想成一个挂在表门口的通知牌,它的作用就是为了提高效率,如果事务 A 锁了表里面的某一行数据,这个时候事务 B 它想要给整个表加一个锁,那 MYSQL 咋办呢,难道一行一行去检查有没有行锁吗,那表里面如果有上千万行数据查到猴年马月去,这数据库直接就会被卡死,所以 InnoDB 就设计了这个通知牌,当事务 A 给某一行加锁的时候,InnoDB 会自动在这个表的门口上面挂个牌子,上面写着里面有人正在操作请注意,这样当事务 B 想来锁整张表的时候抬头一看这张表的门口上有个牌子,立刻就知道里面有行锁,有冲突,自己得等着,效率一下就上来了,所以意向锁本身它是一个表级锁,但它不是用来锁数据的,而是用来打配合提高效率的,
有了意向锁这个帮手我们再来看看 InnoDB 真正在一线干活的几个核心武器
MYSQL 中记录锁(Record Lock)
记录锁,记录锁就是最纯粹的行锁,指哪打哪,只锁定你查询命中的那一条记录,比如能用注解 where id = 10 去更新,那他就只锁 ID 等于 10 这一行,这是我们最希望看到的最理想的情况
但是如果查询条件不是唯一的呢,比如查 age>18 这样的范围,这时候面试必考的知识点就来了 - 幻读
为了解决幻读 InnoDB 就掏出来他的大杀器间隙锁 & 临键锁
MYSQL 中的间隙锁 & 临键锁
间隙锁它不锁任何已经存在的数据,就是一个开区间,它只是锁住记录和记录之间的那个缝,比如说索引里面有 10 和 20,那他就锁住 10-20 这一个区间,让你没法在这个缝隙里面插入新的数据,它就像给数据之间放了个结界,任何新的换的数据都不允许冒出来。
而临键锁呢,他就是记录锁 + 间隙锁,它不但锁住记录本身,还把这条记录前面的那个缝也锁住了,这才是 InnoDB 在可重复读隔离级别下默认使用的锁,它就像一张大网,把记录和它旁边的缝全给罩住,让幻读无处遁形
总结
锁这个东西从来没有绝对的好与坏,全局锁虽然暴力,但是在备份的时候离不开它,行锁虽然精细,但你得承担死锁的风险,临键锁虽然能解决幻读,但它也可能锁住没有必要的范围,牺牲了一些性能
面试回答
在MySQL中,锁主要可以按粒度、模式、属性、状态和算法来分类。最常见的几种锁类型我可以大概总结一下:
- 粒度分类:
- 行级锁:锁定单行数据,这对高并发非常有效。你修改一行数据,我修改另一行,不会互相干扰,但是它的开销比较大。
- 表级锁:锁定整张表,适用于低并发的场景,开销小,但并发性能差。
- 全局锁:锁住整个数据库,通常用于数据库备份时,保证数据一致性,但也会阻塞其他所有操作。
- 模式分类:
- 乐观锁:这种方式认为冲突的可能性小,不加锁,只在提交时检查数据是否被修改过。如果被修改,事务就会失败。适合读多写少的场景。
- 悲观锁:认为数据会被其他事务抢占,所以提前加锁,防止其他事务修改,适用于并发写操作较多的场景。
- 属性分类:
- 共享锁:也就是读锁,它允许多个事务同时读取数据,但不能修改,适用于只读场景。
- 排他锁:也叫写锁,只有当前事务能够修改数据,其他事务既不能读也不能修改,适用于修改数据时使用。
- 状态分类:
- 意向锁:这其实是为提高效率而设计的,事务在打算锁定某些行时,会在表级加一个提示,告诉系统将来要对这行加锁,从而避免不必要的检查。
- 算法分类:
- 记录锁:只锁定某一行数据,最常见的行锁。
- 间隙锁:锁住记录之间的“空隙”,防止其他事务在这个区间插入数据,主要是为了解决幻读问题。
- 临键锁:这是记录锁和间隙锁的结合,不仅锁住记录本身,还锁住记录前后的空隙,用来解决更复杂的幻读问题。
总的来说,这些锁类型帮助MySQL处理不同的并发控制需求,既能保证数据一致性,又尽量提升系统性能,避免了不必要的阻塞。
MySQL 的乐观锁和悲观锁是什么?
-
悲观锁(Pessimistic Locking):
悲观锁的意思是数据库操作认为会有其他事务对数据进行修改,所以会先加锁,确保其他事务无法修改数据,直到当前事务完成。这种方式比较适合数据需要强一致性保证的场景,比如银行转账之类。 -
乐观锁(Optimistic Locking):
乐观锁则假设数据不会发生冲突,它不会立即加锁,而是进行数据更新前,会先检测数据是否被其他事务修改过。如果数据没有被其他事务修改,就提交事务;如果数据已经被修改,当前事务就会被拒绝,要求重新尝试。这种方式适合数据冲突较少的情况。
总结:
- 悲观锁:会先加锁,确保没有其他事务修改数据,适合高一致性要求的场景。
- 乐观锁:不会加锁,假设数据不会有冲突,适合冲突少的场景。
简单来说,悲观锁适用于数据需要严密控制的环境,乐观锁则适用于冲突较少、容忍重新尝试的环境。
MySQL 中如果发生死锁应该如何解决?
死锁是指在数据库中,两个或更多事务在执行时互相等待对方释放资源,导致所有事务都无法继续执行的情况。常见的情况是,事务A锁定了资源1,事务B锁定了资源2,然后事务A等待资源2,事务B等待资源1,这就导致了死锁。
如何解决死锁?
- MySQL自动解决:
- MySQL有自带的死锁检测机制,当它检测到死锁时,会自动回滚其中一个事务,从而解除死锁。
- 另外,MySQL也可以设置超时参数(比如innodb_lock_wait_timeout),如果某个事务长时间未能获得锁,它会被强制回滚,从而避免死锁持续存在。
- 手动解决死锁:
- 如果死锁发生得比较频繁,我们可以通过手动干预来解决。首先,通过命令查询出被死锁阻塞的事务,然后手动执行KILL命令终止阻塞的事务,释放资源。
如何避免死锁?
- 避免事务互相依赖:
将大的事务拆成多个小事务,减少资源竞争的机会。
- 调整锁的顺序:
确保所有事务按固定的顺序申请锁,这样可以避免环状等待。
- 降低隔离级别:
比如使用较低的隔离级别,减少对锁的需求,从而降低死锁发生的概率。
- 优化查询:
确保查询效率高,避免长时间锁定资源,从而降低死锁的可能。
- 合理设置锁的超时时间:
配置适当的锁超时,避免因等待过长导致死锁。
总之,通过合理设计事务,优化查询和锁机制,我们可以有效地减少死锁的发生。
如何使用 MySQL 的 EXPLAIN 语句进行查询分析?
我会用 EXPLAIN 来分析 SQL 的执行计划,它能显示查询是否用到了索引、扫描了多少行、有没有出现性能问题。
我重点看四个字段:
| 参数 | 含义 | 优化目标 |
|---|---|---|
| type | 表示连接类型,反映数据访问方式 | 优化重点,越靠前性能越好。常见值:system > const > eq_ref > ref > range > index > ALL |
| key | 实际使用到的索引名称 | 确认是否命中正确索引 |
| rows | 预估需要扫描的行数 | 越少越好 |
| extra | 额外信息,说明执行细节 | 例如:Using index(覆盖索引,性能好) 或 Using filesort(文件排序,性能差) |
-
type 表示访问方式,性能从好到坏是 const、ref、range、index、ALL;
-
key 表示实际使用到的索引名称;
-
rows:是预估需要扫描的行数,越少越好;
-
extra 里如果出现 Using filesort 或 Using temporary 就说明需要优化。
通常我会通过建立合适的索引、调整查询条件或字段顺序,让 type 提升、rows 下降,从而提高 SQL 查询性能。
常见优化思路
- 建立合适的索引:确保查询字段、排序字段都在索引中;
- 优化复合索引顺序:遵循“最左前缀匹配原则”;
- 避免SELECT :只查询必要字段;
- 避免使用函数或隐式类型转换:会导致索引失效;
- 观察 rows 值:过大说明索引没生效。
MySQL 中 count(*)、count(1) 和 count(字段名) 有什么区别?
面试官您好,COUNT(*)、COUNT(1)、COUNT(字段) 虽然都能返回数量,但底层逻辑不同。
-
第一,COUNT(*):统计所有行,不管字段是不是 NULL。
InnoDB 会优先使用最小的二级索引或表元数据,所以性能最高。 -
第二,COUNT(1):本质等价于 COUNT(*),只是把 * 换成常量,MySQL 优化器会自动忽略常量,所以性能几乎一致。
-
第三,COUNT(字段):只统计该字段值非 NULL 的行,因为它需要逐行判断字段是否为 NULL,所以性能略低。
一句话总结:
COUNT(*) ≈ COUNT(1) > COUNT(字段)
一般我们统计总行数用 COUNT(*),统计某字段有效值数量用 COUNT(字段)。

如果面试官追问
1️.为什么 COUNT(*) 性能最高?
- 在 InnoDB 中,COUNT(*) 不会去取出数据,只会扫描索引页;
- 如果存在更小的二级索引,会优先使用最小索引;
- MySQL 8.0 之后甚至可以直接用“表统计信息”优化返回结果。
2.为什么 COUNT(字段) 慢?
- 需要判断每一行字段是否为 NULL;
- 如果该字段未建索引,还要扫描整表。
MySQL 中 int(11) 的 11 表示什么?
表示显示宽度,不影响存储大小和数值范围。如果长度没有达到11前面用0来补充
MySQL 中 varchar 和 char 有什么区别?
char 和 varchar 的区别主要有三点:
- 存储方式不同:
- char 是固定长度,空间会被空格补齐;
- varchar 是可变长度,只占用实际字符长度 + 1~2 字节的长度标识。
- 性能与空间权衡:
- char 访问速度快,但可能浪费空间;
- varchar 更节省空间,但增删改频繁时需要维护长度信息,性能略低。
- 使用场景不同:
- char 适合存储长度固定的数据(如身份证号、MD5);
- varchar 适合长度不固定的字段(如用户名、备注等)。
一句话总结:char 稳定但浪费,varchar 灵活但有开销。
如果面试官继续追问
问:varchar 最长能存多少?
答: 理论上单行最大 65535 字节,但要减去行开销、NULL 标志、字符集影响,实际远小于这个值。
问:排序时哪个快?
答:char 排序快,因为长度固定无需额外计算;varchar 排序会涉及 sort_buffer,略慢。
口语速答版
char 是固定长度,varchar 是可变长度。
char 存储效率高但浪费空间,varchar 节省空间但维护成本高。
一般固定字段用 char,比如身份证号;可变字段用 varchar,比如用户名或备注。
MySQL 中如何进行 SQL 调优?
MySQL 中如何进行 SQL 调优?
答:SQL 调优我一般分两步走:“先定位问题,再针对性优化”。
- 第一步:定位性能瓶颈
-
开启慢查询日志(slow query log)
用来记录执行时间超过设定阈值的 SQL,快速找出慢查询语句。set global slow_query_log = 'ON'; -
使用 EXPLAIN 分析执行计划
查看 SQL 是否使用了索引、扫描了多少行、有没有出现 Using filesort 或 Using temporary 这些低效操作。
- 第二步:优化 SQL 语句
-
合理设计索引:
建立合适的单列索引或联合索引,满足“最左前缀匹配原则”;常用字段可使用覆盖索引减少回表。 -
避免索引失效:
1.避免 SELECT *(只查必要字段)
2.避免 LIKE ‘%xxx’ 模糊匹配
3.避免在索引字段上进行函数或计算
4.注意不同字符集、类型转换导致索引失效
-
减少不必要的操作:
1.减少多表 JOIN
2.避免复杂的 OR 条件(可以用 UNION ALL 替代)
3.GROUP BY、ORDER BY 尽量排序字段建索引
-
优化表结构与缓存:
1.字段类型尽量精简
2.避免大字段频繁查询
3.适当使用缓存机制(如 Redis)减轻数据库压力
总结:
“我会先通过慢查询日志和 EXPLAIN 找出性能瓶颈,然后从索引设计、SQL写法和表结构三方面去优化。
比如合理用联合索引、避免 SELECT *、避免索引字段函数计算,让查询走索引、少回表、少排序,从而显著提升性能。”
速答版:
我做 SQL 调优一般分两步:先定位问题,再优化语句。
首先我会开启 慢查询日志 找出执行慢的 SQL,然后用 EXPLAIN 查看执行计划,看是否走了索引、有没有全表扫描或 filesort。
优化时我主要关注 索引设计和写法,比如建立合适的联合索引、遵守最左前缀原则、避免 SELECT *、LIKE ‘%xx’、函数计算等导致索引失效的情况。
同时控制多表 join、group by、order by 的使用,必要时加缓存或优化表结构,让查询更高效。
如何在 MySQL 中避免单点故障?
在 MySQL 中避免单点故障的常见方法是通过 主从架构 来实现。在主从架构中,主数据库处理所有的写操作,从数据库主要负责读操作。当主数据库发生故障时,可以通过切换到从数据库来保持服务的持续性,减少单点故障的影响。
如何在 MySQL 中实现读写分离?
在 MySQL 中,读写分离通常是通过主从复制来实现的。
主数据库负责处理所有的写请求,从数据库处理读取请求。这样可以减轻主数据库的负担,提高系统的查询性能。
通常有两种方式可以实现:一种是通过中间件来转发读写请求,另一种是直接在数据库层面配置主从关系,确保写操作只能访问主库,而读操作访问从库。这两种方式各有优缺点,具体选择要看实际情况。
什么是 MySQL 的主从同步机制?它是如何实现的?
MySQL的主从同步机制是一种数据复制技术,用于将主库上的数据变更按顺序同步到一个或多个从库中。
原理是:主库记录 binlog,从库拉取并重放这些日志,把数据保持一致。
如何实现主从同步?
主从同步主要靠日志 + 两端线程实现,流程可以简单记成 3 步 2 线程 2 日志:
1)主库
事务提交时写** binlog**(记录所有变更)
2)从库 I/O 线程
连接主库的 **binlog dump **线程
把 binlog 拉下来写到本地的 relay log
3)从库 SQL 线程
从 relay log 里读事件
按顺序回放,执行到从库,保证数据一致
关键字:binlog、relay log、I/O 线程、SQL 线程
主从同步的扩展类型:
- 异步复制:
- 主服务器将更新操作记录到日志中,然后就立即返回,而不等待从服务器确认。这样可能存在主从数据不一致的风险,但性能较高。
- 半同步复制:
- 主服务器写入日志后,会等待至少一个从服务器确认已经接收到日志并应用。这样做能确保主从数据一致性,但会稍微降低性能。
- 全同步复制:
- 主服务器必须等待所有从服务器确认收到日志并应用后,才能完成操作。虽然数据一致性最强,但性能可能会受到很大影响。
| 模式 | 特点 | 一致性 | 性能 |
|---|---|---|---|
| 异步(默认) | 主库不等从库 | 可能丢数据 | 最佳 |
| 半同步 | 主库必须等至少 1 个从库收到 binlog并应用 | 大大降低丢数据 | 略慢 |
| 全同步 | 主库等从库执行完再返回 | 最强一致性 | 最差,少用 |
怎么保证从库追得上主库?
MySQL 支持并行复制:
- 5.6:按库并行
- 5.7:基于组提交/逻辑时钟并行
- 8.0:基于 WriteSet 冲突检测,并行效果最好
简单理解:版本越高,并行越智能,从库越不容易落后
补充:
Relay Log
Relay Log(中继日志)是MySQL主从复制架构中用于同步数据的核心组件,主要作用是将主服务器的数据变更记录传输到从服务器,确保数据一致性。
使用中怎么避免数据不一致?
几条重点可以提一下:
- 开启半同步复制
- sync_binlog=1 + innodb_flush_log_at_trx_commit=1
- 使用 GTID + relay_log_recovery=ON,方便故障切换不丢点位
- 读写分离时,要考虑读一致性,比如延迟读问题
一句话总结
MySQL 主从复制就是:主库写 binlog → 从库拉 binlog 写 relay log → 从库重放日志同步数据,通过异步/半同步模式和并行复制来平衡一致性和性能。
如何处理 MySQL 的主从同步延迟?
首先我们要明确主从同步延迟一定存在,无法彻底避免,只能减少延迟和避免延迟带来的业务问题。
我们从业务层 + 数据库配置 + 系统层来解决
- 业务层 数据库层减少复制延迟
- 二次查询
从库查不到,再查主库,API 层封装即可- 缺点:主库压力大,适用于读不多的关键业务
- 关键业务强制走主库
比如用户下单、支付成功查询这种场景
非关键业务走从库,读写分离 - 缓存兜底
主库写入后,把数据写缓存,读的时候优先读缓存
降低延迟的问题- 缺点:要处理缓存一致性
总结:重 数据库层减少复制延迟要读走主库 + 缓存,普通读走从库
2. 数据库层减少复制延迟
几个核心措施:
- 开启 并行复制
- MySQL 5.7/8.0 并行执行 relay log,大幅减少延迟
- 调整复制模式
- 异步 → 半同步(减少丢数据风险)
- GTID + relay_log_recovery=ON
- crash-safe replication,故障切换更快
- 系统 & 资源层优化
- 增加 CPU / 内存 / IO
- 使用 SSD
- 优化网络带宽与延迟
- 避免大事务、长事务(它们是延迟元凶)
- 合理分库分表、减少单库写压力
- 主库不要一次性压力过大(批量写拆分)
补充:面试官如果追问:你选哪种方案?
举个例子:高并发电商场景的话,热点写走主库+缓存,普通读走从库,开启 MySQL 并行复制,并避免大事务,这样能把延迟影响降到最低。
一句话总结
MySQL 主从延迟是必然存在的。
应对思路是:业务层避免一致性问题 + 数据库层并行与半同步 + 系统层提高资源与网络,避免长事务。
什么是分库分表?分库分表有哪些类型(或策略)?
分库分表 是一种通过将大数据量的数据拆分到多个库和多个表中的策略,目的是为了提升系统的可扩展性、性能和可用性。
分库分表通常有几种类型(或策略):
- 水平分表:
水平分表是将一张大表按行切割成多张小表的方式。例如,可以根据用户的 ID 将数据分到多个表(如 user_1、user_2)。这种方式适合用于数据量非常大的情况 - 垂直分表:
垂直分表是将一张表中不同的列分到不同的表里,以减轻单表中列数过多的问题。举个例子,如果一个用户表有很多字段,可能将基础信息和详细信息分开存储,这样能提高查询性能。 - 水平分库:
水平分库是将数据按库的级别进行拆分,将相同类型的数据分配到多个数据库中,用于读写压力很大的场景. - 垂直分库:
垂直分库通过将不同的数据存储到不同的数据库中来减少数据库的压力。常用来解决业务逻辑复杂时产生的查询慢、负载重的问题。
小结:
- 水平分表,把一张表的数据分散到多张表中,例如user_1、user_2
- 记忆提示:水平就像是将一张表按行切割成多张表。
- 垂直分表,把一张表的不同字段分散到不同表中
- 记忆提示:垂直就像是将一张表的字段切割成多个表。
- 水平分库,把相同的库结构复制到另一个库中,分担单一数据库的读写压力
- 记忆提示:水平分库就是将相同的数据结构分布到多个数据库中,类似于将一个大仓库拆成几个小仓库。
- 垂直分库,把不同表分散到不同库中
- 记忆提示:垂直分库就是把不同的表分到不同的数据库中,类似于将不同部门的数据分开管理。
如果组长要求你主导项目中的分库分表,大致的实施流程是?
分库分表实施流程:
- 先分析需求:看下是不是因为数据量大或者增长太快,才需要分库分表。
- 设计方案:选择合适的策略,比如水平分表、垂直分表,或者分库分表。
- 设计数据路由:想好怎么根据条件(像用户ID)把数据放到不同的库或表里。
- 数据迁移:把旧的数据迁移到新的分库分表结构,确保数据没丢。
- 调整SQL和业务逻辑:根据新的分布方式,改改SQL语句和业务流程。
- 监控优化:实施后,要监控系统,确保它的性能好,没问题。
对数据库进行分库分表可能会引发哪些问题?
当做分库分表时,我们需要注意几个问题:
1.数据一致性:数据分散在不同的库或表中,可能导致一致性问题,我们得确保它们同步或者用其他方法保持一致。
2.JOIN 问题:传统的 JOIN 查询不能直接执行,可能需要先从一个表查出数据,再到其他表去查。我们可以通过其他方法来解决这个问题。
3.全局 ID 唯一:分库分表后,ID 会冲突,所以需要使用全局唯一的 ID,保证每个记录都是唯一的。
4.排序和 count:分库分表后,排序和 count 会变复杂,可能需要在应用层做这些操作,或者用一些额外的工具来支持。
从 MySQL 获取数据,是从磁盘读取的吗?(buffer pool)
MySQL 获取数据并不总是从磁盘读取的。 MySQL(尤其是使用 InnoDB 存储引擎时)通常会先检查数据是否已经被加载到缓存中(比如 Buffer Pool)。如果数据已经在缓存中,系统会直接从内存中读取,这样可以提高查询性能。
如果数据不在缓存中,系统会从磁盘读取数据,并把这些数据加载到缓存中,后续的查询就可以直接从缓存读取,提高效率。
所以,MySQL 优先从内存缓存(Buffer Pool)中获取数据,只有在数据不在缓存中的情况下才会从磁盘读取。
MySQL 的 Doublewrite Buffer 是什么?它有什么作用?
MySQL 的 Doublewrite Buffer 是一个机制,用于确保数据的安全性和一致性。在 MySQL 的 InnoDB 存储引擎中,写入操作首先会将数据写入一个内存中的缓冲区(也叫 Doublewrite Buffer)。接着,这些数据会被写入磁盘上的实际数据文件。这个过程保证了即使在写入过程中发生崩溃或系统故障,也能通过 Doublewrite Buffer 中的数据恢复数据,避免数据损坏。
简而言之,Doublewrite Buffer 的作用就是:先在内存中缓存数据,再写入磁盘。如果写入磁盘时发生故障,系统可以通过 Doublewrite Buffer 来恢复数据,确保数据的一致性和完整性。
MySQL 中的 Log Buffer 是什么?它有什么作用?
在 MySQL 中,Log Buffer 是一个内存区,用来暂时存储事务的 redo log(重做日志)数据。它的主要作用是提高性能,避免每次写操作都直接去磁盘,而是先把数据写入内存(缓冲区)。这能显著减少磁盘 I/O 操作,提高数据库性能。
buffer pool -> log buffer -> redo log 保证内存页不丢失
buffer pool -> double write buffer -> double write buffer FIles 保证内存页不损坏
为什么在 MySQL 中不推荐使用多表 JOIN?
我们不推荐在 MySQL 中使用多表 JOIN,因为它会导致查询的计算复杂度大幅增加。
尤其是在数据量大的情况下,JOIN 操作需要扫描多个表,消耗大量内存和 CPU,影响性能。而且复杂的查询语句也很难维护和调试,出错的几率增加。
所以我们通常会使用适当的索引来优化查询,或者将大表分成小表来避免性能瓶颈。
MySQL 中如何解决深度分页的问题?
MYSQL中深度分页常常遇到的一个问题是,随着分页的深入,查询的效率会变得非常低,因为每次需要跳过很多数据行,尤其是当 LIMIT 和 OFFSET 结合使用时,OFFSET 会导致数据库扫描大量无用的记录,造成性能下降。
为了优化这一点,通常可以采用以下两种方法
- 子查询优化分页:
比如,如果我们要查询第100000页的数据,每页10条记录,我们可以先通过一个子查询找到第100000页的第一条记录的ID。然后,在实际的查询中,使用这个ID作为起始点来查询接下来的10条记录。这样我们就避免了从头开始扫描所有记录,查询效率大大提升。
例子:
1 | SELECT * FROM users WHERE id >= ( |
- 记录最后查询的ID:
每次分页查询时,我们记录下上一次查询结果的最后一条记录的ID,下一次查询时,直接从这个ID开始查询。这样每次分页查询都避免了大范围的跳过操作,直接从上一次查询的位置开始,显著提高了查询性能。
1 | SELECT * FROM users WHERE id > last_id ORDER BY id LIMIT 10; |
通过这两种方法,我们可以避免传统的 LIMIT + OFFSET 的性能问题,尤其是在数据量大的时候,查询速度会更快,系统的负担也会减轻。
如何在 MySQL 中监控和优化慢 SQL?
监控慢 SQL:
1.开启慢查询日志:
- 在 MySQL 中,可以通过启用 slow_query_log 来监控慢查询。这会记录所有执行时间超过一定阈值的 SQL 查询。
- 你可以设置 long_query_time 来指定查询超过多少秒才会被记录。
2.使用 EXPLAIN 分析查询计划:
当发现慢查询时,可以使用 EXPLAIN 来查看查询的执行计划。通过查看执行的顺序、使用的索引、扫描的行数等信息,你可以分析出瓶颈所在,进而进行优化。
优化慢 SQL
优化的方向通常包括使用合适的索引、避免无效的全表扫描、减少 JOIN 的复杂度以及合理配置数据库连接数等。
MySQL 中 DELETE、DROP 和 TRUNCATE 的区别是什么?
删库跑路用drop;只删数据Truncate;删除行数据用Delete。
MySQL 中 INNER JOIN、LEFT JOIN 和 RIGHT JOIN 的区别是什么?
inner join 内连接:内连接就是返回两张表都关联上的数据
left join 左连接:返回时保留左表的数据,右表关联不上的会显示null值
right join 右连接:返回时保留右表的数据,左表关联不上的会显示null值
MySQL 中 LIMIT 100000000, 10和 LIMIT 10 的执行速度是否相同?
不相同
LIMIT1000000,10会先遍历前100000条数据,再找到这之后的10条数据进行返回,消耗很大
LIMIT 10,只对10条数据进行返回,消耗很少
MySQL 中 DATETIME 和 TIMESTAMP 类型的区别是什么?
- datetime 采用字符串形式存储,占用8个字节,不会受时区影响。
- timestamp 采用时间戳形式存储,占用4个字节,受时区影响,时间从1970-01-01开始。
在 MySQL 中,你使用过哪些函数?
聚合函数
sum avg max min count
字符串函数
upper lower length replace
数学函数
pow abs mod
MySQL 中 TEXT 类型最大可以存储多长的文本?
tinyText:255字节
text:64KB
mediumText:16MB
longText:4G
MySQL 中 AUTO_INCREMENT 列达到最大值时会发生什么?
如果表定义的自增id达到上限后,再申请下一个id,得到的值不变,所以会导致报重复值的错误
在 MySQL 中存储金额数据,应该使用什么数据类型?
在 MySQL 中存储金额数据时,推荐使用 decimal 数据类型。这个数据类型适合精确存储小数,特别是在财务或金额计算中,非常重要的是避免浮动误差。通常我们会使用 decimal(18, 2),其中 18 表示总位数(包括小数部分),2 表示小数点后的位数。
为什么不使用 bigint?
尽管 bigint 能够存储大整数,但它不适合存储有小数的金额,且它只能存储整数值,不支持小数位。
关于 BigDecimal 和 decimal:
BigDecimal 是 Java 中的一个类,它用于高精度计算,可以处理比 decimal 更高精度的小数,因此在 Java 应用中处理数据库中存储的 decimal 类型时通常会用到。
什么是数据库的视图?
数据库视图是一个虚拟的表,它让我们在不暴露底层数据表的情况下,仍然能高效查询所需的数据。同时,视图帮助增强了数据的安全性和简化了数据库操作。
为什么不推荐在 MySQL 中直接存储图片、音频、视频等大容量内容?
数据库本身是高效处理结构化和关系型的数据,存储图片音频视频等可以使用云平台的对象存储服务,在mysql中存储URL即可。
相比于 Oracle,MySQL 的优势有哪些?
免费、开源、轻量化适合小中型应用
MySQL 中 VARCHAR(100) 和 VARCHAR(10) 的区别是什么?
两者的区别就是能存储的字符串长度上限不同
- VARCHAR(100)可以存储最多100个字符。
- VARCHAR(10)可以存储最多10个字符。
两者存储 相同字符串时 占用空间一样
在什么情况下,不推荐为数据库建立索引?
以下几种情况不推荐建立索引:
-
数据量小的表
如果表中的数据很少,比如几百条记录,建立索引可能并不会带来显著的性能提升,反而会增加管理和维护的成本。 -
频繁更新的表
对于频繁进行插入、更新、删除操作的表,建立索引时会带来额外的性能负担,因为每次修改数据时都需要同步更新索引,这会影响数据库的写入性能。 -
执行大查询时使用SELECT
如果你查询的字段很多,使用SELECT *查询会使数据库扫描大量无用的列,这时建立索引对提高性能的帮助较小,甚至可能降低查询性能,因为索引的维护需要额外的存储和计算。 -
低选择性的列(高重复值的列)
如果你要为高重复值的列建立索引(例如性别字段,值只有“男”和“女”),索引的作用有限。因为这些列的重复值太多,索引无法有效减少扫描的数据量。 -
非常长的列
比如TEXT类型字段,这种类型占用的空间大,不适合创建索引。如果为这些列建立索引,会占用大量磁盘空间,还会增加I/O消耗,导致查询变慢。
MySQL 中 EXISTS 和 IN 的区别是什么?
- EXISTS 用于判断子查询是否返回任何行,通常用于检查某个条件是否满足。
- IN 用于检查某个值是否在指定的集合中,可以是一个子查询或静态值列表。
什么是 Write-Ahead Logging (WAL) 技术?它的优点是什么?MySQL 中是否用到了 WAL?
WAL(Write-Ahead Logging)技术的核心理念是,先将数据修改记录到日志里,然后再修改数据库本身。(先写日志再写数据)。这可以确保系统在崩溃时能够通过日志恢复数据,保证数据的一致性。MySQL中的InnoDB使用了类似的技术,叫做Redo Log,它实现了WAL的功能,通过先记录修改日志,再更新数据文件来保证数据的持久性和一致性。当数据库崩溃时,我们可以用这些日志来恢复未完成的事务。它不仅增强了数据的可靠性,还能提升系统性能。
你们生产环境的 MySQL 中使用了什么事务隔离级别?为什么?
mysql默认可重复读,但一般公司修改为读已提交,为了提高并发和降低死锁率
为什么阿里巴巴的 Java 手册不推荐使用存储过程?
存储过程难以调试、扩展更没有一致性,比如 MySQL 的存储过程跟 Oracle 的存储过程不一样。
如何实现数据库的不停服迁移?
使用双写方案
双写方案意味着同时向原数据库和目标数据库写入数据,确保数据的迁移不会中断业务。
1.搞一个云上数据库,作为从库,同步数据
2.改写业务代码,使写数据的操作,要同时写入主库和从库。要加个开关,可以随时开启以及关闭双写。
3.在业务低峰期,主从完全同步的时候,开启双写,这时候读数据还是在主库上
4.进行数据核对,写代码进行抽样调查,看主库和从库数据是否一致,不一致就告警
5.如果数据核对一致,则进行灰度切流,将一部分比例的用户的读请求切到从库上,逐渐的增加比例到100%
6.跑一段时间,如果没有问题了,关闭双写,只写新库
MySQL 数据库的性能优化方法有哪些?
在 MySQL 性能优化时
- 首先要从 SQL 入手。避免使用 SELECT * ,只查询你需要的字段,并且避免在查询中进行复杂的计算,确保查询能利用索引。使用 EXPLAIN 来分析 SQL 执行计划,确保查询效率。
- 同时,数据库设计也非常重要,选择合适的数据类型,避免过大的字段类型,并且合理分表、分库,使用合适的索引来提升查询速度。如果有冷数据,也可以分开存储,减少数据库的负担。
MySQL 中 InnoDB 存储引擎与 MyISAM 存储引擎的区别是什么?
InnoDB 和 MyISAM 是 MySQL 中最常用的两种存储引擎,它们的主要区别在于事务支持、锁机制、外键支持、存储方式和恢复能力。
事务支持:InnoDB 支持事务,可以保证 ACID 特性,而 MyISAM 不支持事务,适用于一些不需要事务的应用。
存储方式:InnoDB 使用 聚集索引,数据和索引存储在一起。而 MyISAM 使用 非聚集索引,数据和索引分别存储。
锁机制:InnoDB 使用 行级锁,可以支持更高的并发操作。而 MyISAM 使用 表级锁,在高并发的写操作场景下会有性能瓶颈。
外键支持:InnoDB 支持 外键约束,而 MyISAM 不支持。
恢复能力:InnoDB 支持崩溃恢复,使用 redolog 进行数据恢复。而 MyISAM 不支持崩溃恢复。
总结来说,如果应用需要事务、外键约束和高并发写操作,应该选择 InnoDB;如果只是需要快速的读操作,且没有事务需求,可以考虑 MyISAM。
| 特性 | InnoDB | MyISAM |
|---|---|---|
| 事务支持 | 支持事务,保证 ACID 特性 | 不支持事务 |
| 存储方式 | 聚集索引,数据和索引存储在一起 | 非聚集索引,数据和索引存储在不同文件中 |
| 锁机制 | 行级锁,支持更高并发操作 | 表级锁,性能瓶颈,尤其是高并发写操作 |
| 外键支持 | 支持外键约束,参照完整性保证 | 不支持外键约束 |
| 恢复能力 | 支持崩溃恢复,使用 redolog 日志 | 不支持崩溃恢复 |
| 适用场景 | 适用于需要事务、外键约束和高并发写操作 | 适用于不需要事务的快速读操作 |
MySQL 的查询优化器如何选择执行计划?
MySQL 查询优化器选择执行计划时,会计算多种执行计划的成本,并选择成本最低的那个计划。主要有两个成本:
IO 成本:这是将一页的数据从磁盘加载到内存的成本。
CPU 成本:这是处理数据所需的计算成本,比如扫描、排序等操作,操作越多,CPU 成本越高。
最后,查询优化器会根据这两种成本来选择最合适的执行计划,确保查询执行得更高效,减少数据库的负载。
什么是数据库的逻辑删除?数据库的物理删除和逻辑删除有什么区别?
在数据库中,逻辑删除和物理删除是两种不同的删除方式。
- 逻辑删除并不是真的从数据库里删除数据,而是通过设置一个标记(比如 is_deleted)来标记该记录为删除状态,这样数据仍然保留在表里。它的好处是,数据可以随时恢复,保留历史数据,方便追溯,但会占用存储空间。
- 而物理删除就是直接从数据库中移除数据,这样就释放了存储空间,但数据一旦删除就无法恢复。选择哪种删除方式,取决于业务需求,如果需要保留数据记录,通常会选择逻辑删除,如果数据不再需要,选择物理删除。
什么是数据库的逻辑外键?数据库的物理外键和逻辑外键各有什么优缺点?
物理外键是通过数据库本身来维护表与表之间的关系,比如当删除一个父表的记录时,数据库会自动处理子表中的关联数据,确保数据一致性。它的优点是数据一致性强,不容易出错,但缺点是可能会增加一些性能开销。
逻辑外键则是通过应用程序的逻辑来维护的,通常我们在应用层面手动检查和维护表与表之间的关系。它的优点是更加灵活,不会增加数据库的负担,适用于高性能需求的场景。但缺点是代码更复杂,需要手动维护数据一致性,容易出错。
MySQL 事务的二阶段提交是什么?
在MySQL中,事务使用两阶段提交(Two-Phase Commit)来确保数据的一致性,特别是在涉及redo log和binlog的情况下。这是为了确保即使在崩溃恢复时,数据也能保持一致。
两阶段提交的过程可以分为两个阶段:
1.准备阶段(Prepare Phase):
当一个事务开始时,MySQL会先写入redo log(WAL),表示事务已经开始并进入“准备”状态。此时,虽然事务还没有完全提交,但数据库已经准备好执行操作。
2.提交阶段(Commit Phase):
当redo log的准备阶段完成后,MySQL会写入binlog,这时表明事务的数据修改已经准备好提交。如果binlog写入成功,MySQL会通知InnoDB,修改事务状态为commit,完成整个事务的提交。
为什么要使用两阶段提交?
两阶段提交确保了数据库的一致性。第一个阶段主要是记录日志,表示事务已经准备好,但还没提交。第二阶段则是确认事务成功提交,并且保证所有修改的记录都一致。这样,即使在事务处理中断或崩溃时,恢复过程也能保持数据的一致性。
细节补充:
- redo log用于记录数据页的修改,而binlog记录的是SQL语句或DML操作。它们配合使用,确保在数据库崩溃后能够准确恢复数据。
- 通过XID(事务ID)来关联redo log和binlog,从而保证它们的内容一致。
- XID帮助确保当事务被提交时,所有日志信息都一致。如果发现某个事务的redo log没有进入准备阶段,那么该事务会被回滚。
总结:
两阶段提交通过将事务的操作分成准备和提交两个阶段,确保数据的一致性,并且能够在崩溃恢复时保证数据的完整性。
面试速答:
"两阶段提交(Two-Phase Commit)是MySQL确保事务数据一致性的一种方式,简单来说,就是把事务提交分成两个阶段来做,确保数据不会出问题。
- 准备阶段(Prepare Phase):
在事务开始时,数据库先记录redo log,表示事务已经开始,准备好进行数据修改。但这时候数据还没提交。
- 提交阶段(Commit Phase):
当准备阶段完成后,数据库再写入binlog,表示事务的数据修改已经成功,接下来就真正提交数据,让所有的修改生效。
为什么要用两阶段提交?
两阶段提交的目的是确保即使数据库发生崩溃,也能保证事务的数据是完整的和一致的。第一阶段只是标记准备好,第二阶段才真正提交事务,确保操作的一致性。
总的来说,两阶段提交帮助我们保证事务在数据库恢复时不会丢失数据,也能确保数据的一致性和完整性。"
MySQL 三层 B+ 树能存多少数据?
其实不用去背公式,理解大致原理就够了。
- 在 InnoDB 里,一页(也就是一个节点)默认大小是 16KB。
- 非叶子节点存的是 索引键和指针,所以一个节点大概能存一千多个子节点。
- 叶子节点才存真正的数据行,一页能放十几条记录。
这样算下来:
-
第一层(根节点)可以挂上千个二级节点;
-
每个二级节点又能挂上千个三级节点;
-
每个三级节点能放十几条记录。
乘起来大概就是 上千万到两千万条数据,也就是我们常说的「一个三层 B+ 树能支撑 2000 万左右的行数」。
而且由于 B+ 树一般只有 2~3 层,索引查找只需磁盘IO两三次,性能是非常高的。
当单表数据量超过这个数量级(比如几千万甚至上亿)时,通常我们才会考虑分表或分库。
注:当然这只是一个估算,实际能存多少还跟行大小、页大小(比如16KB或32KB)、以及索引字段长度有关。所以真实环境中2000万不是固定上限,只是一个性能拐点。
MySQL 在设计表(建表)时需要注意什么?
为字段设计合适的字段类型
分析表结构建立合适的索引
满足范式,但为了满足系统设计可以适当反范式,来增加一些冗余字段。
不要给空字段设置为null值,可以给默认值。因为如果是null值的话每次操作都会进行判空,浪费性能。
MySQL 插入一条 SQL 语句,redo log 记录的是什么?
当 MySQL 执行一条 SQL 插入语句时,redo log 记录的是数据页的修改,而不是具体的 SQL 语句。这是为了确保在系统崩溃后,能够通过 redo log 恢复数据库到崩溃时的状态。redo log 实际上记录的是物理数据修改,而不是操作细节,比如它记录了数据页的更新,而不会记录“插入一行”这样的 SQL 命令。
MySQL 中 binlog、undo log、redo log 和 relay log 的区别
在 MySQL 中,binlog、undo log、redo log 和 relay log 各有不同的作用。
- binlog(二进制日志) 主要是记录数据库所有的数据修改操作,比如插入、更新和删除,用于 数据恢复 和 主从复制,它是一个逻辑日志,记录的是执行的操作。
- undo log(回滚日志) 用于 回滚事务,当事务失败时,undo log 会撤销已做的修改,保证 事务的原子性,也是逻辑日志。
- redo log(重做日志)是记录 物理数据的修改,主要用于在数据库崩溃时通过重做日志来恢复数据,保证 数据的持久性,它是物理日志。
- relay log(中继日志) 则是从库用来同步 主库 数据的日志,它记录了主库的 binlog,用于 主从同步,同样是物理日志。
SQL 中 select、from、join、where、group by、having、order by、limit 的执行顺序是什么?
最先的都是确定数据源的:from >on>join
其次的是条件和聚合函数:where> group by >聚合函数 >having
最后才是筛选类子句:select > distinct >order by >limit
为什么 MySQL 索引用的是 B+ 树而不是红黑树?
MySQL 中选择 B+ 树 作为索引结构,主要是为了优化查询性能,并考虑磁盘 I/O 的效率。
-
磁盘 I/O 更少
B+ 树的 树高 比红黑树低,因此每次查询需要访问的 磁盘 I/O 次数更少,性能更高。在数据库中,磁盘 I/O 成本高,所以降低树的高度可以大大提高查询效率。 -
相比红黑树,B+ 树更适合数据库的查询需求
红黑树虽然支持快速的插入、删除和查找,但它的结构更复杂,不适合存储大量数据并执行高效的磁盘 I/O 操作。而 B+ 树 则更适合做范围查询、排序等需求,而且可以通过 顺序扫描 快速获取数据。
所以,InnoDB 选择 B+ 树 是因为它在磁盘 I/O 性能、范围查询、排序以及多种查询操作的支持上,都能提供更好的性能。而红黑树在数据量大且需要频繁读取和排序时,性能和效率较低。
MySQL一张表最多可以有多少列?
InnoDB 存储引擎:最多支持 1017 列
MyISAM 存储引擎:最多支持 4096 列
MySQL 中 如果我 select * from 一个有 1000 万行的表,内存会飙升么?
不会的。
MySQL 执行 SELECT * FROM … 时,并不会把 1000 万行一次性全部加载到内存里,而是 边查询、边取数据、边通过网络发给客户端,属于 流式输出。
为什么不会把 1000 万行一次性放进内存?
MySQL 是按照 批次(batch)从磁盘读取数据的,每批的数据量由 net_buffer_length 控制,默认只有 16KB。
实际流程是:
- MySQL 从磁盘取一行数据
- 写入
net_buffer(最多 16KB) - buffer 满了就发送给客户端
- 继续下一批直到结束
所以服务器端内存始终是小批小批地用,不会因为 1000 万行而爆炸。
什么时候可能卡住?
不是 MySQL 内存飙升,而是客户端读得太慢。
如果客户端处理得慢,导致服务器端的 socket send buffer 写满了,那么 MySQL 会暂时停住发送数据,但依然不会导致 MySQL 内存暴涨。
总结
SELECT *大表不会导致 MySQL 内存飙升,因为 MySQL 是流式传输、批量发送数据,不会把所有行一次性加载到内存。


