用过 mysql 的童鞋都知道创建索引的必要性,但是很多人对创建索引的目的仅仅停留于创建索引可让查询变快 mysql
然而,为了达到这个目的,科学合理的创建索引也是很是有必要的 sql
索引可以轻易将查询性能提升几个数量级,而一个“最优”索引有时比一个“好的”索引性能要高两个数量级 数据库
在 MySQL 中,索引能够包含一个活多个列的值,由于 MySQL 只能高效地使用索引的最左前缀列,因此包含多个列的索引中列的顺序也十分重要 数据结构
而建立一个包含两个列的索引和建立两个分别包含一列的索引是大不相同的 函数
MySQL 中,索引的类型有不少类型,可以为不一样场景提供更好的性能 性能
索引是在存储引擎层实现的,不一样存储引擎的索引工做方式不一样,也并非全部引擎都支持所有的索引类型,而对于同一类索引,不一样引擎的底层实现也多是不一样的 优化
大多数 MySQL 存储引擎都支持 B-Tree 索引,也所以,B-Tree 索引是最经常使用的索引类型,若是未加说明,索引通常都指的是 B-Tree 索引 搜索引擎
然而,虽然在建立表时关键字都是 B-Tree,可是各个存储引擎的底层实现多是不一样的,如 NDB 集群存储引擎内部实际上使用了 T-Tree 结构,而 innoDB 使用的 B+ Tree url
因为磁盘读取靠的是机械运动,每次都要花费寻道时间、旋转延迟、传输时间三部分时间才能读取数据,总计时间是很是长的,若是针对数据库的动辄十万百万乃至千万级的数据查询,每次几毫秒的时间,结果将会是灾难性的 spa
所以操做系统对此进行了一些优化,每次读取时并不只仅读取须要的数据,而是把相邻数据所有读取到内存缓冲区中,这样,每次都读取一页数据(4KB 或 8KB),而针对一页上数据的读取,事实上仅进行了一次磁盘 IO 操做
B-Tree 的结构以下图:
因为 B 树的多分支结构特性,致使树的高度能够大幅降低,这样,若是每一个节点都存储一页数据,若是须要访问第三层数据,则只须要进行三次磁盘 IO,这显然大幅的节省了时间
B+ 树与 B 树的区别在于只有叶子节点存储真实数据,其他非叶子结点仅做为指引搜索方向的数据项
这样存储引擎再也不须要全表扫描,而是根据每一个节点的指引能够快速找到须要的数据
同时,因为 B 树的结构特性,也致使全部的值一般都是按顺序存储的,所以在使用 ORDER BY 操做时,这个索引也能够知足对应的排序需求
CREATE TABLE People (
a varchar(50) not null,
b varchar(50) not null,
c date not null,
d date not null,
e enum('m', 'f') not null,
key(a, b, c, d)
);
对于上面这个表,建立了四列索引,他们遵循下列规则
这是一个很是重要的原则,MySQL 会一直向右匹配直到遇到范围查询(>、<、between、like)
好比查询 a="" and b="2" and c >= 3 and d = 4
在这个查询中,d 是用不到索引的,而若是创建 (a, b, d, c) 则是能够的
同时 where 语句中查询的顺序是能够任意调整的,即 a、b、c、d 的顺序能够任意调整,MySQL 老是按照索引创建的顺序进行查询
尽可能选择区分度高的列做为索引,或是将其放置在左端,区分度越高,即选出的结果行越少,则实际查询的次数就会越少
对于 from_unixtime(a) = '2014-05-29' 这样的查询是不能应用索引的,而应该优化成 a = from_unixtime('2014-05-29')
好比 a+1>5 只有优化为 a > 4 才会应用索引
若是查询 b = 5 and c < 2014 则不会应用索引,这也正是最左前缀匹配原则
对于查询 a=5 and c > 2015,因为跳过了 b 列,因此 c 不会应用索引
上述限制存在于 MySQL 5.5 及之前的数据库版本中,将来的版本可能会取消某些限制
然而,能够看到,建立表时怎样选取索引的列,以及他们的排列顺序是很是重要的
CREATE TABLE testhash (
a varchar(50) not null,
b varchar(50) not null,
KEY USING HASH(a)
) ENGINE=MEMORY;
上面建立表的过程当中建立了一个哈希索引
顾名思义,哈希索引的底层数据结构是用哈希表实现的,只有精确匹配索引全部列的查询才有效
索引会为每一行数据创建一个很小的哈希码,所以哈希索引占用空间小,执行效率高,但只支持等值查询,而不支持范围查询
同时,因为哈希表并不按照值的大小顺序存储,所以在 ORDER BY 操做中并不会应用该索引,也不支持仅使用索引中部分列进行查找
可是,若是是某些特定适合使用哈希索引的场合,索引所带来的性能提高将很是显著,如经典的“星型” schema,须要关联不少查找表,哈希索引就很是适合查找表的需求
哈希索引是 MEMORY 存储引擎的默认索引方式,MEMORY 引擎同时也支持 B-Tree 索引,目前,在 MySQL 中,只有 MEMORY 引擎显式支持哈希索引
InnoDB 引擎有一个特殊的功能 -- 自适应哈希索引,对于被频繁使用的索引值,InnoDB 引擎会自动在内存中建立一个哈希索引,用户只能经过配置选择是否启用这一特性,一旦启用,该过程将是彻底自动,用户没法察觉的
InnoDB 建立的自适应哈希索引和真正的哈希索引并非一回事,而是在原有的 B-Tree 索引的基础上,将检索的值变成哈希码,以下降磁盘使用
针对不支持哈希索引的存储引擎,用户也能够采用相似 InnoDB 的思路去自定义哈希索引
典型的如将 url 变成 CRC32,能够有效节省磁盘使用,而且提升查询速度
如针对下面的查询:
SELECT id FROM url WHERE url = 'http://www.techlog.cn/article/list/10182793';
这样的查询显然是很耗时的,且若是为 url 建立索引,索引也将很是庞大
优化成如下这样:
SELECT id FROM url WHERE crc32_url = CRC32('http://www.techlog.cn/article/list/10182793');
这样,咱们为 crc32_url 字段建立索引,索引的大小、查询效率都会有显著的提高
可是,这样又须要维护一个新的字段 crc32_url,经过建立触发器,能够自动的添加该字段:
CREATE TABLE pseudohash (
id int unsigned NOT NULL auto_increment,
url varchar(255) NOT NULL,
url_crc int unsigned NOT NULL DEFAULT 0,
PRIMARY KEY(id)
KEY(url_crc);
);
DELIMITER //
CREATE TRIGGER pseudohash_crc_ins BEFORE INSERT ON pseudohash FOR EACH ROW BEGIN
SET NEW.url_crc = crc32(NEW.url);
END;
//
CREATE TRIGGER pseudohash_crc_upd BEFORE UPDATE ON pseudohash FOR EACH ROW BEGIN
SET NEW.url_crc = crc32(NEW.url);
END;
//
DELIMITER ;
这样,每当添加或修改 url 字段,触发器会自动更新 url_crc 字段
因为可能存在的哈希冲突,因此直接查询可能会出现多条记录,能够优化为:
SELECT id FROM url WHERE crc32_url = CRC32('http://www.techlog.cn/article/list/10182793') and url = 'http://www.techlog.cn/article/list/10182793';
MyISAM 表支持空间索引,能够用做地理数据存储
与 B-Tree 索引不一样,空间数据索引无需前缀查询,他会从全部维度索引数据,能够任意组合查询
可是必须使用 MySQL 的 GIS 相关函数,如 MBRCONTAINS() 来维护数据,然而 MySQL 对 GIS 支持并不完善,因此大部分人不会使用这个特性
PostgreSQL 的 PostGIS 对 GIS 支持很好
全文索引查找的是文本中的关键词,而不是比较索引中的值,相似于搜索引擎
使用 MATCH AGAINST 操做进行索引,目前不支持中文
还有不少第三方存储引擎使用其余不一样类型的数据结构来存储索引,他们各自有不一样的适用场景和优点