mysql 索引- 笔记
索引
mysql最常用的索引结构是btree(O(log(n))),但是总有一些情况下我们为了更好的性能希望能使用别的类型的索引。hash就是其中一种选择,例如我们在通过用户名检索用户id的时候,他们总是一对一的关系,用到的操作符只是=而已,假如使用hash作为索引数据结构的话,时间复杂度可以降到O(1)。不幸的是,目前的mysql版本(5.6)中,hash只支持MEMORY和NDB两种引擎,而我们最常用的INNODB和MYISAM都不支持hash类型的索引。
1、BTree索引:
BTree(多路搜索树,并不是二叉的)是一种常见的数据结构。使用BTree结构可以显著减少定位记录时所经历的中间过程,从而加快存取速度。按照翻译,B 通常认为是Balance的简称。这个数据结构一般用于数据库的索引,综合效率较高。
B-Tree索引可以被用在像=,>,>=,<,<=和BETWEEN这些比较操作符上。而且还可以用于LIKE操作符,只要它的查询条件是一个不以通配符开头的常量。
EXPLAIN select * from blog.my_user where `name` in ('rhythmk251','rhythmk2151','rhythmk7251','rhythmk685'); +----+-------------+---------+------+---------------+------+---------+------+--------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+------+---------------+------+---------+------+--------+-------------+ | 1 | SIMPLE | my_user | ALL | NULL | NULL | NULL | NULL | 778424 | Using where | +----+-------------+---------+------+---------------+------+---------+------+--------+-------------+ 1 row in set
添加 idx_name 索引:
ALTER TABLE blog.`my_user` ADD INDEX `idx_name` (`name`) USING BTREE ; mysql> EXPLAIN select * from blog.my_user where `name` in ('rhythmk251','rhythmk2151','rhythmk7251','rhythmk685'); +----+-------------+---------+-------+---------------+----------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+-------+---------------+----------+---------+------+------+-------------+ | 1 | SIMPLE | my_user | range | idx_name | idx_name | 303 | NULL | 4 | Using where | +----+-------------+---------+-------+---------------+----------+---------+------+------+-------------+ mysql> EXPLAIN select * from blog.my_user where `name` LIKE 'rhythmk830%'; +----+-------------+---------+-------+---------------+----------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+-------+---------------+----------+---------+------+------+-------------+ | 1 | SIMPLE | my_user | range | idx_name | idx_name | 303 | NULL | 11 | Using where | +----+-------------+---------+-------+---------------+----------+---------+------+------+-------------+ 1 row in set
任何一个没有覆盖所有WHERE中AND级别条件的索引是不会被使用的。也就是说,要使用一个索引,这个索引中的第一列需要在每个AND组中出现。
mysql> EXPLAIN select * from blog.my_user where `name` LIKE 'rhythmk830%' or age>35; +----+-------------+---------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | my_user | ALL | idx_name | NULL | NULL | NULL | 7266 | Using where | +----+-------------+---------+------+---------------+------+---------+------+------+-------------+ 1 row in set mysql> EXPLAIN select * from blog.my_user where `name` LIKE 'rhythmk830%' and age>35; +----+-------------+---------+-------+---------------+----------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+-------+---------------+----------+---------+------+------+-------------+ | 1 | SIMPLE | my_user | range | idx_name | idx_name | 303 | NULL | 11 | Using where | +----+-------------+---------+-------+---------------+----------+---------+------+------+-------------+ 1 row in set
有时候mysql不会使用索引,即使这个在可用的情况下。例如当mysql预估使用索引会读取大部分的行数据时。(在这种情况下,一次全表扫描可能比使用索引更快,因为它需要更少的检索)。然而,假如语句中使用LIMIT来限定返回的行数,mysql则会使用索引。因为当结果行数较少的情况下使用索引的效率会更高。
mysql> EXPLAIN select * from blog.my_user where `name` <> 'rhythmk830' ; +----+-------------+---------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | my_user | ALL | idx_name | NULL | NULL | NULL | 7769 | Using where | +----+-------------+---------+------+---------------+------+---------+------+------+-------------+ 1 row in set mysql> EXPLAIN select * from blog.my_user where `name` <> 'rhythmk830' limit 5; +----+-------------+---------+-------+---------------+----------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+-------+---------------+----------+---------+------+------+-------------+ | 1 | SIMPLE | my_user | range | idx_name | idx_name | 303 | NULL | 5658 | Using where | +----+-------------+---------+-------+---------------+----------+---------+------+------+-------------+ 1 row in set
2、Hash 索引特征:
Hash类型的索引有一些区别于以上所述的特征:
精确查找非常快(包括= <> 和in),其检索效率非常高,索引的检索可以一次定位,不像BTree 索引需要从根节点到枝节点,所以 Hash 索引的查询效率要远高于 B-Tree 索引。
不适合:
1、不适合模糊查询和范围查询(包括like,>,<,between……and等)。由于 Hash 索引比较的是进行 Hash 运算之后的 Hash 值,所以它只能用于等值的过滤,不能用于基于范围的过滤,因为经过相应的 Hash 算法处理之后的 Hash 值的大小关系,并不能保证和Hash运算前完全一样;
2、不适合排序,数据库无法利用索引的数据来提升排序性能,同样是因为Hash值的大小不确定;
3、复合索引不能利用部分索引字段查询,Hash 索引在计算 Hash 值的时候是组合索引键合并后再一起计算 Hash 值,而不是单独计算 Hash 值,所以通过组合索引的前面一个或几个索引键进行查询的时候,Hash 索引也无法被利用。
3、备注:
1、索引列需要根据业务判断 ,在where条件中出现的列。
2、使用索引需要考虑某列中值的分布,比如性别,搜索 “男/女” 都会得出大约一半的行,所有索引没有多大的用处。
3、使用短索引好处多多。对字符串进行索引的时候,应该指定一个前缀长度。索引长度小,节约索引空间及磁盘IO同时让索引,短的值比较起来更快,同时Mysql 可以在内存中存储更多的值。
4、利用最左前缀。创建一个多列索引,可起几个索引的作用。因为可利用索引最左边的列来匹配行。这样的列集称为最左前缀。
5、不要过度索引。 额外的索引需要占用空间,并降低写操作的性能。修改表的时候索引必须进行更新,有时可能需要重构,索引越多时间越长。索引太多,也可能导致MYSQL选择不到最优索引。
6、INNODB 存储引擎,记录优先按主键保存,如果没有主键,但是有唯一索引那么就按唯一索引列保存。如果两者都没有,那么表中会生成一个内部列。按照这个列的顺序保存,InnoDB普通索引都会保存主键,所以主键要尽可能选择较短的数据类型。
7、尽量避免NULL:应该指定列为NOT NULL,除非你想存储NULL。在MySQL中,含有空值的列很难进行查询优化,因为它们使得索引、索引的统计信息以及比较运算更加复杂。你应该用0、一个特殊的值或者一个空串代替空值。
8、使用--查看当前session所有已产生的profile
mysql> show profiles;