今天监控数据库时,一慢 SQL 引起了我的注意,这个 SQL 运行时间在 474 ms 左右,经分析,创建合适的索引后,最终将 SQL 优化到 4 ms 左右,以下是优化过程,记录下。
数据库日志
1 |
2013-03-12 14:14:15.266 CST,"francs","francs",10729,"192.168.100.114:32996",513e742a.29e9,759,"SELECT",2013-03-12 08:17:46 CST,1424/3734,0,LOG,00000,"duration: 474.781 ms execute <unnamed>: select this_.id as id0_0_, this_.create_time as create2_0_0_, this_.create_user as create3_0_0_, this_.deleted as deleted0_0_, this_.modify_time as modify5_0_0_, this_.modify_user as modify6_0_0_, this_.apk_id as apk7_0_0_, this_.apk_md5 as apk8_0_0_, this_.apk_size as apk9_0_0_, this_.app_class_id as app10_0_0_, this_.app_id as app11_0_0_, this_.package as package0_0_, this_.app_show_ver as app13_0_0_, this_.app_type as app14_0_0_, this_.app_ver as app15_0_0_, this_.authentic as authentic0_0_, this_.cn_name as cn17_0_0_, this_.content_provider as content18_0_0_, this_.eng_name as eng19_0_0_, this_.on_off as on20_0_0_, this_.promotion as promotion0_0_, this_.sdk_ver as sdk22_0_0_, this_.visible as visible0_0_ from tbl_test this_ where this_.app_class_id in ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) and this_.app_type in ($14, $15) and this_.on_off=$16 and this_.visible=$17 and this_.deleted=$18 order by this_.app_type desc, this_.create_time desc limit $19","parameters: $1 = '3', $2 = '4', $3 = '5', $4 = '6', $5 = '7', $6 = '8', $7 = '9', $8 = '10', $9 = '11', $10 = '12', $11 = '13', $12 = '14', $13 = '15', $14 = '0', $15 = '1', $16 = '1', $17 = '1', $18 = '0', $19 = '80'",,,,,,,"exec_execute_message, postgres.c:2025","" |
备注:SQL 的详细信息可以从数据库日志得到。
格式化 SQL
1 |
select this_.id as id0_0_, |
备注:这个 SQL 非常简单,没有关联查询,运行时间在 474 ms 确实比较糟糕。
优化前的执行计划
1 |
-------------------------------------------------------------------------------------------------------------------------------- |
备注:重新运行这个 SQL ,花了 500 ms 左右,时间主要花在通过索引 “ idx_tbl_test_muti ” 读取记录环节,并且 PLAN 中显示了排序步骤,接下来看下表结构。
表信息
表结构
1 |
Table "francs.tbl_test" |
备注:重点看下索引 idx_tbl_test_muti 的定义。
查询字段 distinct 值
1 |
francs=> select tablename,attname,n_distinct from pg_stats where tablename='tbl_test' |
备注:pg_stats 视图可以查看表字段的统计信息,这里查看字段的 distinct 值,从上面看出,where 条件的几个字段的选择性都非常不好,但 order by 字段的 create_time 的n_distinct 为 -1 ,表示字段 create_time 是唯一的,选择性好。
创建索引
1 |
set default_tablespace='tbs_francs_03'; |
备注:尝试仅在 order by 字段上创建索引。
优化后的执行计划
1 |
QUERY PLAN |
备注:优化后,执行时间下降到 4 ms 左右,仅原来的百分之一。
索引引使用情况
1 |
francs=> select relname,indexrelname ,idx_scan from pg_stat_user_indexes where relname='tbl_test' order by idx_scan; |
备注:上面显示索引 “idx_tbl_test_muti2” 已经被使用了,这时可以删除老索引 “idx_tbl_test_muti” 了。
参考
- http://www.postgresql.org/docs/9.1/static/indexes-ordering.html
- http://www.postgresql.org/docs/9.1/static/catalog-pg-statistic.html
- https://postgres.fun/20120621210555.html
原创文章,作者:3628473679,如若转载,请注明出处:https://blog.ytso.com/237945.html