当前位置:首页 > 问答 > 正文

数据库索引最左原则到底是啥,咋用才最有效,原理又怎么讲清楚

关于数据库索引最左原则,咱们可以把它想象成一本非常厚的、按照“省份 + 城市 + 区”顺序编排的电话号码本,这本电话簿的编排方式,就是索引最左原则最生动的体现。

最左原则到底是啥?

最左原则指的是:当一个数据库索引包含多个列时(比如一个联合索引包含了 A、B、C 三个列),这个索引的生效方式是从最左边的列开始,像爬楼梯一样,一阶一阶地向右匹配,如果最左边的列没有被用到,或者中间某个阶梯“断掉”了,那么索引后面部分的列就无法被用来快速查找。

回到电话簿的例子:

  • 索引就像这本电话簿的编排规则:先按省份排,同一个省份内按城市排,同一个城市内再按区排
  • 最左原则就是:你想利用这本电话簿快速找人,必须先从“省份”这个最左边的条件开始查

我们来对比几种查询方式,你就立刻明白了:

  1. 完美使用索引(从左到右连续)

    数据库索引最左原则到底是啥,咋用才最有效,原理又怎么讲清楚

    你的查询是:“找广东省深圳市南山区”的电话,这完全符合电话簿的编排顺序(省->市->区),你可以飞速地定位到目标页,数据库也是这样,索引被完美利用。

  2. 部分使用索引(用了左,断了右)

    你的查询是:“找广东省”的所有电话,这时,你利用“省份”这个最左边的条件,快速翻到广东省那部分,但因为你没有指定城市和区,所以你只能手动扫描广东省的所有记录,数据库也是类似,它用了索引的“省份”列,但后面的“城市”和“区”列就没用上。

  3. 索引失效(跳过最左)

    • 你的查询是:“找所有城市是深圳市”的电话。这就违反了最左原则!因为你的查询条件跳过了最左边的“省份”,这时你会怎么办?你不可能从电话簿第一页开始,一页一页地翻看每个城市是不是“深圳市”,这个效率极低,我们称之为“全表扫描”,数据库遇到这种情况,它很可能就会放弃使用这个联合索引,转而进行全表扫描,因为那样可能比用一个不匹配的索引更快。
  4. 索引部分失效(中间断了)

    数据库索引最左原则到底是啥,咋用才最有效,原理又怎么讲清楚

    你的查询是:“找广东省南山区”的电话,你看,你跳过了中间的“城市”条件,这时,你利用“省份”快速找到了广东省的部分,但因为没指定“城市”,你无法利用“区”的排序,你仍然需要在广东省的所有记录里,去逐个查看“区”是不是“南山区”,这个查询只用了索引的“省份”这一列,“区”这个索引条件实际上没起到加速作用。

咋用才最有效?

根据上面的原理,要让联合索引最有效,核心就两点:

  1. 查询条件必须包含联合索引的最左列,这是前提,没有它,索引基本就废了。
  2. 尽量让查询条件连续,不要跳过中间的列,虽然跳过中间列不会导致索引完全失效(最左列还在用),但会使得索引后半部分的能力丧失。

在实际写SQL时,要这样思考:

  • 正确用法WHERE 省份='广东' AND 城市='深圳'WHERE 省份='广东' AND 城市='深圳' AND 区='南山'WHERE 省份='广东',这些都遵循了最左原则。
  • 低效或失效用法WHERE 城市='深圳' (跳过了最左的“省份”)、WHERE 省份='广东' AND 区='南山' (跳过了中间的“城市”),对于后一种情况,如果想高效查询,你应该为 (省份, 区) 再单独建立一个联合索引。

范围查询(><BETWEEN)也会“打断”索引。WHERE 省份='广东' AND 城市 > '广州' AND 区='南山',这个查询能用上“省份”和“城市”的索引,但“区”的索引就用不上了,因为“城市”是个范围查询,在这个范围内的“区”是无序的。

数据库索引最左原则到底是啥,咋用才最有效,原理又怎么讲清楚

原理怎么讲清楚?

其背后的原理和数据库索引的数据结构(主要是B+树)紧密相关,你可以这样理解:

想象一下一棵巨大的多层字典树或者公司组织架构图。

  • 索引就是一棵排序的树:这棵大树的第一层节点(根节点),是按照索引的第一个列的值进行排序的,第一层就是按“A,B,C,D...”这样的字母顺序排的(对应我们的“省份”)。
  • 每一层内部再排序:当你进入第一个字母(G”)的分支后,这个分支下的第二层节点,是按照索引的第二个列的值排序的(对应“城市”)。
  • 以此类推:再往下,第三层按第三个列排序(对应“区”)。

这棵树的排序是有严格的先后顺序的:先保证第一列有序,在第一列相同的情况下,保证第二列有序,以此类推。

现在你就能明白最左原则的原理了:

  • 为什么必须从最左开始? 因为这棵树的全局有序性是由最左列保证的,如果你不指定最左列的值,比如直接查第二列“城市=深圳”,数据库就无法快速定位到“深圳”在树的哪个分支,因为“深圳”可能分布在各个“省份”分支下(广东有深圳,广西也有深圳镇),它没有全局有序性,所以只能遍历整棵树的所有叶子节点,也就是全表扫描。
  • 为什么中间不能断? 因为排序是递进的,当你只指定了“省份=广东”和“区=南山”时,数据库利用“广东”快速定位到了分支,但在这个分支里,“区”的值并不是直接有序的,它是在“城市”有序的基础上才有序的,由于你没指定“城市”,数据库不知道“南山”在哪个“城市”下面,所以它只能扫描“广东省”分支下的所有记录,去匹配“区=南山”。

引用自《高性能MySQL》等资料的核心思想):索引的最左原则是由B+树索引的“有序性”本质决定的,索引键的排序方式像是图书馆里给书编号,先是楼层号,再是书架号,最后是层编号,如果你不知道楼层号,直接找书架号,无疑是大海捞针,设计索引和编写查询时,时刻想着这个“电话簿”或“图书编号”的比喻,就能清晰地理解并有效运用最左原则了。