为什么sql server存储过程在ssms中执行很快,但是在jdbc调用时却又很慢?
近几年老是不定期地遇到同一个问题,sql server的存储过程在ssms里执行很快,但是用jdbc调用就很慢。
今天仔细地研究了一下,发现问题的关键在于存储过程的执行计划。 存储过程在创建完之后,通常会被程序员用一组参数调用这个存储过程,查看能否执行。然而恰恰就是在这第一次在ssms里的调用,数据库会为这个存储过程创建一个执行计划。然后之后每次在ssms里的调用都会复用这个执行计划。所以,假如第一次调用使用的参数是一个异常值,那很有可能会让执行计划在遇到参数是正常值时的效率不是最高。以上只是在ssms中调用存储过程时观察到的现象。
假如是使用jdbc调用存储过程,那么jdbc使用的执行计划不是在ssms里第一次调用存储过程创建的那一个,而是这个存储过程在jdbc中第一次执行时创建的一个新的执行计划。所以假如使用jdbc调用存储过程,那么第一次用jdbc执行存储过程使用的参数必须是正常范围内的常见值,这样创建出来的执行计划才可以尽可能满足大多数正常范围内的参数值。
假如不希望存储过程复用执行计划,可以在create或者alter存储过程时加上with recompile,这样该存储过程在每次调用时都会重新根据实际的参数创建新的执行计划。这样做的好处是每次调用都是用的合适的执行计划,不会因为执行计划不匹配的问题导致执行很慢。缺点是每次都要生成一次执行计划,可能根据代码复杂度的不同产生长短不一的时间消耗。
可能有人会问,为什么jdbc调用存储过程没有复用ssms里调用存储过程时创建的执行计划。可能有人还会问,jdbc调用存储过程的执行计划会不会覆盖ssms调用存储过程的执行计划?这两个问题其实是同一个问题,它们的答案是:对于同一个存储过程,数据库会为ssms和jdbc创建两套执行计划。为了证明这一点,本文为ssms和jdbc的两套执行计划做了四组实验,分别是ssms低效-jdbc高效,ssms高效-jdbc低效,ssms低效-jdbc低效,ssms高效-jdbc高效。低效的执行计划可以用异常值参数生成。高效的执行计划可以用正常值参数生成。实验证明:每次实验中,同一个存储过程的两个执行计划可以同时存在。这就是为什么有时在ssms里执行很快,但是用jdbc调用就很慢的原因,是因为他们用的执行计划是不同的。