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

SQL语句里为啥用上索引就是快,不用索引就慢呢?

要理解这个问题,我们可以用一个非常生活化的比喻:想象一下,你面前有一本非常厚的、没有目录也没有按任何顺序(比如拼音或笔画)排列的通讯录,里面杂乱无章地记录着成千上万个人的姓名和电话号码,你需要找到“张三”的电话号码,你会怎么做?

你只能从第一页开始,一页一页地翻,一行一行地看,直到找到“张三”这个名字为止,这种最笨拙、最费力的方法,在数据库里就叫做“全表扫描”(Full Table Scan),如果通讯录很薄,只有几页,那可能很快,但如果它有上千页,而你找的人恰好在最后一页,你就需要翻遍整本书,这无疑是非常慢的。

我们给这本通讯录加上一个“索引”,这个索引就像一本薄薄的小册子,是通讯录的“目录”,这个目录做了什么呢?它把所有的人名按照拼音顺序排列好,并且在每个名字后面标明了这个人的详细信息在厚厚的主通讯录中的具体页码。

这时,你再想找“张三”,你不再需要去翻那本厚书了,你拿起这本按拼音排序的索引小册子,因为“张”的拼音是“Zhang”,你迅速翻到“Z”开头的部分,很快就能定位到“张三”这个名字,并且看到它后面写着“主通讯录,第500页”,你直接翻到主通讯录的第500页,一下子就找到了张三的电话号码。

这个对比非常直观地解释了为什么用索引快,不用索引慢。

不用索引(全表扫描)为什么慢?

就像翻那本没有目录的厚通讯录一样,数据库需要从存储设备(比如硬盘)的第一行数据开始,一行一行地读取整个表的所有数据,然后逐行判断这一行数据是不是符合你的查询条件(where name = ‘张三’),硬盘的读写速度相对于内存和CPU来说是非常慢的,这种大量的、无差别的磁盘输入输出操作是导致速度慢的主要原因,数据量越大,需要读取的“页”就越多,耗费的时间就呈线性甚至更快的增长,这就像书越厚,你翻完它所花的时间就越长。

用索引为什么快?

索引之所以快,核心原因在于它通过一种高效的数据结构(最常见的是B-Tree,可以想象成一种层次化的、排序好的目录树),实现了两大优势:

  1. 有序性: 索引里的键值(比如上例中的人名)是预先排好序的,就像按拼音排序的索引小册子,数据库可以使用高效的查找算法(如二分查找),快速定位到目标数据,而无需遍历所有条目,在成千上万的数据中,二分查找可能只需要十几次比较就能找到目标,而全表扫描需要上万次。
  2. 精准定位: 索引中不仅存储了排序后的键值,还存储了指向表中对应数据行的“指针”(类似于页码),一旦在索引中找到了“张三”,数据库就能立刻知道该条记录的物理存储位置,然后直接“翻到那一页”(进行一次或少数几次磁盘读取),把数据拿出来,这极大地减少了需要访问的数据量。

深入一点:索引的工作步骤

当你执行一条带索引的查询语句(如 SELECT * FROM users WHERE name = ‘张三’)时,数据库大致会这样做:

  1. 查找索引: 数据库首先去查找名为“name”的索引,它在这个排序好的索引结构中进行快速搜索,找到所有值为“张三”的条目。
  2. 获取地址: 对于每一个找到的索引条目,数据库从中取出指向实际数据行的指针(ROWID或物理地址)。
  3. 访问数据: 数据库根据这些指针,直接到表的数据块中去读取对应的行数据。

这个过程,大部分工作是在小巧、有序的索引上完成的,只有最后一步才去访问庞大的主数据表,因此速度非常快。

一个重要的提醒:索引不是万能的

虽然索引能极大提升查询速度,但它也有代价,并不是在任何情况下都值得使用。

  • 维护成本: 索引就像书的目录,如果书的内容变了(增、删、改了一页),目录也必须相应更新,同样,当你对表进行插入、删除、更新操作时,数据库不仅需要修改表数据,还需要更新所有相关的索引,这会降低数据写入的速度,索引越多,写操作的成本就越高。
  • 适用场景: 索引最适合在需要从大量数据中筛选出少量数据的查询中使用(比如在上万条记录中找几条),如果你的查询需要返回表中大部分的数据(查询所有姓“张”的人,而表中90%的人都姓张),那么使用索引可能反而更慢,因为这时,数据库需要先在索引中找到所有“张”姓的指针,然后再根据成千上万个指针去主表中一条条取数据,这个来回跳转的过程(称为“回表”),可能还不如直接进行一次全表扫描来得高效,这就像如果一本书里每一页都是你要找的内容,那你直接从头到尾翻一遍比先查目录再根据目录一页页翻要快得多。

SQL语句用索引快,是因为索引像一个精心编排的目录,让数据库能够绕过低效的“逐行扫描”,通过“精确定位”来大幅减少需要检查的数据量,而不用索引慢,是因为数据库被迫使用最笨的“全表扫描”方法,在大量的数据中“大海捞针”,理解这个原理,有助于我们在设计数据库和编写SQL时,做出更合理的决策,知道何时该创建索引,以及如何编写能有效利用索引的查询语句。

(注:以上解释主要基于数据库通用原理,特别是关系型数据库如MySQL、Oracle等常见的B-Tree索引机制,具体实现细节可能因数据库产品而异。)

SQL语句里为啥用上索引就是快,不用索引就慢呢?