MySQL索引优化:被面试官问懵后,我终于搞懂了B+树和最左前缀原则
大家好,我是小明,一名刚毕业三个月的Java后端开发。今天想和大家分享一个让我既尴尬又成长的故事。上个月,我去一家心仪的公司面试后端开发岗位。面试进行得很顺利,自我介绍、项目经验、Java基础都对答如流,面试官频频点头,我心里暗喜:这次稳了!结果面试官话锋一转,问了我一个问题:“小明同学,我看你的项目经验里写到了数据库优化。你能给我解释一下,为什么这条SQL查询需要0.5秒才能返回结果吗?我盯着这
MySQL索引优化:被面试官问懵后,我终于搞懂了B+树和最左前缀原则
一名Java实习生的数据库性能优化实战笔记,从"这SQL怎么这么慢"到"优化只需0.01秒"的蜕变
前言:那次让我"社死"的面试
大家好,我是小明,一名刚毕业三个月的Java后端开发。今天想和大家分享一个让我既尴尬又成长的故事。
上个月,我去一家心仪的公司面试后端开发岗位。面试进行得很顺利,自我介绍、项目经验、Java基础都对答如流,面试官频频点头,我心里暗喜:这次稳了!
结果面试官话锋一转,问了我一个问题:
“小明同学,我看你的项目经验里写到了数据库优化。你能给我解释一下,为什么这条SQL查询需要0.5秒才能返回结果吗?”
说完,他在我面前写了一条SQL语句:
SELECT * FROM orders WHERE order_time LIKE '%2024-01%' ORDER BY update_time DESC LIMIT 100;
我盯着这条SQL,脑子里一片空白。什么?索引?我知道索引啊,数据库查询变快的就是那个东西对吧?但是具体怎么用、为什么这条SQL慢、我该怎么优化,我是真的不知道。
我支支吾吾地说:“可能是…没有建索引吧…”
面试官微微一笑,又问:“那你知道为什么加了索引之后,这条SQL依然很慢吗?”
我当时的表情,大概是这样的:😰
结果可想而知,面试官礼貌地送走了我。回到出租屋,我躺在床上,翻来覆去睡不着,心里一直想着那个问题:为什么加了索引还慢?
从那天起,我开始了为期两周的MySQL索引优化学习之旅。今天这篇文章,就是我这两周学习的总结,希望能帮助到和我一样曾经对索引一知半解的朋友们。
一、重新认识索引:它到底是什么?
在开始学习之前,我对索引的理解就是简单的"查询加速器"。面试回来后,我第一件事就是问自己:索引到底是什么?为什么它能让查询变快?
1.1 生活化的比喻
为了理解索引,我导师给我举了一个特别形象的比喻:
“小明啊,你可以把数据库想象成一个图书馆,里面有几十万本书(数据行)。没有索引的时候,你要找一本特定的书,比如《Java从入门到放弃》,管理员只能一本一本地翻给你看,这就是全表扫描。如果有索引呢?索引就像图书馆的图书分类目录,你可以先查目录知道这本书在第几排第几个书架,然后直接走过去拿。这就是索引的作用——减少查找的次数。”
这个比喻让我恍然大悟。索引的本质就是建立一种数据结构,能够快速定位到我们要找的数据,而不是一条一条地去遍历。
1.2 索引的物理结构
后来我了解到,在MySQL中,索引是实实在在存储在磁盘上的数据结构。不同的存储引擎实现方式不同,但我们最常用的InnoDB引擎使用的是B+树这种数据结构。
这里有个关键点需要理解:数据库的数据是存在磁盘上的,而磁盘IO的效率比内存慢得多。所以索引设计的核心目标就是用最少的磁盘IO次数找到我们需要的数据。
如果没有索引,查询一条数据可能需要:
- 读取第一条记录,判断是否符合条件
- 读取第二条记录,判断是否符合条件
- …
- 读取第100万条记录,终于找到了!
如果有索引,查询过程变成:
- 读取索引根节点
- 根据根节点的信息,读取下一层节点
- …
- 定位到具体的数据行
想象一下,如果是100万条数据,没有索引可能需要IO 100万次,有索引可能只需要IO 4次(假设B+树高度为4),这个差距是巨大的。
1.3 索引的代价
但是,索引不是免费的午餐。我导师反复提醒我:索引虽然能加速查询,但会拖累写入操作。
每次我们执行INSERT、UPDATE、DELETE操作时,数据库不仅要修改数据本身,还要维护对应的索引结构。这就像图书馆的图书分类目录:每次放入一本新书,不仅要把书放到书架上,还要在目录卡上增加一条记录。书越多,目录维护的成本就越高。
所以,索引不是越多越好。好的索引策略需要在查询速度和写入速度之间找到平衡点。
二、B+树:为什么MySQL选择它?
面试官问我的第二个问题是:"你知道为什么MySQL用B+树而不是其他数据结构做索引吗?"我当时完全答不上来,后来查了资料才明白,这里面大有学问。
2.1 常见数据结构的对比
在学习B+树之前,我先是了解了几种常见的数据结构:
二叉查找树(Binary Search Tree)
二叉查找树的查询效率是O(logN),看起来很不错。但问题是,二叉树会"偏科"——如果数据是有序的,二叉树会退化成链表,查询效率变成O(N)。更关键的是,二叉树的高度太高了。假设有100万条数据,二叉树的高度可能超过20层,这意味着一次查询需要20次磁盘IO。
平衡二叉树(AVL树)
AVL树通过旋转操作保持平衡,解决了二叉树退化成链表的问题。但AVL树的要求太严格了——任何节点的左右子树高度差不能超过1。这导致AVL树的插入操作需要大量的旋转来保持平衡,写入效率不高。而且20层的深度问题依然存在。
B树(Balance Tree)
B树是一种多路查找树,每个节点可以有多个子节点和多个数据项。假设每个节点可以存100个数据项,3层的B树就能存100万数据,查询只需要3次IO。这比二叉树强多了!但B树有个问题:它的数据既存在索引节点,也存在叶子节点。而且范围查询时,需要中序遍历,效率不够高。
B+树
B+树是B树的改进版,它的核心改进是:
- 非叶子节点只存索引(不存实际数据),这样每个节点能存更多的索引
- 所有数据都在叶子节点,叶子节点之间用双向链表连接
这样设计的好处是:
- 非叶子节点更小,内存能缓存更多节点,减少IO次数
- 叶子节点链表结构让范围查询非常高效
- 查询路径稳定,IO次数可预测
2.2 B+树的可视化理解
为了理解B+树,我画了一个简单的示意图。假设我们有一个用户表,存储了用户ID和用户姓名,基于用户ID建立了主键索引:
[100 | 200 | 300]
/ | \
[1-100] [101-200] [201-300] [301-400]
/ \ / \ / \ / \
... ... ... ... ... ... ... ...
叶子节点(双向链表连接):
[1, 张三] → [2, 李四] → [3, 王五] → ... → [99, 赵六] → [100, 钱七]
这个图虽然简化了很多,但能说明几个关键点:
- 非叶子节点(索引层):存储的是索引值和指向下一层的指针,不存储实际数据
- 叶子节点:存储完整的数据行,并且按照索引值从小到大排列
- 链表结构:叶子节点之间有双向指针,范围查询时可以直接遍历链表
假设我们要查询ID为99的用户:
- 从根节点开始,发现99小于100,走左边的分支
- 到达中间层节点,发现99大于1且小于100,走中间分支
- 到达叶子节点,遍历链表找到ID为99的记录
整个过程只需要3次IO。
2.3 面试题敲黑板
经过学习,我总结了几个关于B+树的高频面试题:
问题一:为什么索引结构不用哈希表?
哈希表查询是O(1),看起来很快。但哈希表有两个致命缺陷:第一,不支持范围查询,比如"查询ID大于100的用户"就做不了;第二,哈希碰撞会导致性能退化。更重要的是,哈希表是无序的,无法支持ORDER BY操作。
问题二:B+树的高度通常是多少?
在InnoDB中,每个节点默认是16KB。假设索引键是8字节的bigint(主键ID),加上6字节的指针,每个节点大概能存1000个左右的键。3层B+树就能存10亿条数据。所以大多数情况下,B+树的高度是1-4层,查询只需要1-4次IO。
问题三:为什么建议主键自增?
主键自增的话,每次插入新记录都是追加到叶子节点的末尾,不需要移动已有数据,也不需要分裂节点。如果主键是无序的UUID,每次插入都可能需要找到合适的位置,甚至触发节点分裂,性能会差很多。
三、EXPLAIN:优化SQL的瑞士军刀
面试回来后,我导师(是的,我们公司也有导师制度)告诉我:想优化SQL,第一步就是学会用EXPLAIN。
3.1 EXPLAIN是什么?
EXPLAIN是MySQL提供的一个命令,它可以模拟优化器执行SQL查询,返回查询的执行计划。简单来说,EXPLAIN会告诉我们:这条SQL是怎么执行的、用了哪个索引、扫描了多少行数据。
使用方法超级简单:
EXPLAIN SELECT * FROM orders WHERE order_no = '202401150001';
执行后,MySQL会返回一张表,包含十几个字段。这些字段里藏着优化SQL的关键信息。
3.2 关键字段解析
我花了很长时间研究EXPLAIN返回的各个字段,最后发现最重要的就是这几个:
type字段:访问类型
type字段表示MySQL如何查找表中的行,按从好到坏排序:
| type值 | 含义 | 说明 |
|---|---|---|
| system | 系统表 | 只有一行数据 |
| const | 常量匹配 | 最多匹配一行,比如主键查询 |
| eq_ref | 唯一索引查找 | 使用主键或唯一索引 |
| ref | 普通索引查找 | 使用普通索引,可能多行 |
| range | 范围查询 | 使用索引查一个范围 |
| index | 全索引扫描 | 遍历整个索引树 |
| ALL | 全表扫描 | 遍历整个数据表 |
好的SQL应该达到const或eq_ref,最差也要是index,如果是ALL就需要优化了。
key字段:实际使用的索引
这个字段告诉我们查询到底用没用索引,用了哪个索引。如果显示NULL,说明没有使用索引,是全表扫描,需要重点优化。
rows字段:扫描的行数
这个字段是优化器预估的需要扫描的行数。注意是预估,不是实际。这个数字越小越好。如果一张表有100万行,rows显示100万,说明是全表扫描。
Extra字段:额外信息
这个字段包含很多重要信息:
- Using filesort:排序没有使用索引,需要额外排序
- Using temporary:使用了临时表,可能需要优化
- Using index:覆盖索引,不需要回表查询
- Using where:在存储引擎返回数据后,又用WHERE条件过滤
3.3 实战演示
让我用一个实际的例子来演示EXPLAIN的用法。假设我们有一张订单表:
CREATE TABLE orders (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
order_no VARCHAR(32) NOT NULL,
user_id BIGINT NOT NULL,
order_time DATETIME NOT NULL,
status TINYINT NOT NULL DEFAULT 0,
amount DECIMAL(10,2) NOT NULL,
update_time DATETIME NOT NULL,
INDEX idx_user_id (user_id),
INDEX idx_order_time (order_time),
INDEX idx_order_no (order_no)
) ENGINE=InnoDB;
现在执行几条SQL,看看EXPLAIN的结果:
案例一:主键查询
EXPLAIN SELECT * FROM orders WHERE id = 10086;
结果:
| id | select_type | table | type | key | key_len | rows | Extra |
|---|---|---|---|---|---|---|---|
| 1 | SIMPLE | orders | const | PRIMARY | 8 | 1 | NULL |
type是const,说明通过主键直接定位到唯一的一行,查询效率最高。
案例二:普通索引查询
EXPLAIN SELECT * FROM orders WHERE user_id = 100;
结果:
| id | select_type | table | type | key | key_len | rows | Extra |
|---|---|---|---|---|---|---|---|
| 1 | SIMPLE | orders | ref | idx_user_id | 8 | 156 | NULL |
type是ref,说明通过普通索引user_id查询,返回156行(预估)。这里有个问题:查询完后,MySQL需要通过主键回表查询完整数据,所以Extra显示NULL。
案例三:范围查询
EXPLAIN SELECT * FROM orders WHERE order_time > '2024-01-01' AND order_time < '2024-01-31';
结果:
| id | select_type | table | type | key | key_len | rows | Extra |
|---|---|---|---|---|---|---|---|
| 1 | SIMPLE | orders | range | idx_order_time | 5 | 5234 | Using index condition |
type是range,使用索引查询一个范围,rows显示5234行。Extra显示Using index condition,说明优化器用到了索引条件下推(ICP)功能。
案例四:最左前缀原则失效
EXPLAIN SELECT * FROM orders WHERE order_no LIKE '%20240115%';
结果:
| id | select_type | table | type | key | key_len | rows | Extra |
|---|---|---|---|---|---|---|---|
| 1 | SIMPLE | orders | ALL | NULL | NULL | 125678 | Using where |
type是ALL!说明根本没有使用索引,是全表扫描。rows显示125678行,几乎扫描了整个表。这就是典型的索引失效场景。
面试官问我的那条SQL,就是这种情况。
3.4 面试题敲黑板
问题:怎么用EXPLAIN判断索引是否生效?
看key字段。如果key显示的是索引名,说明用到了索引;如果显示NULL,说明没有使用索引,是全表扫描。
问题:Extra显示Using filesort怎么优化?
Using filesort说明排序没有走索引。需要检查ORDER BY的字段是否在索引中,或者调整索引的顺序,让排序字段符合最左前缀原则。
四、最左前缀原则:索引设计的核心法则
如果说B+树是索引的"内功",那么最左前缀原则就是索引的"招式"。这是面试中经常被问到的问题,也是实际开发中最需要掌握的内容。
4.1 什么是最左前缀原则?
最左前缀原则的意思是:在创建联合索引时,索引字段的顺序非常重要,查询时必须从左到右使用索引字段,才能命中索引。
举个例子,我们创建一个联合索引:
INDEX idx_user_time (user_id, order_time, status)
这个索引包含三个字段:user_id、order_time、status。
根据最左前缀原则,以下查询能使用索引:
-- 能使用索引(使用第1个字段)
SELECT * FROM orders WHERE user_id = 100;
-- 能使用索引(使用第1、2个字段)
SELECT * FROM orders WHERE user_id = 100 AND order_time > '2024-01-01';
-- 能使用索引(使用全部三个字段)
SELECT * FROM orders WHERE user_id = 100 AND order_time > '2024-01-01' AND status = 1;
以下查询不能使用索引:
-- 不能使用索引(跳过第1个字段,直接查第2个)
SELECT * FROM orders WHERE order_time > '2024-01-01';
-- 不能使用索引(跳过第1、2个字段,直接查第3个)
SELECT * FROM orders WHERE status = 1;
-- 不能使用索引(范围查询后,字段失效)
SELECT * FROM orders WHERE user_id < 100 AND order_time > '2024-01-01';
第三个例子需要特别说明:如果user_id使用了范围查询(比如<、>、BETWEEN),那么order_time字段的索引就失效了。这是因为B+树的索引结构决定的:范围查询会找到user_id<100的所有节点,但这些节点的order_time是乱序的,无法再使用索引快速查找。
4.2 索引失效的常见场景
除了最左前缀原则,还有几种常见的索引失效场景:
场景一:在索引列上使用函数
-- 索引失效
SELECT * FROM orders WHERE YEAR(order_time) = 2024;
-- 索引有效
SELECT * FROM orders WHERE order_time >= '2024-01-01' AND order_time < '2024-02-01';
原因:在索引列上使用函数,会破坏索引的有序性,MySQL无法使用B+树的有序特性。
场景二:字符串不加引号
-- 假设order_no是varchar类型
-- 索引可能失效
SELECT * FROM orders WHERE order_no = 202401150001;
-- 索引有效
SELECT * FROM orders WHERE order_no = '202401150001';
原因:类型转换会破坏索引的有序性。
场景三:LIKE以百分号开头
-- 索引失效
SELECT * FROM orders WHERE order_no LIKE '%20240115';
-- 索引有效
SELECT * FROM orders WHERE order_no LIKE '20240115%';
-- 索引有效(只查索引列)
SELECT order_no FROM orders WHERE order_no LIKE '20240115%';
原因:LIKE '%abc’是从右向左匹配的,无法利用索引的有序性。
场景四:使用OR连接
-- 假设order_time有索引,user_id有索引
-- 可能索引失效
SELECT * FROM orders WHERE order_time > '2024-01-01' OR user_id = 100;
-- 改用AND,索引有效
SELECT * FROM orders WHERE order_time > '2024-01-01' AND user_id = 100;
原因:OR会导致只要有一个条件没索引,就使用全表扫描。
4.3 如何设计高效的联合索引?
知道了最左前缀原则,就能明白联合索引的设计是有讲究的。我总结了一个口诀:区分度高的放左边,范围查询放右边。
区分度:指的是字段的唯一值数量占总行数的比例。比例越高,区分度越好。比如user_id的区分度通常比status高(因为用户ID唯一,状态只有几种)。
设计原则:
- 等值查询的字段放左边:因为等值查询可以精确匹配,不会导致后续字段失效
- 区分度高的放左边:这样过滤掉的数据多,后续查询的数据量小
- 范围查询的字段放右边:范围查询会导致后续字段失效,所以放在最后
举个例子,如果我们的查询经常是:
SELECT * FROM orders WHERE user_id = ? AND order_time > ? AND status = ? ORDER BY order_time DESC;
最佳索引应该是:
INDEX idx_user_status_time (user_id, status, order_time)
而不是:
INDEX idx_user_time_status (user_id, order_time, status)
因为如果order_time放在中间,status的索引就失效了。
五、覆盖索引:让查询飞起来
在研究EXPLAIN的时候,我发现Extra字段经常出现"Using index",这是什么意思呢?
5.1 什么是回表?
在InnoDB中,有两种索引:聚簇索引(Clustered Index)和二级索引(Secondary Index)。
聚簇索引:就是主键索引,叶子节点存储的是完整的数据行。
二级索引:也叫非聚簇索引,叶子节点存储的是索引键和主键值,不存储完整数据。
当我们通过二级索引查询时,过程是这样的:
- 在二级索引树中找到对应的记录,获取主键ID
- 通过主键ID回到聚簇索引,查找完整数据
- 返回完整数据
这个过程叫做回表。回表意味着需要两次索引查找,性能比只查一次索引要差。
5.2 什么是覆盖索引?
覆盖索引的意思是:查询的所有数据都能在索引中找到,不需要回表。
比如我们有一个索引是INDEX idx_order_no (order_no),如果查询的是:
SELECT order_no, user_id FROM orders WHERE order_no = '202401150001';
这条SQL的order_no和user_id都能在idx_order_no索引中找到(因为user_id是主键,会被包含在二级索引的叶子节点中),所以不需要回表。
如果查询的是:
SELECT * FROM orders WHERE order_no = '202401150001';
这就需要回表了,因为status、amount等字段不在索引中。
5.3 覆盖索引的实战应用
怎么利用覆盖索引来优化SQL呢?
优化前(需要回表):
SELECT id, order_no, update_time FROM orders WHERE order_no LIKE '202401%' LIMIT 100;
这条SQL虽然用了索引,但因为LIKE以百分号开头,索引失效了。
优化后(覆盖索引):
SELECT id, order_no, update_time FROM orders
WHERE order_no >= '20240100000000' AND order_no < '20240200000000'
LIMIT 100;
这样写可以利用order_no的索引,而且查询的字段都在索引中,不需要回表。
另一个例子:
优化前:
SELECT * FROM orders WHERE status = 1 ORDER BY order_time DESC LIMIT 100;
Extra显示Using filesort,说明排序没有走索引。
添加覆盖索引:
ALTER TABLE orders ADD INDEX idx_status_time (status, order_time);
优化后:
SELECT * FROM orders WHERE status = 1 ORDER BY order_time DESC LIMIT 100;
Extra显示Using index,说明使用了覆盖索引,不需要回表,也不需要额外排序。
六、性能优化:常见场景与解决方案
学完了理论知识,我用几个实际的优化案例来练练手。这些案例都是我在工作中或者模拟环境中遇到的。
6.1 案例一:分页查询优化
分页查询是开发中非常常见的场景。当offset很大时,查询会越来越慢:
-- 传统分页
SELECT * FROM orders ORDER BY create_time DESC LIMIT 1000000, 20;
这条SQL的原理是:先扫描前100万条记录,扔掉它们,然后取20条。这100万条记录的扫描是实实在在发生的,性能当然差。
优化方案一:基于ID的分页
-- 上一页最后一条记录的ID是1000999
SELECT * FROM orders WHERE id > 1000999 ORDER BY id ASC LIMIT 20;
这种方法利用主键索引,查询20条数据只需要20次IO。
优化方案二:延迟关联
SELECT o.* FROM orders o
INNER JOIN (SELECT id FROM orders ORDER BY create_time DESC LIMIT 1000000, 20) t
ON o.id = t.id;
这个查询分两步:第一步只查主键,Using index很快;第二步用主键关联查完整数据。这种方法比直接查快很多。
6.2 案例二:COUNT(*)优化
COUNT(*)也是经常被优化的对象:
-- 慢
SELECT COUNT(*) FROM orders WHERE status = 1;
如果status没有索引,这条SQL会全表扫描。
优化方案:添加索引
ALTER TABLE orders ADD INDEX idx_status (status);
这样COUNT(*)只需要扫描索引树,不需要扫描数据行。
注意:COUNT()和COUNT(column)是不同的。COUNT()统计所有行,包括NULL;COUNT(column)只统计非NULL的行。
6.3 案例三:JOIN优化
多表JOIN是性能问题的重灾区:
SELECT o.*, u.name, p.product_name
FROM orders o
JOIN users u ON o.user_id = u.id
JOIN products p ON o.product_id = p.id
WHERE o.status = 1
AND u.register_time > '2024-01-01';
这条SQL涉及三张表,JOIN顺序不对可能导致灾难性的性能问题。
优化方案一:确保JOIN的字段有索引
-- orders表的user_id和product_id应该有索引
-- users表的id是主键,products表的id是主键
优化方案二:用小表驱动大表
-- 先查符合条件的用户(数据量小)
SELECT id, name FROM users WHERE register_time > '2024-01-01';
-- 再查这些用户的订单
SELECT o.*, u.name, p.product_name
FROM (SELECT id, name FROM users WHERE register_time > '2024-01-01') u
JOIN orders o ON u.id = o.user_id
JOIN products p ON o.product_id = p.id
WHERE o.status = 1;
优化方案三:关闭不必要的JOIN
-- 如果只需要订单信息,不需要用户和产品详情
-- 只查orders表,加个索引
SELECT * FROM orders WHERE status = 1;
6.4 面试题敲黑板
问题:怎么优化深分页?
方案一:基于ID的游标分页,查询条件是id > last_id
方案二:延迟关联,先查主键再关联
方案三:记录上一页的某个字段值,用WHERE字段 > value来分页
问题:COUNT(*)慢怎么办?
方案一:确认COUNT的字段有索引
方案二:考虑是否真的需要精确COUNT,可以用近似值
方案三:使用缓存计数,但要注意数据一致性
七、总结:索引优化的检查清单
经过这两周的学习,我总结了一份索引优化的检查清单,每次写SQL或者优化SQL时都可以对照一下:
7.1 索引创建检查
| 检查项 | 说明 | 是否通过 |
|---|---|---|
| 主键是否自增 | 自增主键插入效率高 | ☐ |
| 常用查询字段是否建索引 | WHERE、JOIN、ORDER BY的字段 | ☐ |
| 区分度低的字段是否单独建索引 | 区分度低的字段不适合做联合索引的引导列 | ☐ |
| 联合索引顺序是否正确 | 最左前缀原则,区分度高的放左边 | ☐ |
| 是否有多余的索引 | 重复索引、冗余索引要删除 | ☐ |
7.2 SQL编写检查
| 检查项 | 说明 | 是否通过 |
|---|---|---|
| 是否命中索引 | EXPLAIN的key字段不为NULL | ☐ |
| 是否避免索引失效 | 没有函数、没有隐式类型转换、LIKE不以%开头 | ☐ |
| 是否覆盖索引 | 查询字段都在索引中,不需要回表 | ☐ |
| 是否避免SELECT * | 只查询需要的字段 | ☐ |
| 分页是否高效 | 深分页用了合适的优化方案 | ☐ |
7.3 性能监控检查
| 检查项 | 说明 | 是否通过 |
|---|---|---|
| EXPLAIN的type是否良好 | 至少是index,不要是ALL | ☐ |
| 扫描行数是否合理 | rows字段是否在可接受范围 | ☐ |
| 是否出现Using filesort | 排序是否走索引 | ☐ |
| 是否出现Using temporary | 是否避免了临时表 | ☐ |
八、后记:从"小白"到"入门"的感悟
写完这篇文章,我不禁想起一个月前面试时那个尴尬的场景。当时的我,面对面试官的问题,只能支支吾吾地说"可能没有建索引"。
现在回想起来,当时的自己确实太浮躁了。以为会写几个SQL查询,会用MyBatis就懂数据库了。其实数据库是一个博大精深的领域,里面的水很深。
这两周的学习,让我明白了几个道理:
第一,原理比用法更重要。知道了B+树的结构,才能理解最左前缀原则;理解了最左前缀原则,才能设计出高效的索引。只知道"索引能让查询变快"是远远不够的。
第二,工具要用对地方。EXPLAIN是优化SQL的神器,但如果不知道怎么看、看什么,再好的工具也是摆设。
第三,实践出真知。看再多的理论,也不如实际跑几条SQL、看几个EXPLAIN结果来得深刻。
最后,我想对和我一样正在学习后端开发的同学们说:技术路很长,不要怕犯错,不要怕丢脸。每一次被问倒,都是成长的机会。就像我这次面试,虽然失败了,但让我真正开始认真对待数据库优化这个领域。
希望这篇文章能对你有所帮助。如果觉得有用,记得点赞、收藏、关注哦!你们的支持是我继续创作的最大动力!
更多推荐
所有评论(0)