关于 Invisible Indexes,不管是官方还是第三方,都有非常多的介绍文档,这里推荐大家可以先看下:
简单来说,Invisible Indexes 的特点是:对优化器来说是不可见的,但是引擎内部还是会维护这个索引,并且不可见属性的修改操只改了元数据,所以可以非常快。 当我们发现某个索引不需要,想要去掉的话,可以先把索引设置为不可见,观察下业务的反应,如果一切正常,就可以 drop 掉;如果业务有受影响,那么说明这个索引删掉会有问题,就可以快速改回来。所以相对于 DROP/ADD 索引这种比较重的操作,Invisible Indexes 就会显得非常灵活方便。
Invisible Indexes 是 server 层的特性,和引擎无关,因此所有引擎(InnoDB, TokuDB, MyISAM, etc.)都可以使用。
MySQL 官方只在 8.0 版本中支持了这一特性,考虑到 8.0 的普及还比较遥远,为了让大家能早日用上这么好的功能,我们将 Invisible Indexes 这一特性 backport 到 AliSQL 分支,目前开源分支已经支持,大家可以下载使用。
虽然官方文档里有详细的使用介绍,本文为了完整性,也简单介绍下使用方法。
ADD INDEX: 我们可以在后续加索引时,指定加的索引是否可见
`id` int(11) DEFAULT NULL,
`tid` int(11) DEFAULT NULL
) ENGINE=InnoDB;
CREATE INDEX idx_tid ON t1(tid) INVISIBLE;
ALTER TABLE t1 ADD INDEX idx_tid(tid) INVISIBLE;
ALTER INDEX: 我们可以在后续使用时,更改已有索引的可见性
CREATE TABLE `t1` (
`id` int(11) DEFAULT NULL,
`tid` int(11) DEFAULT NULL,
) ENGINE=InnoDB;
ALTER TABLE t1 ALTER INDEX idx_tid VISIBLE;
展示信息增加:
INFORMATION_SCHEMA.STATISTICS
内存表和SHOW INDEX
结果里,分别多了一个 Visible/IS_VISIBLE 字段,表示索引是否可见:
下面我们用一例子来看下:
CREATE TABLE `t1` (
`id` int(11) DEFAULT NULL,
`tid` int(11) DEFAULT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO t1 VALUES(1, 2), (3, 4), (5, 6), (7, 8), (9, 10);
可以看到下面的 EXPLAIN 结果,用的是全表扫描:
mysql> EXPLAIN SELECT * FROM t1 WHERE tid=4;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | t1 | ALL | NULL | NULL | NULL | NULL | 5 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
索引改为可见之后,优化器就可以用了:
mysql> ALTER TABLE t1 ALTER INDEX idx_tid VISIBLE;
mysql> EXPLAIN SELECT * FROM t1 WHERE tid=4;
+----+-------------+-------+------+---------------+---------+---------+-------+------+-------+
+----+-------------+-------+------+---------------+---------+---------+-------+------+-------+
| 1 | SIMPLE | t1 | ref | idx_tid | idx_tid | 5 | const | 1 | NULL |
+----+-------------+-------+------+---------------+---------+---------+-------+------+-------+
虽然索引对优化器不可见,但是 MySQL 内部还是会维护索引的,包括约束条件,可以看下面这个例子:
CREATE TABLE `t2` (
`id` int(11) NOT NULL DEFAULT '0',
`tid` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_tid` (`tid`) INVISIBLE
) ENGINE=InnoDB;
mysql> INSERT INTO t2 VALUES (1, 2), (3, 4);
Query OK, 2 rows affected (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> INSERT INTO t2 VALUES (5, 2);
ERROR 1062 (23000): Duplicate entry '2' for key 'idx_tid'
可以看到虽然 idx_tid
索引不可见,但是 UNIQUE 约束还是被遵守的。
使用注意:
- PK 不能设置为不可见,这里的 PK 包括显示的PK,或者因为PK不存在,被提升为 PK 的 UK;
- 虽然设置索引的不可见属性不需要重建表,但是改变了表定义(frm),需要重新打开表,因此会请求 MDL 排它锁,如果有大事务或者长SQL,会被 block,这点使用时需要注意;
另外 AliSQL 支持索引使用统计(INFORMATION_SCHEMA.INDEX_STATISTICS
),和 Invisible Indexes 配合使用效果更佳,比如我们可以根据索引使用找出使用频率低的索引,然后快速设置为不可见,如果业务没有影响的话,就可以进一步 DROP 掉索引。