MySQL 八股文【持续更新ing】_mysql的varchar八股文
MySQL 八股文【持续更新ing】
文章目录
- MySQL 八股文【持续更新ing】
 - 前言
 - 一、MySQL的存储引擎有哪些?他们之间有什么区别?
 - 二、MySQL `InnoDB 引擎中`的聚簇索引和非聚簇索引有什么区别?
 - 
- 1.InnoDB 中的聚簇索引
 - 2.InnoDB 中的非聚簇索引
 
 - 三、MySQL的索引类型有哪些?
 - 四、为什么MySQL选择使用 B+ 树作为索引结构?
 - 五、MySQL索引的最左匹配原则是什么?
 - 六、MySQL 三层 B+ 树能存多少数据?
 - 七、MySQL中的回表是什么
 - 八、MySQL中使用索引一定有效吗?如何排查索引效果?
 - 九、在MySQL中建索引时需要注意哪些事项?
 - 十、MySQL中的索引数量是否越多越好?为什么?
 - 十一、如何使用MySQL的Explain语句进行查询分析?
 - 十二、MySQL中如何进行SQL调优?
 - 十三、详细描述MySQL的B+树中查询数据的全过程
 - 十四、MySQL中 count(*)、count(1)、count(字段名) 有什么区别?
 - 十五、MySQL 中 varchar 和 char 有什么区别?
 - 十六、MySQL 是如何实现事务的?
 
前言
一名27届后端开发选手的八股学习日常总结。
一、MySQL的存储引擎有哪些?他们之间有什么区别?
- InnoDB存储引擎
特点:- 支持事务(ACID)(通过begin、commit、rollback 控制)
- A(Atomicity)原子性
 - C(Consistency)一致性
 - I(Isolation)隔离性
 - D(Durability)持久性
 
 - 支持行级锁(从锁粒度来划分)
- 通过对索引项加锁来实现行级锁、如果查询条件没有建立索引则会升级为表级锁。
 
 - 外键约束
 - 支持聚簇索引、提高检索效率
- 默认用
表的主键建立聚簇索引 - 没有主键则去查找
唯一非空属性建立聚簇索引 - 没有
唯一非空属性则使用隐藏的Row_id建立聚簇索引 - B+树实现
 - 聚簇索引的B+树叶子结点存放数据本身,非叶子节点不存放数据(进存放索引,例如:主键id)
 
 - 默认用
 - 支持MVCC(多版本并发控制)来提高数据库的并发性能,保证事务的ACID。
 - 提供
undolog和redolog来实现回滚和崩溃恢复 
 - 支持事务(ACID)(通过begin、commit、rollback 控制)
 - MyISAM存储引擎
特点:- 不支持事务、外键
 - 仅支持表级锁(不支持行级锁)
 - 支持非聚簇索引,不支持聚簇索引
- 也是B+树实现
 - 非聚簇索引的B+树叶子结点存放数据行地址(索引文件和数据文件分开存放)
 
 - 适合 读取多、更新少的场景,例如数据仓库
 - 具有较高的读性能和较快的表级锁定
 
 - Memory存储引擎
- 是内存存储引擎,数据存储在内存中,服务重启数据会丢失
 - 仅支持hash索引(存储在内存中)
 - 适用于临时数据存储或快速缓存
 - 存放一些公共的、常用的、且不经常发生改变的数据
 
 - NDB(NDBCluster)
- 支持高可用性和数据分布,适合大规模分布式应用
 - 提供行级锁和自动分区
 
 
二、MySQL InnoDB 引擎中的聚簇索引和非聚簇索引有什么区别?
1.InnoDB 中的聚簇索引
- 聚簇索引的B+树的叶子节点中存放的是数据行、非叶子节点存放的的是索引
 - 叶子结点包含所有的数据
 - 每一个表只能有一个聚簇索引,通常是主键索引,如果没有设定主键,回去选择一个唯一非空列作为索引去建立聚簇索引,如果也不存在唯一非空列,则使用隐藏的Row_id作为索引
 - 适合于范围查询和排序
 
2.InnoDB 中的非聚簇索引
- InnoDB中的非聚簇索引的索引叶子节点存储的是数据行的主键和对应的索引列,需要通过主键进行回表查询完整的数据行
 - 一个表中可以有多个非聚簇索引(这里又称为 非主键索引、辅助索引、二级索引),适合快速查找特定列的数据
 
三、MySQL的索引类型有哪些?
按照数据结构划分:
- B+树索引
 - hash索引
 - 全文索引(倒排索引)
 
从常见的基于 InnoDB B+树 索引角度来看,分为:
- 聚簇索引
 - 非聚簇索引
 
按照索引性质划分:
- 普通索引(二级索引、辅助索引)
 - 主键索引
 - 联合索引
 - 唯一索引
 - 全文索引
 
