您好,我有一个由以下查询创建的表MariaDB版本10.5.9
CREATE TABLE `test` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`status` varchar(60) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `test_status_IDX` (`status`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4
我一直认为主键是默认的聚集索引,它也定义了表中行的顺序,但在这里,状态上的索引似乎被选为聚集索引。为什么会发生这种情况,我该如何改变它?
MariaDB [test]> select * from test;
+----+--------+
| id | status |
+----+--------+
| 2 | cfrc |
| 5 | hjr |
| 1 | or |
| 3 | test |
| 6 | verve |
| 4 | yes |
+----+--------+
6 rows in set (0.001 sec)
4条答案
按热度按时间bq3bfh9z1#
假设SELECT的结果将按跨dB引擎的任何列排序是不安全的。|DESC]如果您希望进行排序。我看到记录是按照添加的顺序显示的,但在删除/插入等操作后可能会发生变化,因此不应依赖。有关详细信息,请参阅here。
fdbelqdn2#
(我将在回答中引用MySQL文档,但在此问题的上下文中,这些信息也适用于MariaDB。)
第一点,InnoDB引擎会自动创建一个额外的(复合)索引在定义辅助索引时后台执行(即非聚集索引的任何索引)。这称为 * 索引扩展 *。此额外索引包含在原始辅助索引中定义的列(以相同的顺序),主键的列添加在它们之后。因此,在您的示例中,InnoDB为
test_status_IDX
(我们称之为X)创建了一个索引扩展,列为(stauts, id)
。现在让我们看一下查询
select * from test;
,这里没有WHERE
子句,因此优化器需要做的就是获取表中所有行的所有列,这归结为获取status
和id
,因为表中没有其他列,这些字段恰好存储在扩展索引X中。这会使索引X成为此查询的覆盖索引。覆盖索引是指在给定查询的情况下,无需读取任何实际数据行就可以完全生成查询结果的索引。因此,优化程序会从索引X中读取并返回查询结果所需的值,这些值按在索引X中出现的顺序排列。也就是status
,这就是你观察到的顺序为了进一步演示和扩展(双关语)这一点,让我们重现这个示例(使用MariaDB 10.4测试):
1.首先创建表并添加行
2。现在,让我们添加辅助索引并再次检查顺序
如上所述,这些行按照它们在(扩展)
secondary_idx
中出现的顺序返回3。现在,让我们删除索引,并使用2个字节的前缀长度重新添加它。这意味着索引将不存储列的完整值,而只存储其前两个字节,这意味着扩展索引不再是覆盖索引,因为它无法完全生成查询结果。因此,将使用聚集索引
4。让我们以另一种方式演示此行为。这里我们将保留原始的二级索引(没有前缀长度),但将向表中添加第三列。这将再次使二级索引成为非覆盖索引(因为它不包含第三列),因此,这里也将使用聚集索引。
将
bar
添加到索引(或从表中删除它)将再次使查询使用辅助索引。您还可以在上面的所有
SELECT
语句中使用EXPLAIN
,以查看在每个阶段使用哪个索引。6mw9ycah3#
@aprsa是正确的我错误地假设结果的顺序与聚集索引的顺序相同,但在本例中(使用INNODB),状态索引用于查询的评估,所以它看起来是按状态“排序”的。如果我选择ID,则使用主索引,结果看起来是按ID“排序”的。在另一个引擎中,这可能不是真的。
neskvpey4#
该特定表由2个B树组成:
PRIMARY KEY
排序的数据。是的,它是聚类的,按1、2、3......排序。status
排序。每个二级索引都包含一个PK副本,以便它可以访问另一个BTree以获取其余列(不是说还有更多列!)。也就是说,is BTree等效于一个包含PRIMARY KEY(status)
和id
的2列表。注意输出是如何按照
status
的顺序进行的,我必须假设它决定简单地读取二级索引 * 按照它的顺序 * 来提供结果。是的,如果你想要一个特定的顺序,你 * 必须 * 指定一个
ORDER BY
。你 * 不能 * 假设我刚才讨论的细节。谁知道呢,明天可能会有其他的东西,比如一个内存中的“hash”,它以其他方式对信息进行了编码!(This答案适用于MySQL和MariaDB。但是,MariaDB已经在玩一个MySQL还没有学会的哈希游戏了。请注意!或者简单地添加一个
ORDER BY
。)