京东二面:日常工作中,你是如何优化SQL的?

开发 前端
如果大家平时有优化过生产的慢SQL,有自己的一套排查那一套最好哈。如果没有的话,可以在自己搞个深分页,或者因为数据量、或者因为没加索引等原因,导致的慢SQL,然后按照这个思路去排查一遍。

大家好,我是田螺

我们去面试的时候,经常被问到,日常工作中,是如何优化SQL的。今天跟大家再聊聊哈。

这里应该如何去回答呢?可以从各种不同维度的,今天我再换个角度。

  • 加索引
  • 避免常见的索引不生效场景
  • 避免返回不必要的数据
  • 减少不必要的逻辑
  • 分批量进行思想
  • 读写分离
  • 优化sql结构
  • 分库分表
  • 性能优化分析神器—explain
  • 慢SQL排查思路

1. 加索引

很多时候,我们的慢查询,都是因为历史原因没有加索引,或者忘记加索引导致的。如果没有加索引的话,会导致全表扫描的。因此,应考虑在where的条件列,建立索引,尽量避免全表扫描。

反例:

select * from user_info where name ='捡田螺的小男孩公众号' ;

正例:

//添加索引
alter table user_info add index idx_name (name);

2. 避免常见的索引不生效场景

我之前整理了常见的十种索引不生效的场景,大家可以看看:

  1. 隐式的类型转换,索引失效
  2. 查询条件包含or,可能导致索引失效
  3. like通配符可能导致索引失效
  4. 查询条件不满足联合索引的最左匹配原则
  5. 在索引列上使用mysql的内置函数
  6. 对索引进行列运算(如,+、-、*、/)
  7. 索引字段上使用(!=或者<>),索引可能失效
  8. 索引字段上使用is null,is not null,索引可能失效
  9. 左右连接,关联的字段编码格式不一样
  10. 优化器选错了索引

3. 避免返回不必要的数据

这个点,我在昨天的文章,其实就提到一个点,包括尽量使用limit,避免不必要的返回

其实这不仅仅是一个点,而是一种思想,就是要什么查什么,而不是返回一些不必要的数据。还有:查询SQL尽量不要使用select *,而是select具体字段。也是这种思想。

反例子:

select * from employee;

正例子:

select id,name, age from employee;
  • select具体字段,节省资源、减少网络开销。
  • select * 进行查询时,很可能就不会使用到覆盖索引了,就会造成回表查询。

4. 减少不必要的逻辑

其实,尽量用 union all 替换 union,就是这种思想。

如果我们明知道,检索结果中不会有重复的记录,推荐union all 替换 union。

因为:

如果使用union,不管检索结果有没有重复,都会尝试进行合并,然后在输出最终结果前进行排序。如果已知检索结果没有重复记录,使用union all 代替union,这样会提高效率。

5. 分批量进行思想

我们更推荐批量查询、插入、删除。

反例:

