SQL索引优化

sql中索引不会被用到的几种情况

mysql 存在索引但不能使用索引的典型场景

单键值的b树索引列上存在null值,导致COUNT(*)不能走索引
索引列上有函数运算或者是表达式的一部分,导致不走索引
数据类型隐式转换导致不走索引
表的数据量小或者需要选择大部分数据,不走索引 (Mysql估计使用索引比全表扫描慢)
负向查询(not , not in , not like , <>, != , !>, !< ) 不会使用索引
组合索引最左前缀
like '%liu' 百分号在前
查询条件中使用or的每个列都必须有索引才能走索引,否则考虑使用union替代or

索引的设计可以遵循一些已有的原则,创建索引的时候请尽量考虑符合这些原则,便于提升索引的使用效率,更高效地使用索引。

1、 搜索的索引列,不一定是所要选择的列。换句话说,最适合索引的列是出现在 WHERE子句中的列,或连接子句中指定的列,而不是出现在 SELECT 关键字后的选择列表中的列。

2、 使用唯一索引。考虑某列中值的分布。索引的列的基数越大,索引的效果越好。例如,存放出生日期的列具有不同值,很容易区分各行。而用来记录性别的列,只含有“ M”和“F”,则对此列进行索引没有多大用处,因为不管搜索哪个值,都会得出大约一半的行。

3、 使用短索引。如果对字符串列进行索引,应该指定一个前缀长度,只要有可能就应该这样做。例如,如果有一个 CHAR(200)列,如果在前 10 个或 20 个字符内,多数值是惟一的,那么就不要对整个列进行索引。对前10个或20个字符进行索引能够节省大量索引空间,也可能会使查询更快。较小的索引涉及的磁盘 IO 较少,较短的值比较起来更快。更为重要的是,对于较短的键值,索引高速缓存中的块能容纳更多的键值,因此,MySQL 也可以在内存中容纳更多的值。这样就增加了找到行而不用读取索引中较多块的可能性。

4、 利用最左前缀。在创建一个 n 列的索引时,实际是创建了 MySQL 可利用的 n 个索引。多列索引可起几个索引的作用,因为可利用索引中最左边的列集来匹配行。这样的列集称为最左前缀。

5、 不要过度索引。不要以为索引“越多越好”,什么东西都用索引是错误的。每个额外的索引都要占用额外的磁盘空间,并降低写操作的性能。在修改表的内容时,索引必须进行更新,有时可能需要重构,因此,索引越多,所花的时间越长。如果有一个索引很少利用或从不使用,那么会不必要地减缓表的修改速度。此外,MySQL 在生成一个执行计划时,要考虑各个索引,这也要花费时间。创建多余的索引给查询优化带来了更多的工作。索引太多,也可能会使 MySQL 选择不到所要使用的最好索引。只保持所需的索引有利于查询优化。

6、 对于 InnoDB 存储引擎的表,记录默认会按照一定的顺序保存,如果有明确定义的主键,则按照主键顺序保存。如果没有主键,但是有唯一索引,那么就是按照唯一索引的顺序保存。如果既没有主键又没有唯一索引,那么表中会自动生成一个内部列,按照这个列的顺序保存。按照主键或者内部列进行的访问是最快的,所以 InnoDB 表尽量自己指定主键,当表中同时有几个列都是唯一的,都可以作为主键的时候,要选择最常作为访问条件的列作为主键,提高查询的效率。另外,还需要注意,InnoDB 表的普通索引都会保存主键的键值,所以主键要尽可能选择较短的数据类型,可以有效地减少索引的磁盘占用,提高索引的缓存效果。

小结:

索引用于快速找出在某个列中有一特定值的行。如果不使用索引,MySQL 必须从第 1条记录开始然后读完整个表直到找出相关的行。表越大,花费的时间越多。如果表中查询的列有一个索引,MySQL 能快速到达一个位置去搜寻数据文件的中间,没有必要看所有数据。如果一个表有 1000 行,这比顺序读取至少快 100 倍。注意如果需要访问大部分行,顺序读取要快得多,因为此时应避免磁盘搜索。大多数 MySQL 索引(如 PRIMARY KEY、UNIQUE、INDEX 和 FULLTEXT 等)在 BTREE 中存储。只是空间列类型的索引使用 BTREE,并且 MEMORY 表还支持 HASH 索引。关于什么情况下数据库会使用索引,以及什么情况下数据库不会使用索引的详细介绍,可参见优化篇的相关章节,这里不再赘述。

索引优化

禁用select *
使用select count(*) 统计行数
尽量少运算
尽量避免全表扫描,如果可以,在过滤列建立索引
尽量避免在where子句对字段进行null判断
尽量避免在where子句使用!= 或者<>
尽量避免在where子句使用or连接
尽量避免对字段进行表达式计算
尽量避免对字段进行函数操作
尽量避免使用不是复合索引的前缀列进行过滤连接
尽量少排序,如果可以,建立索引
尽量少join
尽量用join代替子查询
尽量避免在where子句中使用in,not in或者having,使用exists,not exists代替
尽量避免两端模糊匹配 like %xx%
尽量用union all代替union
尽量早过滤
避免类型转换
尽量批量insert
优先优化高并发sql,而不是频率低的大sql
尽可能对每一条sql进行explain
尽可能从全局出发

Sql中EXISTS与IN的效率问题

in 和exists

in是把外表和内表作表连接,而exists 是对外表作loop 循环,每次loop 循环再对内表进行查询。
一直以来认为exists 比in 效率高的说法是不准确的。如果查询的两个表大小相当,那么用in 和exists 差别不大。

not in 和not exists

如果查询语句使用了not in 那么内外表都进行全表扫描,没有用到索引。
而not extsts 的子查询依然能用到表上的索引。所以无论那个表大,用not exists 都比not in 要快。

IN适合于外表大而内表小的情况;EXISTS适合于外表小而内表大的情况。

limit优化

limit常用于分页以及指定查询条数
limit n :n代表取出前n行。
limit m,n :m代表前m行,n代表取出n行。
limit工作原理是查询前m行,然后丢弃前m行,取出m后面的n行。
因此在m非常大的时候limit就会显得效率低下。
一种比较有效的优化方法是使用where先进行过滤,适用于自增长的列,比如ID主键。
select * from table limit m,n
-> select * from table where id > m limit n

你可能感兴趣的:(SQL索引优化)