索引及sql语句的优化

后台项目常常会和数据库打交道,必要的时候可能还需要自己手写一些sql语句,因此我们必须了解一下索引以及如何写出使用索引的高性能的sql语句。毕竟使用索引和不使用索引在数据量很大时的查询时间可能相差很多倍。

索引为什么能提高查询效率

数据库中的数据是存储在计算机的磁盘上的,每次去读数据时都会发生磁盘IO。而磁盘IO的时间和在数据内存里运行的时间相差很大,因此磁盘IO的时间便可相近于查询数据的时间。而索引便能有效的减少磁盘IO的次数。

索引的分类有很多,比如B-Tree,hash,R-Tree,Full-Text等,但最常用的是B-Tree索引。B-Tree索引的本质是一棵B+树,B+只在叶子结点存储有效数据,因此对于同样数量的数据,相比于B-树它的深度更低(因为数据页的大小是固定的,因此如果树的内层节点越小,则数量越多)。而对于B-Tree索引而言,树的深度即是它的磁盘IO次数。相比于全表查询每一条数据即发生一次磁盘IO,B-Tree索引有显著的效率提升。(对于B树的更详细的介绍请看我另一片博客)

建了索引为什么还是很慢

现在主流的数据库都会对我们书写的sql语句进行优化,生成一个执行计划。因此数据库中真正跑的语句和我们书写的并不一样,可以通过相应的命令如explain来查询数据库的执行计划,找到是哪条语句造成了慢查询,这一般也是sql语句优化的第一步。这些一般都是DBA的活,但我们也应当了解一些编写sql的原则。我们要明白并不是在一个数据列上建了索引,在执行该列上的sql查询时就一会使用索引,一些不当的sql语句会破坏索引造成全表扫描。这就是为什么你可能明明建了索引但这列或者这些列上的查询还是很慢。

常见的sql语句优化

所谓的sql优化通常都是where子句的优化,主要是去除不必要的操作(如一些不必要的的排序,去重),及避免破坏索引。

  • 尽量使用>=,<=来代替>,<。因为这样能有效减少查询数据的数量,如一个表有100万记录,一个数值型字段A,30万记录的A=0,30万记录的A=1,39万记录的A=2,1万记录的A=3。那么执行A>2与A>=3的效果就有很大的区别了,因为A>2时ORACLE会先找出为2的记录索引再进行比较,而A>=3时ORACLE则直接找到=3的记录索引。

  • 在使用LIKE操作符时避免在开头使用”%”通配符,如”%5400%”,这种语句不会使用索引。(”5400%”这样的可以使用索引)

  • 尽量使用union all代替union。因为union操作符会多一个排序去重的操作。

  • SQL的书写尽量统一,因为如果两条SQL的书写不同,比如大小写不一致,甚至是多个空格,SQL优化器也会将它认为为不同的SQL语句,从而进行两次分析。

  • 数据库一般采用自下而上的顺序解析WHERE子句,因此表连接的on语句最好写在where之前,并且范围查询最好写在where子句的末尾。

  • SELECT子句中避免使用” “,数据库一般会将” “转换成所有的列名,这个是通过查询数据字典完成的。

  • 不要再索引列上使用函数,计算,变量。这回破坏索引导致全表扫描。

  • 用Where子句替换HAVING子句。HAVING 只会在检索出所有记录之后才对结果集进行过滤. 这个处理需要排序,总计等操作。

  • 避免在索引列中使用in,not in,or,!=,<>等操作符,这会破坏索引导致全表扫描。可以使用 exist 和not exist代替 in和not in。

  • 不要再索引列上进行NULL值判断,这会破坏索引。

  • .尽量使用数字型字段。若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。

  • 在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。

  • 尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count( * ),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0。

  • 索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。