for(User u :list){
 INSERT into user(name,age) values(#name#,#age#)   
}

正例:

//一次500批量插入,分批进行
insert into user(name,age) values
<foreach collectinotallow="list" item="item" index="index" separator=",">
    (#{item.name},#{item.age})
</foreach>

理由:

  • 批量插入性能好,更加省时间
  • 打个比喻: 假如你需要搬一万块砖到楼顶,你有一个电梯,电梯一次可以放适量的砖(最多放500),你可以选择一次运送一块砖,也可以一次运送500,你觉得哪个时间消耗大?

6. 读写分离

一般情况我们的数据库架构,都要做主从的,然后进行读写分离。主库主要负责写,和一些实时性比较高的读。而从库就负责读实时性要求不高的请求。

图片图片

这样的话,我们不用所有请求都到主库,大大降低了主库的压力。你试想一下,如果所有读请求都到主库,查询压力肯定很大,处理也会相对慢一点。

7. 优化sql结构、逻辑

有些时候,优化SQL结构,都能有一些预想不到的优化效果。

假设我们有个客户表和一个订单表。其中订单表有10万记录,客户表只有1000行记录。

现在要查询下单过的客户信息,可以这样写:

SELECT * FROM customers
WHERE id IN (
    SELECT customer_id FROM orders
);


in 查询会先执行内部查询部分 SELECT customer_id FROM orders,获得 orders 表(大表)中的所有 customer_id,然后在 customers 表(小表)中查找匹配的 id。

也可以这样实现:

SELECT * FROM customers c
WHERE EXISTS (
    SELECT 1 FROM orders o WHERE o.customer_id = c.id
);

EXISTS 会逐行扫描 customers 表(即小表),对每一行 c.id,在 orders 表(大表)中检查是否有 customer_id = c.id 的记录。

因为orders表的数据量比较大,因此这里用exists效果会相对更好一点。其实这就是小表驱动大表的思想。我们也只是调整SQL结构,用exists去替换in,优化效果也是比较明显的。

8. 分库分表

如果单表的数据量很大,达到百万甚至千万级别,我们这种时候,就是加了索引,可能效果也不是很明显。这时候我们可以考虑分库分表啦~~

分库分表一般都是依赖客户号、用户Id、或者时间来拆分。但是需要注意一下,分库分表存在的一些一些问题:

  • 事务问题
  • 跨库关联JOIN
  • 排序问题
  • 分页问题
  • 分布式ID选择

9. 性能优化分析神器—explain

之前我写SQL习惯的时候,有提到explain,就是每次写完查询SQL,都用explain看一下它的执行计划。

有些面试官会单独问这个,我们可以走面试官的路,让面试官无路可走。在回答SQL优化的时候,就把这个回答了。

一般在使用explain的时候,我们要关注:type、rows、filtered、extra、key。

9.1 type

type表示连接类型,查看索引执行情况的一个重要指标。以下性能从好到坏依次:system > const > eq_ref > ref > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

  • system:这种类型要求数据库表中只有一条数据,是const类型的一个特例,一般情况下是不会出现的。
  • const:通过一次索引就能找到数据,一般用于主键或唯一索引作为条件,这类扫描效率极高,,速度非常快。
  • eq_ref:常用于主键或唯一索引扫描,一般指使用主键的关联查询
  • ref : 常用于非主键和唯一索引扫描。
  • ref_or_null:这种连接类型类似于ref,区别在于MySQL会额外搜索包含NULL值的行
  • index_merge:使用了索引合并优化方法,查询使用了两个以上的索引。
  • unique_subquery:类似于eq_ref,条件用了in子查询
  • index_subquery:区别于unique_subquery,用于非唯一索引,可以返回重复值。
  • range:常用于范围查询,比如:between ... and 或 In 等操作
  • index:全索引扫描
  • ALL:全表扫描

9.2 rows

该列表示MySQL估算要找到我们所需的记录,需要读取的行数。对于InnoDB表,此数字是估计值,并非一定是个准确值。

9.3 filtered

该列是一个百分比的值,表里符合条件的记录数的百分比。简单点说,这个字段表示存储引擎返回的数据在经过过滤后,剩下满足条件的记录数量的比例。

9.4 extra

该字段包含有关MySQL如何解析查询的其他信息,它一般会出现这几个值:

  • Using filesort:表示按文件排序,一般是在指定的排序和索引排序不一致的情况才会出现。一般见于order by语句
  • Using index :表示是否用了覆盖索引。
  • Using temporary: 表示是否使用了临时表,性能特别差,需要重点优化。一般多见于group by语句,或者union语句。
  • Using where : 表示使用了where条件过滤.
  • Using index condition:MySQL5.6之后新增的索引下推。在存储引擎层进行数据过滤,而不是在服务层过滤,利用索引现有的数据减少回表的数据。

9.5 key

该列表示实际用到的索引。一般配合possible_keys列一起看。

10. 慢SQL排查思路

如果大家平时有优化过生产的慢SQL,有自己的一套排查那一套最好哈。如果没有的话,可以在自己搞个深分页,或者因为数据量、或者因为没加索引等原因,导致的慢SQL,然后按照这个思路去排查一遍。

  • 查看慢查询日志记录,分析慢SQL
  • explain分析SQL的执行计划
  • profile 分析执行耗时
  • Optimizer Trace分析详情
  • 确定问题并采用相应的措施

如果不熟悉的话,可以多操作几遍,尽量熟悉操作流程,在面试的时候,讲一下这个主要流程。

责任编辑:武晓燕 来源: 捡田螺的小男孩
相关推荐

2021-06-15 06:04:42

MySQL数据库索引

2024-09-29 08:21:11

2019-12-23 08:48:24

Java技术全局变量

2022-07-14 07:12:09

PythonPandasVBA

2020-07-15 07:53:41

VSCode Task脚本命令

2023-01-05 13:36:41

Script优化任务

2009-03-27 10:25:24

OracleDBA职责

2019-08-07 16:50:38

SQLjoingroup

2023-06-13 11:11:14

2024-09-29 09:50:05

2024-04-19 08:05:26

锁升级Java虚拟机

2021-06-27 06:25:14

代码优化技巧Java

2011-07-30 13:01:23

2019-12-02 13:36:57

SQLSQL优化数据库

2023-03-20 11:32:42

数据中心人工智能

2021-03-15 11:20:46

HTTPS优化前端

2021-01-26 01:55:24

HTTPS网络协议加密

2018-04-18 16:27:11

互联网技术学习

2019-12-11 15:21:12

PythonExcel浏览器

2017-11-24 12:35:14

数据科学统计学习机器学习
点赞
收藏

51CTO技术栈公众号