MySQL-索引

219 阅读4分钟

索引可以包含一个或多个列的值。如果索引包含多个列,那么列的顺序也十分重要,因为MySQL只能高效的使用索引的最左前缀列。

索引类型

在MySQl中,索引是在存储引擎层而不是服务层实现的

B-Tree索引

上图反映了InnoDB索引是如何工作的。 B-Tree索引能够加快访问数据的速度,因为存储引擎不再需要进行全表扫描来获取需要的数据,取而代之的是从索引的根结点(图示并未画出)开始进行搜索。根节点的槽中存放了指向子节点的指针,存储引擎根据这些指针向下层查找。通过比较节点页的值和要查找的值可以找到合适的指针进入下层子节点,这些指针实际上定义了子节点页中值的上限和下限。最终存储引擎要么是找到对应的值,要么该记录不存在。

页节点比较特别,它们的指针指向的是被索引的数据,而不是其他的节点页(不同引擎的“指针”类型不同)。

假设有如下数据表:

create table people(
last_name varchar(50) not null,
first_name varchar(50) not null,
dob date not null,
gender enum('m','f') not null,
key(last_name,first_name,dob)
);

对于表中的每一行数据,索引中包含了last_name、first_name和dob列的值。下图展示索引如何组织数据存储

使用场景

B-Tree索引适用于全键值、键值范围或键前缀查找。其中键前缀查找只适用于根据最左前缀的查找。

有如下的查询类型:

  • 全值匹配:全值匹配指的是和索引中的所有列进行匹配

  • 匹配最左前缀:只使用索引的前几列

  • 匹配列前缀:只匹配某一列的值的开头部分。例如前面提到的索引可用于查找所有以J开头的姓的人。这里也只使用了索引的第一列

  • 匹配范围值: 例如前面提到的索引可用于查找姓在Allen和Barrymore之间的人。这里也只使用了索引的第一列

  • 精确匹配某一列兵范围匹配另外一列:前面提到的索引也可用于查找所有姓为Allen,并且名字是字母K开头(比如Kim、Karl等)的人。即第一列last_name全匹配,第二列first_name范围匹配

  • 只访问索引的查询:B-Tree通常可以支持“只访问索引的查询”,即查询只需要访问索引,而无需访问数据行。(这种成为索引覆盖)

因为索引书中的节点是有序的,所以除了按值查找之外,索引还可以用于查询中的ORDER BY操作。可以在创建索引的时候,在列名后面指定排序顺序。

限制

  • 如果不是安装索引的最左列开始查找,则无法使用索引。例如之前的例子中,无法用与查找名字为Bill的人,也无法查找某个特定生日的人
  • 不能跳过索引中的某个列。例如不能查找姓为Smith并且某个特定日期出生的人
  • 如果查询中有某个列的范围查询,则其右边所有列都无法使用索引优化查找。例如 有查询 where last_name='smith' and first_name like 'J%' AND dob='1976-12-23',这个查询只能使用索引的前两列,因这里like是一个范围条件。如果范围查询劣质的数量有限,那么可以通过使用多个等于条件来代替范围条件。

哈希索引

哈希索引基于哈希表实现,只有精确匹配索引所有列的查询才有效。对于每一行数据,存储引擎都会对所有的索引列计算一个哈希码。哈希索引将所有的哈希码存储在索引中,同时在哈希表中表村指向每个数据行的指针。

在MySQL中,只有Memory引擎显示支持哈希索引。

空间数据索引(R-Tree)

MyISAM表支持空间索引,可以用作地理数据存储。和B-Tree索引不同,这类索引无需前缀查询。空间索引会从所有维度来索引数据。

全文索引

是一种特殊类型的索引,它查找的是文本中的关键词,而不是直接比较索引中的值。

索引策略(索引的正确使用姿势)

正确创建和使用索引是实现高性能查询的基础

独立的列

如果查询中的列不是独立的,则MySQL不会使用索引。独立的列是指索引不能是表达式的一部分,也不能是函数的参数。 例如,下面这个无法使用actor_id列的索引

select actor_id from sakila.actor where actor_id+1=5;

另一个常见的错误:

select ... where TO_DAYS(CURRENT_DATE) - TO_DAYS(date_col) <=10;

前缀索引长度选取

多列索引

选择合适的索引列顺序