挽救数据库性能的30条黄金法则

  1. 优化查询,应尽可能避免全表扫描,应该在用于检索数据和排序数据的字段上创建索引,如where子句用于搜索,order by子句用于排序,因此在这两个子句涉及到的字段上须要创建索引。数据库

  2. 应该尽可能避免在where子句中使用否认的操做符,如不等于(!=或<>)、不然数据库引擎将放弃使用索引而进行全表扫描。并发

  3. 在尽可能避免在where子句中使用或(or)做为链接条件,不然数据库引擎将放弃使用索引而进行全表扫描。
    以下面的SQL语句可能会带来性能问题ide

    select id,name,age from persons 
                where name = 'Bill' or age > 30

    因为这条SQL语句使用了or,因此数据库引擎会进行全表扫描,为了不全表扫描,能够将这条SQL语句改为下面的形式。函数

    select id,name,age from persons  where name = 'Bill'
    union all
    select id,name,age from persons where num = 20
  4. 应该尽可能避免在where子句中使用null进行判断,不然数据库引擎将放弃使用索引而进行全表扫描。
    先看下面的SQL语句:

select id,name,age from persons where age is null性能

为了不使用null,能够设置age字段的默认值为0,这样就能够经过下面的SQL语句达到一样的结果。
select id,name,age from persons where age = 0优化

  1. 尽可能不用使用like检索数据,由于也会致使数据库引擎将放弃使用索引而进行全表扫描。

例如,下面的SQL语句执行的效率会很是低:
select id,name,age from persons where name like '%John%'code

若是真想进行模糊查询,可使用全文检索。orm

  1. 在where子句中应尽可能避免在字段中使用表达式(包括函数运算、算数运算等),不然据库引擎将放弃使用索引而进行全表扫描。
    例如,下面的SQL语句执行的性能比较差
    select id,name,age from persons age / 2 &gt; 12

应该利用表达式变换,改为下面的形式:
select id,name,age from persons age &gt; 2 * 12排序

或者干脆改为下面的形式:
select id,name,age from persons age &gt; 24索引

  1. 应尽可能避免使用in和not in,不然也会致使全表扫描。

如并不推荐下面的写法:
select id, name,age from persons where age in (22,23,24)

若是数值是连续的,应该使用between,而不要用in,若是数值是不连续的,能够分红多个SQL,用union all链接查询结果。

select id,name,age from persons where age between 22 and 24

select id,name,age from persons where age = 22
union all
select id,name,age from persons where age = 26
union all
select id,name,age from persons where age = 30
  1. 应该尽可能避免在where子句中使用参数,不然也将致使全表扫描。这是由于参数须要在SQL运行时才进行替换,而SQL优化(使用索引属于优化的一部分)是在编译时进行的。因此数据库引擎在检索到参数时,因为参数的具体指是未知的,因此也就没法优化了,固然也就没法使用索引了。

不使用索引的SQL语句:
select id,name,age from persons where name = @name

为了使用索引,能够改为下面强制使用索引的方式:
select id,name,age from persons with(index(name_index)) where name = @name

其中name_index是创建在name字段上的索引名。

  1. 尽可能不要执行一些没意义的查询,如条件彻底为false的查询:
    select id,name,age into persons1 from persons where age &lt; 0

这样的代码会返回一个空结果集,并且会大量消耗系统资源,若是真的想建一个空表,应该直接用create table语句。

  1. 若是使用的索引是符合索引,只有使用该符合索引的第1个字段做为条件时才能保证数据库引擎使用该符合索引,不然该符合索引不会被使用。而且应该尽量让字段顺序与索引顺序一致。例如,name_index是first_name和last_name字段的符合索引,使用下面的SQL语句会使用该索引。
select id,first_name,last_name
            from persons
                   where first_name = 'Bill'
  1. 若是非要在SQL语句中使用in,那么使用exists代替in是一个好主意:
select id,num from t 
       where num in (select num from h)

应该用下面的SQL语句代替:

select id,num form t
       where exists(select 0 from h where num = t.num)
  1. 索引并非在任什么时候候都有效,若是索引列有大量重复的数据,那么数据库引擎可能不会去利用索引。例如,sex字段的值只有两种可能:male和female,可能这两个值各占一半,这样在sex字段上创建索引就没有任何意义。

  2. 能使用数值型字段就使用数值型字段。由于比较数值型字段的效率要远比字符型字段的效率高,这是由于比较字符型的值,要一个字母一个字母地比较,而数值型的值,只是比较一个数。因此若是只包含数值信息的值,应该尽可能使用数值类型的字段。例如,age、salary等。

  3. 应尽可能避免使用固定长度的字段,如char、nchar。使用可变长度的字段是一个很是好的选择。由于可变长度字段占用的空间是按需分配的,因此占用空间比较少。对于查询来讲,毫无疑问,固然是占用空间小的字段的查询效率更高了。

  4. 尽可能按需返回字段和记录,例如:
    select id,name,age from persons where age &gt; 20

