MySQL 模拟条件索引

我们知道,MySQL 不支持条件索引。 什么是条件索引呢? 条件索引就是在索引列上根据WHERE条件进行一定的过滤后产生的索引。 这样的索引有以下优势:

第一点, 比基于这个列的全部索引占用空间来的小。

第二点, 特别是基于FULL INDEX SCAN 的时候,占用空间小的索引对内存占用也小很多。


PostgreSQL,SqlServer等都支持条件索引,所以我们先来看下条件索引的实际情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
表结构如下,记录大概有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层面上,只能创建一个索引表来保存对应条件的主键以及索引键。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
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  /*!50032 IF EXISTS */ `filtered_insert`$$
CREATE
     /*!50017 DEFINER =  'root' @ 'localhost'  */
     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  /*!50032 IF EXISTS */ `filtered_update`$$
CREATE
     /*!50017 DEFINER =  'root' @ 'localhost'  */
     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  /*!50032 IF EXISTS */ `filtered_delete`$$
CREATE
     /*!50017 DEFINER =  'root' @ 'localhost'  */
     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。


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



当然这只是功能上的一个演示。 最终实现得靠MySQL 5.8了。^____^

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值