性能优化之SQL优化经验总结2: 通过减少总的SQL查询数量

前言:

在这篇文章里, 笔者主要想总结一下在不影响功能的情况下减少SQL查询的一些经验, 主要想法还是一次性把数据库中需要的东西都搬到内存里面, 避免冗余的查询. 具体手段包括, 使用面向过程的方法, 使用临时表等. 下面逐一具体介绍.

减少SQL的数量的三条方法:

1. 尽量采用面向过程, 少用面向对象:

记得以前一开始学习C语言的时候, 所有要使用到的变量都要在开头都申明好, 下面才可以使用, 后来学习了C++, 知道可以declare variable as you go, 也就是你需要用到的时候你就再去申明使用就可以了, 这样就不需要把所有的申明都移到程序的开头, 因为方便也就养成了declare variable as you go的习惯, 换种说法, 面向对象的思路更像是等到你要用的时候你再去拿, 再去申明.

那么回到我们的数据库性能问题上来, 上面这一段可以作为指导思想, 具体的方法论是这样: 通过SQLServer 2008里面有个Profiler的Tool跟踪你的程序运行期间(或者你执行某些操作期间)抓取所有执行的SQL语句进行比较, 看有没有多余的没有用的SQL, 如果有就应该想办法去除这些冗余的SQL查询.  为什么会出现冗余? 我觉得很多时候就是我们太习惯面向对象的思想, 等到要用的时候每次都采取拿. 提高性能的做法是只拿一次, 然后放在内存中(等效于C里面把所有要用的变量都在程序的一开头就declare好), 等到要用的时候都去使用同一个内容中的对象或者变量或者数据吧, 而不是等到要用的时候都去数据库中现拿.

所以这一点是不是也在某种程度上解释了为什么C比C++快? 每次都declare as you go, 不断的局部变量内存创建和销毁, 所以比较慢, C之所以快, 就是因为需要使用的数据都做到最精简(一次性在开头). 但是我们也知道, 所谓像C语言一样把要用到的东西都放在一个地方(如程序开头), 也就是这些东西对于要使用到他们的程序逻辑来讲就是”全局的”, 所以也有坏处, 程序逻辑之间的耦合度过高, 有悖于低耦合, 高内聚(i.e., 尽量减少全局变量的使用)的指导思想. 所以万事皆在权衡之间吧.

2. 使用临时表并联把数据都一次性放到内存中:

这一条的意思是对于一组有规律的SQL语句集合, 比如Select * from Table Where ID = i, 其中 i 可以是一些不同的id值, 对于这一系列的SQL语句, 可以把这整一组语句使用临时表的方法合并成一条SQL语句进行查询.  对于这一点笔者首先说说为什么这么做可以调高性能或者效率, 然后讲讲如何合并把多条SQL查询放到一条SQL语句.

第一为什么: (1) 一组这样的SQL语句Select * from Table Where ID = i 一条一条发送每条都有IO, 如果合并成一条那么就只有一次发送SQL的IO; (2) 数据库比如SQL Server应该会对临时表有自身的优化, 与其我们花大力气想如何写出效率更高的语句不如会接扔给SQL Server自己去优化; (3) 这么一组数据得到的结果集可以一起放到一块连续内存中, 后面想要再使用就可以去这块内存里面查找无需再查询数据库(这一点也和上面第1条相符合)

第二, 一般使用临时表把这么一组语句合并成一条SQL语句的步骤如下: (1) 使用where条件中那个有规律的字段(比如我们例子中的ID)作为临时表的字段, 一般临时表就单独的一个字段. (2) 把这一组SQL语句用到的所有ID值都插入到这个临时表TmpTable (3) 两张表并联进行查询 Select * from Table, TmpTable where Table.ID = TmpTable.ID 或者 变成 Select * From Table where ID IN (Select * from TmpTable), 当然了前者效率会比较好, IN的话到了一定的数量级以后就会变的很慢这个下篇文章也会提到.

3. 找到最耗时的那张表进行优化:

这点很简单, 只是做个记录和参考, 查性能瓶颈的时候可以先关注一下那个表Table耗时最多(比如表A), 如果有这样耗时相对于其他Table来讲绝对多的, 就可以考虑为这张表(表A)制作一张临时表, 或者直接把整张表A读取到内存里面.

Written on July 12, 2014