1. 程式人生 > >新特性解讀 | MySQL 8.0 索引特性4-不可見索引

新特性解讀 | MySQL 8.0 索引特性4-不可見索引


MySQL 8.0 實現了索引的隱藏屬性。當然這個特性很多商業資料庫早就有了,比如ORACLE,在11g中就實現了。我來介紹下這個小特性。

 

介紹

INVISIBLE INDEX,不可見索引或者叫隱藏索引。就是對優化器不可見,查詢的時候優化器不會把她作為備選。

其實以前要想徹底不可見,只能用開銷較大的drop index;現在有了新的方式,可以改變索引的屬性,讓其不可見,這一操作只更改metadata,開銷非常小。

 

使用場景

我大概描述下有可能使用隱藏索引的場景:

1. 比如,我有張表t1,本來已經有索引idxf1,idxf2,idxf3。我通過資料字典檢索到idxf3基本沒有使用過,那我是不是可以判斷這個索引直接刪掉就好了?那如果刪掉後突然有新上的業務要大量使用呢?難道我要頻繁的drop index/add index嗎?這個時候選擇開銷比較小的隱藏索引就好了。

2. 我的業務只有一個可能每個月固定執行一次的SQL用到這個索引,那選擇隱藏索引太合適不過了。

3. 又或者是我想要測試下新建索引對我整個業務的影響程度。如果我直接建新索引,那我既有業務涉及到這個欄位的有可能會收到很大影響。那這個時候隱藏索引也是非常合適的。

 

舉例

下面我來簡單舉例如何使用隱藏索引:

表結構

mysql> create table f1 (id serial primary key, f1 int,f2 int );
Query OK, 0 rows affected (0.11 sec)

建立兩個索引,預設可見。

索引1,

mysql> alter table f1 add key idx_f1(f1), add key idx_f2(f2);
Query OK, 0 rows affected (0.12 sec)
Records: 0 Duplicates: 0 Warnings: 0

索引2,

mysql> show create table f1\G
*************************** 1. row ***************************
Table: f1
Create Table: CREATE TABLE `f1` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`f1` int(11) DEFAULT NULL,
`f2` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id` (`id`),
KEY `idx_f1` (`f1`),
KEY `idx_f2` (`f2`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)

簡單寫個造資料的儲存過程。

DELIMITER $$
USE `ytt`$$
CREATE PROCEDURE `sp_generate_data_f1`(
IN f_cnt INT
)
BEGIN
DECLARE i,j INT DEFAULT 0;
SET @@autocommit=0;
WHILE i < f_cnt DO
SET i = i + 1;
IF j = 100 THEN
SET j = 0;
COMMIT;
END IF;
SET j = j + 1;
INSERT INTO f1 (f1,f2) SELECT CEIL(RAND()*100),CEIL(RAND()*100);
END WHILE;
COMMIT;
SET @@autocommit=1;
END$$
DELIMITER ;

生成1W條記錄

mysql> call sp_generate_data_f1(10000);
Query OK, 0 rows affected (5.42 sec)

我們把f2列上的索引變為不可見,結果瞬間完成。

mysql> alter table f1 alter index idx_f2 invisible;
Query OK, 0 rows affected (0.05 sec)
Records: 0 Duplicates: 0 Warnings: 0

在看下錶結構,此時索引標記為Invisible。

mysql> show create table f1 \G
*************************** 1. row ***************************
Table: f1
Create Table: CREATE TABLE `f1` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`f1` int(11) DEFAULT NULL,
`f2` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id` (`id`),
KEY `idx_f1` (`f1`),
KEY `idx_f2` (`f2`) /*!80000 INVISIBLE */
) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)

給一條有f2列過濾的SQL, 發現優化器用不到這個索引了。

mysql> explain select count(*) from f1 where f2 = 52\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: f1
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 9991
filtered: 1.00
Extra: Using where
1 row in set, 1 warning (0.00 sec)

用force index 強制使用,直接報錯。

mysql> explain select count(*) from f1 force index (idx_f2) where f2 = 52\G
ERROR 1176 (42000): Key 'idx_f2' doesn't exist in table 'f1'

那 MySQL 8.0 的優化器開關裡可以告訴它,有的時候可以用隱藏索引。來開啟看看。

mysql> set @@optimizer_switch = 'use_invisible_indexes=on';
Query OK, 0 rows affected (0.00 sec)

那這條SQL 現在可以用idx_f2了。

mysql> explain select count(*) from f1 where f2 = 52\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: f1
partitions: NULL
type: ref
possible_keys: idx_f2
key: idx_f2
key_len: 5
ref: const
rows: 121
filtered: 100.00
Extra: Using index
1 row in set, 1 warning (0.00 sec)

 

總結

INVISIBLE INDEX 的確是一個很有用的小特性,給索引增加了第三個額外的開關選項。想要了解更多的建