这将是一个有点长的答案,但我们开始了。请注意,我不打算在这里处理数据库引擎的差异(MyISAM 和 InnoDB 有不同的方式来实现我试图描述的内容)
关于索引,您必须了解的第一件事是它是存储在磁盘上的单独数据结构。通常这是一个 b-tree 数据结构,包含您已索引的列,还包含指向表中行的指针(此指针通常是主键)。
与数据一起存储的唯一索引是主键索引。因此主键索引是表。
假设您有以下表定义。
CREATE TABLE `Student` (
`StudentNumber` INT NOT NULL ,
`Name` VARCHAR(32) NULL ,
`Surname` VARCHAR(32) NULL ,
`StudentEmail` VARCHAR(32) NULL ,
PRIMARY KEY (`StudentNumber`) );
因为我们在 StudentID 上有一个主键,所以会有一个包含主键和索引中其他列的索引。如果您必须查看索引中的数据,您可能会看到类似这样的内容。
1 , John ,Doe ,Jdoe@gmail.com
如您所见,这是表数据再次向您显示主键索引是表。
StudentNumber 列已编入索引,这使您可以有效地在其上搜索其余数据与密钥一起存储。因此,如果运行以下查询:
SELECT * FROM Student WHERE StudentNumber=1
MySQL 将使用主索引快速查找行并读取与索引列一起存储的数据。由于有索引 MySQL 可以使用索引对 b-tree 进行有效的二叉查找操作。
此外,在执行搜索后检索数据时,MySQL 可以从索引中读取数据,因此我们在索引中使用 1 操作来检索数据。现在,如果我运行以下查询:
SELECT * FROM Student WHERE Name ='Joe'
MySQL 将检查是否存在可用于加快查询速度的索引。但是在我的例子中,名称上没有索引,因此 MySQL 会从第一行到最后一行一次从表中顺序读取一行。
在每一行,它将根据 where 子句评估该行并返回匹配的行。所以基本上它从上到下读取主键索引。记住主键索引是表。
如果我运行以下语句:
ALTER TABLE `TimLog`.`student`
ADD INDEX `ix_name` (`Name` ASC) ;
ALTER TABLE `TimLog`.`student`
ADD INDEX `ix_surname` (`Surname` ASC) ;
MySQL 将在 Student 表上创建新索引。这将存储在磁盘上的表之外,其中的数据如下所示:
Data in ix_Name
John, 1 <--PRIMARY KEY VALUE
Data in ix_Surname
Doe, 1 <--PRIMARY KEY VALUE
请注意 ix_Name 索引中的数据是名称和主键值。太好了,所以如果我运行前面的 select 语句,MySQL 将读取 ix_name 索引并获取匹配项的主键值,然后使用主键索引来获取其余数据。
所以从索引中获取数据的操作数是 2。在索引中找到匹配的行,然后在主键上进行查找以获取行数据。
您现在有以下查询:
SELECT * FROM Student WHERE Name='John' AND surname ='Doe'
这里 MySQL 不能同时使用两个索引,因为这会浪费操作。如果 MySQL 必须在此查询中使用两个索引,则会发生以下情况(这不应该发生)。
1 Find in the ix_Name the rows with the value John
2 Read the primary key that matches to get the row data
3 Store the matching results
4 Find in the ix Surname the rows with the value Doe
5 Read the primary key that matches to get row data.
6 Store the matching results
7 Take the Name results and Surname results and merge them
8 Return query results.
这确实是对 IO 的浪费,因为 MySQL 会读取该表两次。基本上使用一个索引会比尝试使用两个更好(我将在 momnet 中解释原因)。MySQL 将选择 1 个索引用于这个简单的查询。
那么 MySQL 如何决定使用哪个索引呢?
MySQL 在内部保留有关索引的统计信息。这些统计数据基本上告诉 MySQL 索引的唯一性。因此,为了论证,假设姓氏索引 (ix_surname) 比名称索引 (ix_name) MySQL 使用姓氏索引 (ix_surname) 更独特。
因此查询检索将是这样的:
1 Use the ix_surname and find rows that match the value Doe
2 Read the primary key and apply the filter for the value John on the actual column data in the row.
3 Return the matched row.
如您所见,此搜索中的操作数量要少得多。我已经过度简化了很多技术细节。索引是一个有趣的东西,但你必须从我如何以最少的 IO 获取数据的角度来看待它。
希望它现在像泥巴一样清澈!