四、为什么MySQL选择使用 B+ 树作为索引结构?
- 高效的查询性能
- B+ 树是一种自平衡树,每个叶子结点到根节点的路径长度相同,B+ 树在插入和删除节点时会进行分裂和合并操作,以保持树的平衡,但是他有一定的冗余节点,使得删除的时候树结构的变化小,更高效
 - 查找、插入、删除等操作的时间复杂度为O(logn)
 
 - 树的高度增长不会过快,这样可以减少查询磁盘的IO次数
- B+ 树不像红黑树,数据越多树的高度增长就越快。B+ 树是多叉树,非叶子节点仅保存主键或索引值和页面指针,使得每一页能容纳更多的记录,因此内存中可以放更多索引
 
 - 范围查询能力强
- B+ 树特别适合范围查询,因为叶子结点通过双向链表连接,从根节点定位到叶子结点查找的范围的起点之后只需要顺序扫描链表即可完成后续的数据遍历
 
 
五、MySQL索引的最左匹配原则是什么?
MySQL索引的最左匹配原则指的是:在使用联合索引时,查询条件必须按照索引建立的顺序去筛选,例如建立索引的顺序是(a、b、c),那么作为条件查询的时候应该尽量保持
where a = ? and b = ? and c ...。
底层原理:
MySQL建立的联合索引中,是按照顺序建立的,例如联合索引(first_name,last_name,age)的排列方式大致示意图:
 (Alice, Black, 35) | ------------------------------------------------------------- | |  | | (Alice, Brown, 25) (Alice, Smith, 30) (Bob, Smith, 20) (Bob, White, 19) 
六、MySQL 三层 B+ 树能存多少数据?
在MySQL的InnoDB存储引擎中,B+树默认数据页大小是一页 16kB。
聚簇索引中,每一个非叶子结点中存放:
- 索引键(一般取 主键id:bigInt --> 8B)、每个指针(6B),那么每个中间节点可以存放(16kB /(6B + 8B)) = (16 * 1024 B) / (14B)= 1170
 - 即每一层的中间节点都可以指向1170个子节点。
 - 最后叶子结点的个数为:1170 * 1170 * 16 = 21902400,一棵三层的B+ 树在MySQL中可以存储大约2000万条记录。
 
图示:
 
七、MySQL中的回表是什么
回表一般是指在使用InnoDB引擎的二级索引(非聚簇索引)作为条件进行查询时,由于二级索引中只存储了索引字段的值和对应的主键值,无法得到其他数据,如果要查询数据行中的其他数据,需要根据主键去聚簇索引中查找实际的数据行,这个过程叫做回表。
注意:仅仅是指InnoDB中的非聚簇索引中,MyISAM中的非聚簇索引是数据文件和索引文件分开存放,不存在回表的概念。
八、MySQL中使用索引一定有效吗?如何排查索引效果?
- 不一定,索引可能会失效,索引常见失效情况如下:
- 查询条件中破坏了最左匹配原则(没有按照索引建立顺序且使用了范围查询,或者使用了没有建立索引的列)
 - 索引列参与了计算或者函数
 - not in 和 not exists使用不当
 - 某些情况下的数据类型强制转换
 - 。。。
 
 - 一般使用Explain关键字去查看索引使用情况
explain select ....
其中下面这些列可以看出走索引的情况- type:表示查询时数据访问方式:性能从优到劣排序如下:
- system > const > eq_ref > ref > range > index > all
- system:表中仅一行数据(系统表)。
 - const:通过主键或唯一索引查到一行数据(如 
WHERE id = 1)。 - eq_ref:唯一索引关联(如 
JOIN时使用主键匹配)。 - ref:非唯一索引查找(可能返回多行)。
 range(优化目标):索引范围扫描(如BETWEEN、IN)。- index:全索引扫描(遍历索引树)。
 - ALL:全表扫描(需优化)。
 
 
 - system > const > eq_ref > ref > range > index > all
 - key:实际使用到的索引
- 若为null,则表示未使用索引
 
 - key_len:索引使用的字节数,反应索引字段的利用率
- 可以根据建立的索引列属性计算出哪些索引被用上了
 
 - Extra:表示附加信息,反应执行细节。
- Using where:使用 
WHERE过滤数据。 - Using index:覆盖索引(无需回表)。
 - Using temporary:使用临时表(常见于排序或分组)。
 - Using filesort:
外部排序(需优化索引)。 - Using join buffer:使用连接缓冲区(关联字段无索引)。
 
 - Using where:使用 
 
 
九、在MySQL中建索引时需要注意哪些事项?
- 索引不是越多越好,索引会占用存储空间,每次修改都需要维护索引中的数据,也会耗时
 - 字段值大量重复的字段不要建立索引,例如:性别字段
 - 长字段不应该建立索引,因为占据的内存大,扫描的时候比较耗时,可以考虑建立前缀索引
 - 当表的修改频率远大于查询频率是,应该考试是否建立索引
 - 对于需要频繁作为条件查询的字段应该建立索引,且可以考虑优先建立联合索引,减少索引数量,利用覆盖索引提高效率,避免回表查询
 - 对经常在order by、group by、distinct 后面的字段建立索引,加快效率
 
十、MySQL中的索引数量是否越多越好?为什么?
索引并不是越多越好。
- 每建立一个索引都需要新建一个B+树,所以如果索引过多会占据很大的空间
 - 对于索引的更新操作会导致B+树进行节点的修改、旋转,如果修改操作较多,比较耗时,并且B+树可能会有页分裂、合并等操作,时间开销上面会更大
 
十一、如何使用MySQL的Explain语句进行查询分析?
直接在查询语句前面加上 Explain关键字即可
输出的列表示的含义:
 1. id
- 作用:标识查询中 
SELECT的执行顺序。 - 规则:
id相同:执行顺序从上到下(如多表关联查询)。id不同:值越大优先级越高,越先执行(如子查询)。id为NULL:表示是其他查询的联合结果(如UNION)。
 
2. select_type
- 作用:表示 
SELECT的类型,反映查询的复杂度。 - 常见值:
- SIMPLE:简单查询(无子查询或 
UNION)。 - PRIMARY:外层主查询。
 - SUBQUERY:子查询中的第一个 
