SQL调优

背景

    每当交易高峰时期,可能会暴露一些平时无法发现的问题,机遇和挑战并存。下面聊聊最近解决的一个案例,因为执行计划走错导致慢查询,进而引发应用线程阻塞、线程池爆满,最后应用功能瘫痪。如何标本兼治的解决问题,需要很多思考。

问题分析

   step1 应用瘫痪

    用户反应某查询功能一直处于加载中,并出现错误提示。查看后台应用日志,调用远程查询服务出现大量超时。

   step2 线程池爆满

    通过jstack命令来分析查询服务jvm线程堆栈,发现设定的线程池已经满了,而且大部分线程阻塞在了数据库查询阶段(如下图),看来是有很多慢查询。

step3 慢查询的由来 - 错误执行计划

为何忽然出现这么多慢查询?我们来分析一下:

  1. SELECT no, time
  2. FROM tablename
  3. where no = :1
  4. and time >= to_date(:2, 'yyyy-mm-dd')
  5. and time < to_date(:3, 'yyyy-mm-dd')
  6. ORDER BY time DESC

注:条件字段(no、time)分别有索引(idx_no、idx_time)。

经过DBA帮助,分析上段sql的执行计划发现:

  • 有时数据库会走idx_no索引,查询很快

  • 有时会走idx_time索引,查询很慢

 

解决方案

    既然数据库自动生成的执行计划有时会出问题,那么我们可以用hints语句指定当前sql走哪条索引,以固定执行计划。

下面以Oracle为例,添加hints:

  1. SELECT /*+ index(tablename IDX_NO) */ no, time
  2. FROM tablename
  3. where no = :1
  4. and time >= to_date(:2, 'yyyy-mm-dd')
  5. and time < to_date(:3, 'yyyy-mm-dd')
  6. ORDER BY time DESC

如果感觉在sql中添加不方便的话,oracle 10g以上版本提供了SQL Profile方式。

总结思考

    这个问题应该一直有,为何在交易高峰时段才表现出来值得去研究。

    数据库:平时数据库压力较小,出现几个走错执行计划的慢查询,也只是让数据库压力比均值高出一小部分,性能未受影响。但是,高峰时段数据库本身就处于一个高负载状态,这时出现的慢查询无疑是雪上加霜,接近某个临界点时性能就明显下滑。

    应用:慢查询会导致线程阻塞等待,一直占用线程池资源。我们知道,用户在点击查询按钮后发现一直加载,会不知觉多点几遍。这样的话,线程数量暴涨且大部分处于阻塞状态,线程池满了之后应用就会瘫痪掉,使其他正常的接口也无法正常工作。

    综上所述,数据层的优化是治本,同时还要借助一些手段来保证不出现雪崩式性能下降。比如,在前台页面添加重复提交限制,尽量避免短时间内产生大量慢查询。

参考资料

Oracle Hints:https://docs.oracle.com/cd/B12037_01/server.101/b10752/hintsref.htm#5156

Oracle SQL Profile:https://docs.oracle.com/database/121/TGSQL/tgsql_profiles.htm#TGSQL596

原文地址:https://www.cnblogs.com/feiqihang/p/5310517.html