Commit 44941b0e authored by 王雷's avatar 王雷 😹

Update (SQL)优化分析与方法.md

parent 3688aea7
......@@ -101,69 +101,69 @@ limit
6. 条件类型,在执行查询时,作为变量的条件要尽可能的与要比对的数据列类型一致,减少数据库引擎转换数据类型的时间;
7. 如果要参与比对的条件需要用到函数,则尽量避免在数据列上使用,这样会造成索引失效,而形成全表扫描,增加数据量;
8. 如果查询条件中对同一个数据列有多个选项,不要使用 or 进行条件关联,而是使用 in;
9. 如果查询条件中是一个变量可能符合多个数据列,可以将 in 反过来写,如,订单表中,想要查找 **未交费****未开票****未发货** 的数据,则条件可以写为 ***where 'N' in (pay_status, invoice_status, send_status)***
9. 如果查询条件中是一个变量可能符合多个数据列,可以将 in 反过来写,如,订单表中,想要查找 **未交费****未开票****未发货** 的数据,则条件可以写为 ```where 'N' in (pay_status, invoice_status, send_status)```
10. 如果查询条件包含较多的 or,可以使用 union all 来分解为多个 and 条件的 sql,避免因为使用 or 关系造成索引失效;
11. 避免不必要的分组和排序;
12. 如非比较,减少 like 的使用,like 比较在大部分情况下无法利用到索引;
13. 从我了解到的资料看,MySQL 在解析 from 中的表和 Where 中的条件时基本都是采用自做向右的方式进行处理,因此我们再写跨表查询和多条件查询时,要尽量将可以大量过滤数据的条件写在前面,将可以利用索引的条件写在前面;
12. 如非必要,减少 like 的使用,like 比较在大部分情况下无法利用到索引;
13. 从我了解到的资料看,MySQL 在解析 from 中的表和 Where 中的条件时基本都是采用自左向右的方式进行处理,因此我们在写跨表查询和多条件查询时,要尽量将可以大量过滤数据的条件写在前面,将可以利用索引的条件写在前面;
14. 在使用条件时,尽量减少 not、<> 这样的比较方式;
15. 能使用 between 时,尽量别拆成多个 or 条件;
15. 能使用 between 时,尽量别拆成 in 条件;
16. 能使用去重方式获取数据,就不要使用 group by 进行分组;
17. 合理利用 limit,减少一次性传输大量数据的可能;
18. 合理利用游标技术,减少反复查询;
#### 五、举个栗子 ☺
&emsp;&emsp;下面,我们拉出在 **2019年1月** 造成大规模线上问题的 SQL 来具体说明优化步骤。
&emsp;&emsp;下面,我们揪出在 **2019年1月** 造成 **大规模线上问题** 的 SQL 来具体说明优化步骤。
```sql
-- 这个就是造成数据库 CPU 超负荷以至于停止对外服务的罪魁祸首
SELECT
select
ss.djxh djxh,
ci.ID cid,
ci.E_ID eid,
ss.ID sbid,
ci.SHXYDM shxydm,
ci.CUSTOMER_NAME customer_name,
db.BBLX_MC zsxm_mc,
CASE ss.TB_BZ WHEN 'Y' THEN '已同步' ELSE ( CASE ss.SBZT WHEN 'Y' THEN '已申报' ELSE '未申报' END ) END AS sbzt,
ss.SBRQ sbrq,
ss.YBTSE ybtse,
CASE ss.JKZT WHEN 'Y' THEN '已缴款' ELSE '未缴款' END AS jkzt,
ss.JKRQ jkrq,
ss.SSSQ_Q sssq_q,
ss.SSSQ_Z sssq_z,
ss.ZSXM_DM zsxm_dm,
ss.BBLX_DM bblx_dm,
ei.REAL_NAME NAME,
ss.SWJGMC swjgmc,
ss.SWJGJC swjgjc,
ss.NSRSBQ nsrsbq
FROM agency_tax.sb_sbxx ss
LEFT JOIN `customer_info` ci ON ci.ID = ss.C_ID
LEFT JOIN agency_tax.dm_bblx db ON db.BBLX_DM = ss.BBLX_DM
LEFT JOIN employee_info ei ON ei.id = ci.SERVICE_TAX_ID
LEFT JOIN (
SELECT b.num, sbxx.SB_ID, sbxx.BB_DM
FROM agency_tax.sb_sbxx_bbmx sbxx
LEFT JOIN (
SELECT COUNT(*) num, bbmx.SB_ID sbId
FROM agency_tax.sb_sbxx_bbmx bbmx
WHERE bbmx.STATE = 'Y'
GROUP BY bbmx.SB_ID
) b ON b.sbId = sbxx.SB_ID
WHERE sbxx.STATE = 'Y'
GROUP BY sbxx.ID
) a ON ss.ID = a.SB_ID
WHERE ss.state = 'Y'
AND a.num > 0
AND ci.CUSTOMER_NAME LIKE '%柏%'
AND ( ss.SBZT = 'Y' OR ss.BBLX_DM = '10411' OR ss.TB_BZ = 'Y' )
AND ci.SERVICE_TAX_ID = 696
GROUP BY ss.ID
ORDER BY ss.SSSQ_Z DESC, ss.C_ID, ss.ID
LIMIT 15
ci.id cid,
ci.e_id eid,
ss.id sbid,
ci.shxydm shxydm,
ci.customer_name customer_name,
db.bblx_mc zsxm_mc,
case ss.tb_bz when 'Y' then '已同步' else ( case ss.sbzt when 'Y' then '已申报' else '未申报' end ) end as sbzt,
ss.sbrq sbrq,
ss.ybtse ybtse,
case ss.jkzt when 'Y' then '已缴款' else '未缴款' end as jkzt,
ss.jkrq jkrq,
ss.sssq_q sssq_q,
ss.sssq_z sssq_z,
ss.zsxm_dm zsxm_dm,
ss.bblx_dm bblx_dm,
ei.real_name name,
ss.swjgmc swjgmc,
ss.swjgjc swjgjc,
ss.nsrsbq nsrsbq
from agency_tax.sb_sbxx ss
left join `customer_info` ci on ci.id = ss.c_id
left join agency_tax.dm_bblx db on db.bblx_dm = ss.bblx_dm
left join employee_info ei on ei.id = ci.service_tax_id
left join (
select b.num, sbxx.sb_id, sbxx.bb_dm
from agency_tax.sb_sbxx_bbmx sbxx
left join (
select count(*) num, bbmx.sb_id sbid
from agency_tax.sb_sbxx_bbmx bbmx
where bbmx.state = 'Y'
group by bbmx.sb_id
) b on b.sbid = sbxx.sb_id
where sbxx.state = 'Y'
group by sbxx.id
) a on ss.id = a.sb_id
where ss.state = 'Y'
and a.num > 0
and ci.customer_name like '%柏%'
and ( ss.sbzt = 'Y' or ss.bblx_dm = '10411' or ss.tb_bz = 'Y' )
and ci.service_tax_id = 696
group by ss.id
order by ss.sssq_z desc, ss.c_id, ss.id
limit 15
```
&emsp;&emsp;这个 sql 的业务是查询符合条件税种申报、缴款情况,并返给用户查看,在发生问题的时刻,这个 sql 涉及的数据表数据长度如下表:
&emsp;&emsp;这个 sql 的业务是查询符合条件税种申报、缴款情况,并返给用户查看,在发生问题的时刻,这个 sql 涉及的数据表行数如下表:
| 序号 | 数据表名称 | 数据行数 |
|---|---|---|
......@@ -180,7 +180,7 @@ LIMIT 15
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | extra |
|---|---|---|---|---|---|---|---|---|---|
| 1 | PRIMARY | <derived2> | ALL | | | | | 53801 | Using where; Using temporary; Using filesort |
| 1 | PRIMARY | <derived2> | ALL | | | | | 1053801 | Using where; Using temporary; Using filesort |
| 1 | PRIMARY | ss | eq_ref | PRIMARY,IDX_SB_SBXX_CID,IDX_SB_SBXX_CID_ZSXM,IDX_SB_SBXX_CID_ZSXM_SQ,IDX_SB_SBXX_CID_ZSXM_BBLX_SQ,IDX_SB_SBXX_SQ_STATE | PRIMARY | 4 | a.SB_ID | 1 | Using where |
| 1 | PRIMARY | db | eq_ref | PRIMARY | PRIMARY | 30 | agency_tax.ss.BBLX_DM | 1 | |
| 1 | PRIMARY | ci | eq_ref | PRIMARY | PRIMARY | 4 | agency_tax.ss.C_ID | 1 | Using where |
......@@ -256,11 +256,11 @@ limit 15
```
&emsp;&emsp;对优化前和优化后的 SQL 分别执行,时间分别为 2s 和 300ms(多次平均),基本可以认为 SQL 完成了优化;
&emsp;&emsp;我们再次执行查询计划,发现仍然存在一个一个全表扫描,customer_info 表;
&emsp;&emsp;我们再次执行查询计划,发现仍然存在一个全表扫描,customer_info 表;
&emsp;&emsp;纵观 SQL,我们可以看到 customer_info 除了提供结果数据外,还提供了一个筛选条件 service_tax_id,而通过分析表结构发现,service_tax_id 列缺少索引,并且该列可以过滤大量数据,因此可以考虑增加索引;
&emsp;&emsp;增加索引后,再次执行优化后的 SQL,可以看到执行时间已经降低到 15ms(平均多次),至此,我们可以任务,本次 SQL 优化完成。
&emsp;&emsp;增加索引后,再次执行优化后的 SQL,可以看到执行时间已经降低到 15ms(平均多次),至此,我们可以认为,本次 SQL 优化完成。
#### 五、总结
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment