【原創】MySQL 模擬條件索引

yifanwu發表於2021-09-09


我們知道,MySQL 不支援條件索引。 什麼是條件索引呢? 條件索引就是在索引列上根據WHERE條件進行一定的過濾後產生的索引。 這樣的索引有以下優勢:

第一點, 比基於這個列的全部索引佔用空間來的小。

第二點, 特別是基於FULL INDEX SCAN 的時候,佔用空間小的索引對記憶體佔用也小很多。

PostgreSQL,SqlServer等都支援條件索引,所以我們先來看下條件索引的實際情況。

表結構如下,記錄大概有10W行:

           Table "ytt.girl1"

 Column |  Type   |     Modifiers     

--------+---------+--------------------

 id     | integer | not null

 rank   | integer | not null default 0

Indexes:

    "girl1_pkey" PRIMARY KEY, btree (id)

    "idx_girl1_rank" btree (rank) WHERE rank >= 10 AND rank <= 100

執行的查詢語句為:

select * from girl1 where rank between 20 and 60 limit 20;

用了全部索引的查詢計劃:

                                                           QUERY PLAN                                                           

---------------------------------------------------------------------------------------------------------------------------------

 Limit  (cost=0.29..36.58 rows=20 width=8) (actual time=0.024..0.054 rows=20 loops=1)

   ->  Index Scan using idx_girl1_rank on girl1  (cost=0.29..421.26 rows=232 width=8) (actual time=0.023..0.044 rows=20 loops=1)

         Index Cond: ((rank >= 20) AND (rank <= 60))

 Total runtime: 0.087 ms

(4 rows)

Time: 1.881 ms

用了條件索引的查詢計劃:

                                                           QUERY PLAN                                                           

---------------------------------------------------------------------------------------------------------------------------------

 Limit  (cost=0.28..35.54 rows=20 width=8) (actual time=0.036..0.068 rows=20 loops=1)

   ->  Index Scan using idx_girl1_rank on girl1  (cost=0.28..513.44 rows=291 width=8) (actual time=0.033..0.061 rows=20 loops=1)

         Index Cond: ((rank >= 20) AND (rank <= 60))

 Total runtime: 0.106 ms

(4 rows)

Time: 0.846 ms

可以看出,在掃描的記錄數以及時間上,條件索引的優勢都很明顯。

接下來,我們在MySQL 模擬下這樣的過程。

由於MySQL 不支援這樣的索引, 在SQL層面上,只能建立一個索引表來儲存對應條件的主鍵以及索引鍵。

ytt>show create table girl1_filtered_index;

+----------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

| Table                | Create Table                                                                                                                                                                                 |

+----------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

| girl1_filtered_index | CREATE TABLE `girl1_filtered_index` (

  `id` int(11) NOT NULL,

  `rank` int(11) NOT NULL DEFAULT '0',

  PRIMARY KEY (`id`),

  KEY `idx_rank` (`rank`)

) ENGINE=InnoDB DEFAULT CHARSET=latin1 |

+----------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

1 row in set (0.00 sec)

接下來,對基礎表的更新操作做下修改,建立了三個觸發器。

DELIMITER $$

USE `t_girl`$$

DROP TRIGGER `filtered_insert`$$

CREATE

   

    TRIGGER `filtered_insert` AFTER INSERT ON `girl1`

    FOR EACH ROW BEGIN

IF new.rank BETWEEN 10 AND 100 THEN

INSERT INTO girl1_filtered_index VALUES (new.id,new.rank);

END IF;

    END;

$$

DELIMITER ;

DELIMITER $$

USE `t_girl`$$

DROP TRIGGER `filtered_update`$$

CREATE

   

    TRIGGER `filtered_update` AFTER UPDATE ON `girl1`

    FOR EACH ROW BEGIN

IF new.rank BETWEEN 10 AND 100 THEN

REPLACE girl1_filtered_index VALUES (new.id,new.rank);

ELSE

DELETE FROM girl1_filtered_index WHERE id = old.id;

END IF;

    END;

$$

DELIMITER ;

DELIMITER $$

USE `t_girl`$$

DROP TRIGGER `filtered_delete`$$

CREATE

   

    TRIGGER `filtered_delete` AFTER DELETE ON `girl1`

    FOR EACH ROW BEGIN

DELETE FROM  girl1_filtered_index WHERE id = old.id;

    END;

$$

DELIMITER ;

OK,我們匯入測試資料。

ytt>load data infile 'girl1.txt' into table girl1 fields terminated by ',';

Query OK, 100000 rows affected (1.05 sec)           

Records: 100000  Deleted: 0  Skipped: 0  Warnings: 0

ytt>select count(*) from girl1;

+----------+

| count(*) |

+----------+

|   100000 |

+----------+

1 row in set (0.04 sec)

ytt>select count(*) from girl1_filtered_index;

+----------+

| count(*) |

+----------+

|      640 |

+----------+

1 row in set (0.00 sec)

這裡,我們把查詢語句修改成基礎表和條件索引表的JOIN。

select a.id,a.rank from girl1 as a where a.id in (select b.id from girl1_filtered_index as b where b.rank between 20 and 60)  limit 20;

當然這只是功能上的一個演示。 最終實現得靠MySQL 5.8了。^____^

©著作權歸作者所有:來自51CTO部落格作者david_yeung的原創作品,如需轉載,請註明出處,否則將追究法律責任

MySQL最佳化條件索引MySQL效能最佳化


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2157/viewspace-2820670/,如需轉載,請註明出處,否則將追究法律責任。

相關文章