簡體   English   中英

為什么在MariaDB中添加了另一個非聚集索引,主鍵不是聚集索引

[英]Why the primary key is not the clustered index if another non clustered index is added in MariaDB

您好,我有一個由以下查詢創建的表 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)

假設 SELECT 的結果將按跨 dB 引擎的任何列排序是不安全的。 如果您希望進行排序,則應始終使用 ORDER BY col [ASC|DESC]。 我看到記錄按添加順序顯示,但在刪除/插入等之后可能會發生變化,不應依賴。 有關更多詳細信息,請參見此處

(我將在我的回答中引用 MySQL 文檔,但在這個問題的上下文中,該信息也適用於 MariaDB。)

首先,讓我們談談索引擴展 每當您定義二級索引(即任何非聚簇索引的索引)時,InnoDB 引擎都會在后台自動創建一個附加(復合)索引。 這稱為索引擴展 這個額外的索引包含您在原始二級索引中定義的列(以相同的順序)以及在它們之后添加的主鍵列。 因此,在您的示例中,InnoDB 為test_status_IDX創建了一個索引擴展(我們稱之為 X),其中包含列(stauts, id)

現在讓我們看看select * from test; . 這里沒有WHERE子句,所以優化器需要做的就是獲取表中所有行的所有列來滿足這個查詢。 這歸結為獲取statusid ,因為表中沒有其他列。 這些確切的字段恰好存儲在擴展索引 X 中。這使得索引 X 成為該查詢的覆蓋索引 覆蓋索引是一種索引,在給定查詢的情況下,它可以完全生成查詢結果而無需讀取任何實際數據行。 因此,優化器從索引 X 讀取並返回查詢結果所需的值,按照它們出現在那里的順序,即按status ,因此是您觀察到的順序。

為了進一步證明和擴展(雙關語意)這一點,讓我們重現示例(使用 MariaDB 10.4 測試):

1 . 首先創建表並添加行

CREATE TABLE foo (
  id int(10) unsigned NOT NULL AUTO_INCREMENT,
  status varchar(60) DEFAULT NULL,
  PRIMARY KEY (id)
) ENGINE=InnoDB;

INSERT INTO foo VALUES
(1, 'or'),
(2, 'cfrc'),
(3, 'test'),
(4, 'yes'),
(5, 'hjr'),
(6, 'verve');

SELECT * FROM foo;
+----+--------+
| id | status |
+----+--------+
|  1 | or     |
|  2 | cfrc   |
|  3 | test   |
|  4 | yes    |
|  5 | hjr    |
|  6 | verve  |
+----+--------+`

2 . 現在讓我們添加二級索引並再次檢查順序

CREATE INDEX secondary_idx ON foo (status);

SELECT * FROM foo;
+----+--------+
| id | status |
+----+--------+
|  2 | cfrc   |
|  5 | hjr    |
|  1 | or     |
|  3 | test   |
|  6 | verve  |
|  4 | yes    |
+----+--------+

如上所述,行按照它們在(擴展的) secondary_idx中出現的順序返回

3 . 現在讓我們刪除索引並使用 2 個字節的前綴長度重新添加它。 這意味着索引不會存儲列的完整值,而只會存儲它的前兩個字節,這意味着擴展索引不再是覆蓋索引,因為它不能完全產生查詢結果。 因此將使用聚簇索引

ALTER TABLE foo DROP INDEX secondary_idx;

CREATE INDEX secondary_idx ON foo (status(2));

SELECT * FROM foo;
+----+--------+
| id | status |
+----+--------+
|  1 | or     |
|  2 | cfrc   |
|  3 | test   |
|  4 | yes    |
|  5 | hjr    |
|  6 | verve  |
+----+--------+

4 . 讓我們以另一種方式展示這種行為。 在這里,我們將保留原始二級索引(沒有前綴長度),但我們將向表中添加第 3 列。 這將再次使二級索引成為非覆蓋索引(因為它不包含第 3 列),因此,聚集索引也將在這里使用。

ALTER TABLE foo DROP INDEX secondary_idx;

CREATE INDEX secondary_idx ON foo (status);

ALTER TABLE foo ADD bar integer NOT NULL;

SELECT * FROM foo;
+----+--------+-----+
| id | status | bar |
+----+--------+-----+
|  1 | or     |   0 |
|  2 | cfrc   |   0 |
|  3 | test   |   0 |
|  4 | yes    |   0 |
|  5 | hjr    |   0 |
|  6 | verve  |   0 |
+----+--------+-----+

bar添加到索引(或將其從表中刪除)將再次使查詢使用二級索引。

ALTER TABLE foo DROP INDEX secondary_idx;

CREATE INDEX secondary_idx ON foo (status, bar);

SELECT * FROM foo;
+----+--------+-----+
| id | status | bar |
+----+--------+-----+
|  2 | cfrc   |   0 |
|  5 | hjr    |   0 |
|  1 | or     |   0 |
|  3 | test   |   0 |
|  6 | verve  |   0 |
|  4 | yes    |   0 |
+----+--------+-----+

您還可以對上面的所有SELECT語句使用EXPLAIN來查看每個階段使用了哪個索引。

@aprsa 是對的,我錯誤地假設結果將與聚集索引的順序相同,但在這種情況下(使用 INNODB)狀態索引用於查詢的評估,這就是為什么它似乎是按狀態“排序”的. 如果我 select 使用 id,則使用主索引,結果似乎按 id“排序”。 在另一個引擎中,這可能不是真的。

該特定表由 2 個 BTree 組成:

  • 數據,按PRIMARY KEY排序。 是的,它是聚集的,並按 1,2,3,... 排序

  • 二級索引,按status排序。 每個二級索引都包含一個PK的副本,以便它可以到達另一個BTree以獲得列的rest(不是還有更多。),即BTree相當於一個2列表PRIMARY KEY(status)加上一個id

請注意 output 的status順序。 我必須假設它決定按順序讀取二級索引以提供結果。

是的,如果您想要特定的排序,則必須指定ORDER BY 不能假設我剛才討論的細節。 誰知道,明天可能會有其他事情發生,例如內存中的“哈希”,其中信息以其他方式加擾!

(This Answer applies to both MySQL and MariaDB. However, MariaDB is already playing a game with hashing that MySQL has not yet picked up. Be warned! Or simply add an ORDER BY .)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM