
BATJTMD 等大厂的面试难度愈来愈高,但不管从大厂仍是到小公司,一直不变的重点就是对 SQL 优化经验的考察。一提到数据库,面试官就会问“先说一说你对 SQL 优化的看法吧?”。前端

图片来自 Pexelsjava
SQL 优化已经成为衡量程序猿优秀与否的硬性指标,甚至在各大厂招聘岗位职能上都有明码标注,若是是你,在这个问题上能吊打面试官仍是会被吊打呢?python
有朋友疑问到,SQL 优化真的有这么重要么?以下图所示,SQL 优化在提高系统性能中是:成本最低和优化效果最明显的途径。web
优化成本:硬件>系统配置>数据库表结构>SQL 及索引。面试
优化效果:硬件<系统配置<数据库表结构<SQL 及索引。数据库
String result = "嗯,不错,";
if ("SQL优化经验足") {
if ("熟悉事务锁") {
if ("并发场景处理666") {
if ("会打王者荣耀") {
result += "明天入职"
}
}
}
} else {
result += "先回去等消息吧";
}
Logger.info("面试官:" + result );
减小数据访问:设置合理的字段类型,启用压缩,经过索引访问等减小磁盘 IO。小程序
返回更少的数据:只返回须要的字段和数据分页处理,减小磁盘 IO 及网络 IO。缓存
减小交互次数:批量 DML 操做,函数存储等减小数据链接次数。服务器
减小服务器 CPU 开销:尽可能减小数据库排序操做以及全表查询,减小 CPU 内存占用。微信
利用更多资源:使用表分区,能够增长并行操做,更大限度利用 CPU 资源。
最大化利用索引。
尽量避免全表扫描。
减小无效数据的查询。
SELECT 语句,语法顺序以下:
1. SELECT
2. DISTINCT <select_list>
3. FROM <left_table>
4. <join_type> JOIN <right_table>
5. ON <join_condition>
6. WHERE <where_condition>
7. GROUP BY <group_by_list>
8. HAVING <having_condition>
9. ORDER BY <order_by_condition>
10.LIMIT <limit_number>
SELECT 语句,执行顺序以下:
FROM
<表名> # 选取表,将多个表数据经过笛卡尔积变成一个表。
ON
<筛选条件> # 对笛卡尔积的虚表进行筛选
JOIN <join, left join, right join...>
<join表> # 指定join,用于添加数据到on以后的虚表中,例如left join会将左表的剩余数据添加到虚表中
WHERE
<where条件> # 对上述虚表进行筛选
GROUP BY
<分组条件> # 分组
<SUM()等聚合函数> # 用于having子句进行判断,在书写上这类聚合函数是写在having判断里面的
HAVING
<分组筛选> # 对分组后的结果进行聚合筛选
SELECT
<返回数据列表> # 返回的单列必须在group by子句中,聚合函数除外
DISTINCT
# 数据除重
ORDER BY
<排序条件> # 排序
LIMIT
<行数限制>
如下 SQL 优化策略适用于数据量较大的场景下,若是数据量较小,不必以此为准,以避免多此一举。
避免不走索引的场景
SELECT * FROM t WHERE username LIKE '%陈%'
SELECT * FROM t WHERE username LIKE '陈%'
使用 MySQL 内置函数 INSTR(str,substr)来匹配,做用相似于 Java 中的 indexOf(),查询字符串出现的角标位置。
使用 FullText 全文索引,用 match against 检索。
数据量较大的状况,建议引用 ElasticSearch、Solr,亿级数据量检索速度秒级。
当表数据量较少(几千条儿那种),别整花里胡哨的,直接用 like '%xx%'。
以下:
SELECT * FROM t WHERE id IN (2,3)
以下:
SELECT * FROM t WHERE id BETWEEN 2 AND 3
以下:
-- 不走索引
select * from A where A.id in (select id from B);
-- 走索引
select * from A where exists (select * from B where B.id = A.id);
以下:
SELECT * FROM t WHERE id = 1 OR id = 3
以下:
SELECT * FROM t WHERE id = 1
UNION
SELECT * FROM t WHERE id = 3
以下:
SELECT * FROM t WHERE score IS NULL
以下:
SELECT * FROM t WHERE score = 0
-- 全表扫描
SELECT * FROM T WHERE score/10 = 9
-- 走索引
SELECT * FROM T WHERE score = 10*9
以下:
SELECT username, age, sex FROM T WHERE 1=1
⑦查询条件不能用 <> 或者 !=
以下:复合(联合)索引包含 key_part1,key_part2,key_part3 三列,但 SQL 语句没有包含索引前置列"key_part1",按照 MySQL 联合索引的最左匹配原则,不会走联合索引。
select col1 from table where key_part2=1 and key_part3=2
以下 SQL 语句因为索引对列类型为 varchar,但给定的值为数值,涉及隐式类型转换,形成不能正确走索引。
select col1 from table where col_varchar=123;
以下:
-- 不走age索引
SELECT * FROM t order by age;
-- 走age索引
SELECT * FROM t where age > 0 order by age;
第一步:根据 where 条件和统计信息生成执行计划,获得数据。
第二步:将获得的数据排序。当执行处理数据(order by)时,数据库会先查看第一步的执行计划,看 order by 的字段是否在执行计划中利用了索引。若是是,则能够利用索引顺序而直接取得已经排好序的数据。若是不是,则从新进行排序操做。
第三步:返回排序后的数据。
USE INDEX 在你查询语句中表名的后面,添加 USE INDEX 来提供但愿 MySQL 去参考的索引列表,就可让 MySQL 再也不考虑其余可用的索引。
例子: SELECT col1 FROM table USE INDEX (mod_time, name)...
IGNORE INDEX 若是只是单纯的想让 MySQL 忽略一个或者多个索引,可使用 IGNORE INDEX 做为 Hint。
例子: SELECT col1 FROM table IGNORE INDEX (priority) ...
FORCE INDEX 为强制 MySQL 使用一个特定的索引,可在查询中使用FORCE INDEX 做为 Hint。
例子: SELECT col1 FROM table FORCE INDEX (mod_time) ...
例如:
SELECT * FROM students FORCE INDEX (idx_class_id) WHERE class_id = 1 ORDER BY id DESC;
SELECT 语句其余优化
②避免出现不肯定结果的函数
③多表关联查询时,小表在前,大表在后
增删改 DML 语句优化
方法一:
insert into T values(1,2);
insert into T values(1,3);
insert into T values(1,4);
方法二:
Insert into T values(1,2),(1,3),(1,4);
减小 SQL 语句解析的操做,MySQL 没有相似 Oracle 的 share pool,采用方法二,只须要解析一次就能进行数据的插入操做。
在特定场景能够减小对 DB 链接次数。
SQL 语句较短,能够减小网络传输的 IO。
事务占用的 undo 数据块。
事务在 redo log 中记录的数据块。
释放事务施加的,减小锁争用影响性能。特别是在须要使用 delete 删除大量数据的时候,必须分解删除量并按期 commit。
③避免重复查询更新的数据
简单方法实现:
Update t1 set time=now() where col1=1;
Select time from t1 where id =1;
使用变量,能够重写为如下方式:
Update t1 set time=now () where col1=1 and @now: = now ();
Select @now;
写入操做优先于读取操做。
对某张数据表的写入操做某一时刻只能发生一次,写入请求按照它们到达的次序来处理。
对某张数据表的多个读取操做能够同时地进行。
LOW_PRIORITY 关键字应用于 DELETE、INSERT、LOAD DATA、REPLACE 和 UPDATE。
HIGH_PRIORITY 关键字应用于 SELECT 和 INSERT 语句。
DELAYED 关键字应用于 INSERT 和 REPLACE 语句。
查询条件优化
例如:
SELECT col1, col2, COUNT(*) FROM table GROUP BY col1, col2 ORDER BY NULL ;
SELECT col1 FROM customerinfo WHERE CustomerID NOT in (SELECT CustomerID FROM salesinfo )
尤为是当 salesinfo 表中对 CustomerID 建有索引的话,性能将会更好,查询以下:
SELECT col1 FROM customerinfo
LEFT JOIN salesinfoON customerinfo.CustomerID=salesinfo.CustomerID
WHERE salesinfo.CustomerID IS NULL
高效:
SELECT COL1, COL2, COL3 FROM TABLE WHERE COL1 = 10
UNION ALL
SELECT COL1, COL2, COL3 FROM TABLE WHERE COL3= 'TEST';
低效:
SELECT COL1, COL2, COL3 FROM TABLE WHERE COL1 = 10
UNION
SELECT COL1, COL2, COL3 FROM TABLE WHERE COL3= 'TEST';
简单的 SQL 容易使用到 MySQL 的 QUERY CACHE。
减小锁表时间特别是使用 MyISAM 存储引擎的表。
可使用多核 CPU。
案例 1:
select * from t where thread_id = 10000 and deleted = 0
order by gmt_create asc limit 0, 15;
select t.* from (select id from t where thread_id = 10000 and deleted = 0
order by gmt_create asc limit 0, 15) a, t
where a.id = t.id;
建表优化
②尽可能使用数字型字段(如性别,男:1 女:2),若只含数值信息的字段尽可能不要设计为字符型,这会下降查询和链接的性能,并会增长存储开销。
这是由于引擎在处理查询和链接时会 逐个比较字符串中每个字符,而对于数字型而言只须要比较一次就够了。
③查询数据量大的表 会形成查询缓慢。主要的缘由是扫描行数过多。这个时候能够经过程序,分段分页进行查询,循环遍历,将结果合并处理进行展现。
SELECT * FROM (SELECT ROW_NUMBER() OVER(ORDER BY ID ASC) AS rowid,*
FROM infoTab)t WHERE t.rowid > 100000 AND t.rowid <= 100050
做者:_陈哈哈
编辑:陶家龙
出处:https://sohu.gg/FGG98i
在公众号菜单中可自行获取专属架构视频资料,包括不限于 java架构、python系列、人工智能系列、架构系列,以及最新面试、小程序、大前端均无私奉献,你会感谢个人哈
往期热门文章:
1,架构的本质:如何打造一个有序的系统?
2,
分布式高可靠之负载均衡,今天看了你确定会
3,
分布式数据之缓存技术,一块儿来揭开其神秘面纱
4,分布式数据复制技术,今天就教你真正分身术
5,
数据分布方式之哈希与一致性哈希,我就是个神算子
6
,分布式存储系统三要素,掌握这些就离成功不远了
7
,想要设计一个好的分布式系统,必须搞定这个理论
8
,
分布式通讯技术之发布订阅,干货满满
9,
分布式通讯技术之远程调用:RPC
10
,秒杀系统每秒上万次下单请求,咱们该怎么去设计
本文分享自微信公众号 - 架构师修炼(jiagouxiulian)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。