SELECT。 - DERIVED:派生表(例如 
FROM子句中的子查询)。 - UNION:
UNION中的第二个或之后的SELECT。 - UNION RESULT:
UNION的结果集。 
 - SIMPLE:简单查询(无子查询或 
 
3. type
- 作用:数据访问方式,性能从优到劣排序如下:
- system:表中仅一行数据(系统表)。
 - const:通过主键或唯一索引查到一行数据(如 
WHERE id = 1)。 - eq_ref:唯一索引关联(如 
JOIN时使用主键匹配)。 - ref:非唯一索引查找(可能返回多行)。
 range(优化目标):索引范围扫描(如BETWEEN、IN)。- index:全索引扫描(遍历索引树)。
 - ALL:全表扫描(需优化)。
 
 
4. table
- 作用:显示当前行操作的表名。
 - 特殊值:
:派生表(来自 id 为 N 的子查询)。:UNION结果(由 id 为 M 和 N 的查询合并)。
 
5. possible_keys
- 作用:可能用到的索引列表。
 - 注意:若为 
NULL,说明没有适合的索引,需检查表结构或查询条件。 
6. key
- 作用:实际使用的索引。
 - 注意:
- 可能不在 
possible_keys中(优化器可能选择更优索引)。 - 若为 
NULL,表示未使用索引(全表扫描)。 
 - 可能不在 
 
7. key_len
- 作用:索引使用的字节数,反映索引字段的利用率。
 - 计算规则:
- 字符串类型:
长度 × 字符集字节数(如VARCHAR(255) UTF8MB4最大长度为255 × 4 + 2 = 1022)。 - 数值/时间类型:固定长度(如 
INT为 4 字节)。 
 - 字符串类型:
 
8. ref
- 作用:显示与索引比较的列或常量。
 - 常见值:
const:常量值(如WHERE id = 1)。- 列名(如 
WHERE t1.col = t2.col)。 
 
9. rows
- 作用:预估需要扫描的行数(越小越好)。
 - 注意:基于统计信息估算,可能与实际值有偏差。
 
10. partitions
- 作用:匹配的分区(若表未分区则为 
NULL)。 - 适用场景:针对分区表的查询优化。
 
11. filtered
- 作用:查询条件过滤后剩余数据的百分比。
 - 用途:估算与其他表关联时需处理的行数(
rows × filtered)。 
12. Extra
- 作用:附加信息,反映执行细节。
 - 常见值:
 - Using where:使用 
WHERE过滤数据。 - Using index:覆盖索引(无需回表)。
 - Using temporary:使用临时表(常见于排序或分组)。
 - Using filesort:外部排序(需优化索引)。
 - Using join buffer:使用连接缓冲区(关联字段无索引)。
 
十二、MySQL中如何进行SQL调优?
通过慢查询日志找到执行速度比较慢的查询语句然后利用explain分析查询语句的执行计划,优化查询语句。
- 合理设计索引,多用联合索引来代替单个索引,尽量走索引覆盖查询,避免回表次数
 - 避免索引失效的几种情况
- 避免使用 select *
 - 遵循最左匹配原则来查询
 - 避免索引列上的强制类型转换
 - 避免使用前缀模糊查询 like ‘%…’
 - 避免索引列参与计算
 
 - 利用Redis缓存来优化MySQL效率,将频繁访问的数据放到缓存中减少数据库的压力。
 
十三、详细描述MySQL的B+树中查询数据的全过程
首先讲一下B+树的基本结构:
- 非叶子节点:B+树的非叶子节点存储的是键值(索引列)和指向子节点的指针
 - 叶子节点:B+ 树的叶子节点存储实际的数据行(如果是辅助索引的话这里存储的是主键id,需要回表查询,这里忽略这种情况,我们默认是查聚簇索引)。所有的叶子节点包含所有的数据,且用双向链表连接。
查找过程: - 从根节点出发(根节点常驻内存),根据比较数据键值和节点中存储的索引键值,确定数据落在它的哪一个子节点中(也就是哪个区间中)
 - 确定分支后,将该子节点读入内存在进行相同的比较去寻找该数据在下一层中的哪个节点中。
 - 定位到叶子节点后,叶子节点存储实际的数据行记录,但是一页又16KB大小,存储的数据行很多
 - 叶子节点中数据行以组的形式划分,利用页目录结构,通过二分查找定位到对应的组
 - 再利用链表遍历找到对应的数据行
 
十四、MySQL中 count(*)、count(1)、count(字段名) 有什么区别?
count(*):是统计表中所有行的行数,包括null值。性能最高
 count(1):也是统计数据的行数,包括null,性能和上面那个差不多
 count(字段名):统计该字段非null的行数。效率略差
对于count(字段名):该查询就是全表扫描(如果对应的字段没有索引,如果有索引则使用索引),正常情况下还要判断字段是否是null值。所以效率稍微低。
十五、MySQL 中 varchar 和 char 有什么区别?
- varchar(n):可变长度的字符串。存储的字符串长度与实际长度相等,并且在存储数据时会额外增加1~2个字节(字符串长度超过255,则使用两个字节)用于存储字符串的长度信息。
 - char(n):固定长度的字符串,一般用于存储固定长度的数据,例如手机号。MySQL也会在字符串的末尾填充空格已达到指定长度
 
理论上来讲 char 会比 varchar 快,因为varchar长度不固定,处理需要多一次运算。不过除非是像手机号这种固定长度的数据才会用char,其他都建议使用varchar。
varchar 支持的最大长度:
 因为最大行长度是 65535字节,如果允许为null,则需要1 bit 标记是否未 null(MySQL 对于null值是额外用一个null值列表存储的。当前只有一个varchar字段,则需要用 1 个 bit 标记它的null值组成null值列表),又因为varchar列的长度是可变的,需要使用1~2个字节(字符串长度超过255,则使用两个字节)用于存储字符串的长度信息。
 所以支持的最大长度是:65535 - 2 = 65533,如果允许为 null 则是 65532。
 以上是字节数,实际的字符又取决于使用的字符集:
- UTF-8字符集:每个字符最大占用3字节。最大字符数大约:21844(65533 ÷ 3)
 - UTF-16字符集:每个字符通常占用2字节。最大字符数大约:32766(65533 ÷ 2)
 - varchar(n):其中的 n 表示的是 字符的个数,而不是字节数。
 
十六、MySQL 是如何实现事务的?
MySQL主要是使用:锁、redo log、undo log、MVCC 来实现事务。
 事务特性包括四个(ACID):
- 原子性(Atomicity):一个事务是一个不可分割的最小单元,保证事务中的命令同时成功或失败
 - 一致性(Consistency):事务完成时,所有的数据都保持一直状态
 - 隔离性(Isolation):数据库系统提供的隔离机制,保证食物在不受外部并发影响的独立环境下运行
 - 持久性(Durability):事务一旦提交或回滚,对数据库中数据的改变是永久性的
 
如何实现:
- 使用 redo log 和 undo log 来实现事务的原子性、一致性、持久性
 - 使用 锁 和 MVCC 来实现事务的隔离性
 
redo log(维护持久性):
- 重做日志,记录的是事务提交时数据页的物理修改,用来实现事务的持久性。
 - 该日志由两部分组成:
- 重做日志缓存(redo log buffer)
存在内存中,可以设置一个刷新到磁盘的频率 - 重做日志文件(redo log file)
存在磁盘中,当事务提交后会把所有的修改信息都存到日志文件中,用于在刷新脏页到磁盘过程中发生错误时恢复数据使用 
 - 重做日志缓存(redo log buffer)
 
当客户端操作一系列语句后,会先在缓冲区(Buffer Pool)进行操作,如果缓冲区没有数据,则加载磁盘中的数据到缓冲区。
 当在缓冲区操作完成之后,需要刷新脏页到磁盘,如果这个过程发生错误(脏页数据丢失,还没来得及刷新到磁盘),那么 redo log 就发挥作用了,记录你的操作并完成刷新脏页到磁盘。