尽可能如要使用“”返回全部不须要的字段,也不须要一下就查询出全部的记录,以下面的SQL语句在数据量很大时查询效率是很是低的。
`select
from persons`

  1. 索引有利有弊,增长索引,能够提升select的执行效率,但付出的代价是在进行insert和update操做时,可能会下降效率。由于进行insert和update操做时一般须要重建索引。因此在一个表中并非索引越多越好。个人建议以下:
    (1)若是一个表大多数时进行的是select操做,那么索引多一些大多数时候确实能够提高性能,但这有一个前提,就是不能频繁进行insert和update操做。
    (2)一个表中的索引数不能太多,最好不要超过6个,不然就好考虑优化一下数据库了。

  2. 应尽量的避免更新 clustered 索引数据列,由于 clustered 索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将致使整个表记录的顺序的调整,会耗费至关大的资源。若应用系统须要频繁更新 clustered 索引数据列,那么须要考虑是否应将该索引建为 clustered 索引。

  3. 应尽可能避免向客户端返回大理数据,若是数据量过大,应该改变一下需求,或采用分页返回的方式,如使用MySQL中的limit子句如今返回的数据。

  4. 尽可能避免使用游标,由于游标的效率较差,若是游标操做的数据超过1万行,那么就应该采用其余方案。

  5. 使用基于游标的方法或临时表方法以前,应先寻找基于数据集的解决方案来解决问题,基于数据集的方法一般更有效。

  6. 若是使用到了临时表,在存储过程的最后务必将全部的临时表显式删除,先用 truncate table清除表中的数据 ,而后 用drop table完全删除物理表 ,这样能够避免系统表的较长时间锁定。

  7. 避免频繁建立和删除临时表,以减小系统表资源的消耗。

  8. 在新建临时表时,若是一次性插入的数据量很大,那么可使用 select into 代替 create table,避免形成大量 log ,以提升执行效率;若是数据量不大,为了缓和系统表的资源,应先create table,而后使用insert插入数据。

  9. 在全部的存储过程和触发器的开始处设置 SET NOCOUNT ON ,在结束时设置 SET NOCOUNT OFF 。无需在执行存储过程和触发器的每一个语句后向客户端发送 DONE_IN_PROC 消息。

  10. 尽可能避免大事务操做,提升系统并发能力。

  11. 应尽可能一次性插入多条数据,例如,使用下面的SQL语句性能会很低:
insert into persons(id,name,age) values('Bill',24)
insert into persons(id,name,age) values('Mike',26)
insert into persons(id,name,age) values('John',20)

为了提高性能,能够一次性插入这3条记录。
insert into persons(id,name,age) values('Bill',24),('Mike',26),('John',20)

  1. 若是不得不使用like进行模糊查询时,不要在关键字前面加%。

反例:

select id,name,age from persons where name like '%abc%'

若是在关键字前面加%,那么查询是确定要走全表查询的。

正例:
select id,name,age from persons where name like 'abc%'

  1. 尽可能用union all代替union

union和union all的差别主要是前者须要将两个(或者多个)结果集合并后再进行惟一性过滤操做,这就会涉及到排序,增长大量的cpu运算,加大资源消耗及延迟。因此当咱们能够确认不可能出现重复结果集或者不在意重复结果集的时候,尽可能使用union all而不是union。

29.尽可能使用等值链接

等值链接就是inner join,也称为内联进,而left join和right join是外链接。

先看下面的SQL语句

select  a.id,a.name,b.id,b.name from a left join b on a.id = b.id
select  a.id,a.name,b.id,b.name from a right join b on a.id = b.id
select  a.id,a.name,b.id,b.name from a inner join b on a.id = b.id

上面的3条SQL语句,前两条分别使用了左链接和右链接,而最后一条使用了内链接,通过实际运行,使用内链接的SQL语句的执行效率明显优于左链接和右链接。因此在能知足需求的前提下,应该尽量使用内链接(等值链接)。

  1. 尽可能用外链接来替换子查询
    反例
    select id,name from a where exists (select id from b where id&gt;=10 and a.product_id=b.product_id)

在上面的SQL语句中,数据库引擎会先对外表a执行全表查询,而后根据product_id逐个执行子查询,若是外层表(a表)中的数据很是多,查询性能会很是糟糕。因此应该将SQL语句改为下面的形式:

select id,name from a inner join b on A.product_id=b.product_id where b.id&gt;=10

相关文章
相关标签/搜索