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

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

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