SQL性能优化

索引

每当后端技术人员讲起SQL的调优时,第一个想到的方案每每是索引。先举个最简单的例子,这里在tb表中给字段tb_name加上普通的索引,由此根据该字段进行SELECT查询时就无需进行全表遍历,以加快查询速度数据库

CREATE INDEX tb_tb_a ON tb (tb_a);
SELECT tb_id FROM tb WHERE tb_a='Sherry';

如下几种状况将没法使用索引:后端

  1. 使用OR
    带有OR操做的语句即便其中部分带了索引字段的也不会使用,如下SELECT操做是使用不了索引的,因此SQL要尽可能少用OR操做服务器

    CREATE INDEX tb_tb_a ON tb (tb_a);
    SELECT tb_id FROM tb WHERE tb_a='Sherry' OR tb_b='Billy';(索引失效)

    若是要想使用OR而且让索引生效,只能将OR条件里每一个相关列都加上索引!!!函数

  2. 使用多列索引时测试

    CREATE INDEX tb_tb_a_tb_b ON tb (tb_a,tb_b);

    这个多列索引本质上是建立了两个索引,分别是tb_atb_a_tb_b(可理解为字段最左部的某连续部分),则对如下SELECT语句产生不一样的结果优化

    SELECT tb_id FROM tb WHERE tb_a='Sherry';(索引生效)
    SELECT tb_id FROM tb WHERE tb_b='Sherry';(索引失效)
    SELECT tb_id FROM tb WHERE tb_a='Sherry' AND tb_b='Billy';(索引生效)

    多列索引tb_tb_a_tb_b和分开对tb_atb_b字段建立两个独立的索引的区别是多列索引能顺序地利用所包含的字段索引,而分开建立的索引则会选择最严格(能够理解为所选出结果集最小的索引)的索引来进行检索,其余相关的索引也不会被使用,故效果不如多列索引。另外创建多列索引时,须要注意索引所用字段的顺序,应将最严格的索引放在最前面使索引产生更好的效果设计

  3. 使用LIKE接以%开头的字符串时code

    CREATE INDEX tb_tb_a ON tb (tb_a);
    SELECT tb_id FROM tb WHERE tb_a LIKE 'She%';(索引生效)
    SELECT tb_id FROM tb WHERE tb_a LIKE '%rry';(索引失效)

    上面这种失效的状况下,可使用另外一种方式索引

    SELECT tb_id FROM tb WHERE REVERSE(tb_a) LIKE 'yrr%';(索引生效)

    即对所需查询的字段作一次翻转而后再进行LIKE操做,就能够达到利用索引的目的,不过这里又涉及到在SQL中使用函数的问题可能影响效率,所以最好对实际状况进行测试而决定使用方式,但这种方式不适用于LIKE '%xxx%'之类的SQL调优字符串

  4. 列是字符串类型时
    假设字段tb_astring类型

    CREATE INDEX tb_tb_a ON tb (tb_a);
    SELECT tb_id FROM tb WHERE tb_a='123456';(索引生效)
    SELECT tb_id FROM tb WHERE tb_a=123456;(索引失效)

    字符串字段的查询参数不加引号时虽然在某些状况下能查询成功,但并不能利用到已建立的索引

字段

  1. SELECT语句中所提取的字段尽可能少,通常只取出须要的字段,千万不要为了方便编写SQL语句而使用如下相似作法

    SELECT * FROM tb WHERE tb_gender=0;

    当你读取出来的记录量很大时更要禁止这种作法,这就是为何我在本篇文章中写的SQL都是SELECT tb_id之类来做为例子,而不是SELECT *,这个读者能够亲测,即便你数据库的数据量不是不少,你也能发现当你SELECT *SELECT tb_id时的耗时差异有多大(PS:某次项目经历中我就由于这个问题致使两个SQL的耗时分别是 1100ms 和 200ms)

  2. VARCHAR类型的字段长度在尽可能合理范围内分配,无需分配过多

  3. 尽可能使用TINYINTSMALLINTMEDIUM_INT做为整数字段类型而不是INT

  4. 设计容许的状况下,尽可能将字段可否为NULL属性设置为NOT NULL,不然将可能致使引擎放弃使用索引而进行全表扫描

链接表

有时候为了取到多个表的字段,编写SQL时会使用一次甚至屡次JOIN操做,在进行多表链接时应使各个表的数据集尽可能少,举个例子,好比如今tb1表数据量很大

SELECT tb1.tb1_name FROM tb1 LEFT JOIN tb2 ON tb2.tb2_otherid=tb1.tb1_id WHERE tb1.tb1_gender=0;

上面语句JOIN操做时会进行tb1tb2两个表全部数据集链接操做,为了减少链接操做的数据集,可将其改成

SELECT tb1.tb1_name FROM (SELECT tb1.tb1_id, tb1.tb1_name FROM tb1 WHERE tb1.tb1_gender=0) AS tb1 LEFT JOIN tb2 ON tb2.tb2_otherid=tb1.tb1_id;

这样一来,JOIN左边的数据集就仅仅是tb1_gender=0筛选出来的数据集而不是tb1全部数据集,从而提升了JOIN操做的执行速度。要注意一点是,JOIN操做的查询效率要比子查询高得多,因此可使用JOIN操做的状况下尽可能减小或杜绝子查询操做

计算操做

尽可能避免在SQLJOINWHERE部分使用计算操做,由于大多数涉及到在SQL中计算操做的状况每每会使索引失效而进行了全表遍历操做或者加大了数据库的负担,而这些原本是能够放到业务服务器上进行处理的,如

CREATE INDEX tb_tb_time ON tb (tb_time);
SELECT tb_id FROM tb WHERE YEAR(tb_time)='2012';(调用YEAR函数本质上也是计算操做)

这种状况不只不能利用索引,还会给数据库带来更大的计算负担,而这种状况几乎不须要给业务服务器带来更大负担就能够进行优化,只须要将SQL修改成

SELECT tb_id FROM tb WHERE tb_time BETWEEN '2012-01-01 00:00:00' AND '2012-12-31 23:59:59'

便可

相关文章
相关标签/搜索