我們知道MySQL 暫時不支援函數索引。 目前大部分資料庫包括PostgreSQL,Oracle等都支援。 什麼是函數索引呢?
函數索引就是說用某固定的函數來對列產生一個基於此函數結果集的索引樹。 好處是開發人員寫SQL變得隨意而且簡單了,但是不好的一點也是如此,必須寫按照固定條件進行的讀取過濾。
在之前呢,如果要實現這樣的功能,MySQL 得建立一個新的列,然後用前置觸發器來修改此列的值。 現在呢,MariaDB有一個虛擬列的特性可以很方便的來實現這個目的。
先來看下在PostgreSQL中的表結構
t_girl=# \d email_list; Table "public.email_list" Column | Type | Modifiers ----------+-----------------------------+----------- id | integer | email | character varying(200) | log_time | timestamp without time zone | Indexes: "idx_email_suffix" btree (substr(email::text, "position"(email::text, '@'::text) + 1))
這張表的EMAIL列屬性上有一個函數索引,目的是來尋找次EMAIL屬性是屬於哪家供應商,比如163,GMAIL等等。
我們給張表產生了20W行記錄。
t_girl=# select count(*) from email_list; count -------- 200000(1 row)Time: 39.851 ms
現在來進行對應的查詢。 如果不嚴格按照這個函數的建立規範,查詢就不走索引,所以一定要嚴格來寫SQL。
QUERY PLAN -------------------------------------------------------------------------------------------------------------------------------------- Aggregate (cost=1607.19..1607.20 rows=1 width=12) (actual time=5.514..5.514 rows=1 loops=1) -> Bitmap Heap Scan on email_list (cost=48.29..1602.08 rows=2047 width=12) (actual time=1.126..4.806 rows=1960 loops=1) Recheck Cond: (substr((email)::text, ("position"((email)::text, '@'::text) + 1)) = '56.com'::text) -> Bitmap Index Scan on idx_email_suffix (cost=0.00..47.78 rows=2047 width=0) (actual time=0.802..0.802 rows=1960 loops=1) Index Cond: (substr((email)::text, ("position"((email)::text, '@'::text) + 1)) = '56.com'::text) Total runtime: 5.603 ms(6 rows)Time: 6.601 ms
從查詢分析計劃中看到,走這個函數索引,掃描了大概2K行記錄,產生結果集1960行。
t_girl=# select count(email) as num from email_list where substr(email,position('@' in email)+1)='56.com'; num ------ 1960(1 row)Time: 5.251 mst_girl=#
接下來,我們看看在MariaDB中如何來實現對應的功能。
表結構如下:
MariaDB [t_girl]> show create table email_list;+------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+| Table | Create Table |+------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+| email_list | CREATE TABLE `email_list` ( `id` int(11) DEFAULT NULL, `email` varchar(200) DEFAULT NULL, `log_time` datetime(6) DEFAULT NULL, `email_suffix` varchar(100) AS (substr(email,position('@' in email)+1)) PERSISTENT, KEY `idx_email_suffix` (`email_suffix`)) ENGINE=InnoDB DEFAULT CHARSET=latin1 |+------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+1 row in set (0.01 sec)
這裡我們用到了MariaDB的虛擬列,並且對虛擬列指定persistent屬性,這樣就能當成真實的屬性來看待了。
行,下來我們利用這個虛擬列來進行查詢,不過這樣反而簡單點,查詢語句不需要那麼嚴格了,直接跟普通的語句一樣。
MariaDB [t_girl]> explain select count(email) from email_list where email_suffix = '56.com';+------+-------------+------------+------+------------------+------------------+---------+-------+------+-----------------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+------+-------------+------------+------+------------------+------------------+---------+-------+------+-----------------------+| 1 | SIMPLE | email_list | ref | idx_email_suffix | idx_email_suffix | 103 | const | 1959 | Using index condition |+------+-------------+------------+------+------------------+------------------+---------+-------+------+-----------------------+1 row in set (0.02 sec)
查詢速度當然也就非常快了。
MariaDB [t_girl]> select count(email) from email_list where email_suffix = '56.com'; +--------------+| count(email) |+--------------+| 1960 |+--------------+1 row in set (0.02 sec)