写在前面
当我们执行数据库查询时,我们总是希望结果能够迅速返回。在众多提高查询速度的方法中,使用索引是最普遍的一种。一般来说,索引能够显著地提升查询效率。然而,并不是每一个索引都会起到预期的效果,这可能与索引的建立策略有关,或者与 OceanBase 的成本评估模型相关。
在大量的查询中,我们可能会遇到一些低效的索引,甚至是那些根本不被利用的索引。这些“无效”索引占据了宝贵的存储空间和内存资源,造成了不必要的浪费。因此,识别这些索引并对其进行优化或移除,是维持数据库效率和资源合理利用的重要步骤。
如何使用
OceanBase 索引监控功能已默认激活,且无需用户进行任何额外配置或操作即可使用。为了使监控结果可查询,我们引入了租户级别的视图。此外,我们也提供了租户级别的隐藏配置项,以便用户可以自定义监控设置。值得注意的是,这些配置项及数据的汇总是针对各个租户独立的,互不影响。
当前,我们的关注点主要是识别和排除用户租户中的无效索引,因此监控数据的统计仅限于用户租户,不涉及系统租户。这一点在使用时需要特别留意。
配置项说明
OceanBase 4.3 提供了隐藏配置项来自定义监控设置。虽然 show parameters 无法获取这些配置信息,但可通过查询 GV$OB_PARAMETERS 查看,并通过 alter system set 或启动配置文件设置。一般来说,不推荐修改这些配置项,保持默认值即可。
配置项 | 默认值 | 备注 |
_iut_enable | true | true为打开监控,false为关闭监控 |
_iut_max_entries | 30000 | 表示能保存的索引表的最大个数 |
_iut_stat_collection_type | SAMPLED | 只有“ALL”和“SAMPLED”两种模式 |
备注:
- SAMPLED 模式下的采样率不开放配置。
- 配置项为租户级配置项,不同租户之间设置的值互不干扰。
视图说明
OceanBase 提供 DBA_INDEX_USAGE 视图,用来记录租户级索引统计数据。需要注意的是,当前视图中大部分字段为 预留字段,留作后续版本实现,本次发布生效的,有如下字段: OBJECT_ID,NAME,OWNER,TOTAL_ACCESS_COUNT,TOTAL_EXEC_COUNT,LASE_USED。
视图 DBA_INDEX_USAGE
列名 | 类型 | 含义 |
OBJECT_ID | bigint(20) | 索引表 ID |
NAME | varchar(128) | 索引表名 |
OWNER | varchar(128) | 数据库名 |
TOTAL_ACCESS_COUNT | bigint(20) | 总共访问次数 |
TOTAL_EXEC_COUNT | bigint(20) | 总共执行次数 |
TOTAL_ROWS_RETURNED | bigint(20) | 总共返回的索引表行数 |
BUCKET_0_ACCESS_COUNT | bigint(20) | 索引表使用0次 |
BUCKET_1_ACCESS_COUNT | bigint(20) | 索引表引使用1次 |
BUCKET_2_10_ACCESS_COUNT | bigint(20) | 索引表使用2到10次 |
BUCKET_2_10_ROWS_RETURNED | bigint(20) | 索引表返回2到10行 |
BUCKET_11_100_ACCESS_COUNT | bigint(20) | 索引表访问11到100次 |
BUCKET_11_100_ROWS_RETURNED | bigint(20) | 索引表返回11到100行 |
BUCKET_101_1000_ACCESS_COUNT | bigint(20) | 索引表访问101到1000次 |
BUCKET_101_1000_ROWS_RETURNED | bigint(20) | 索引表返回101到1000行 |
BUCKET_1000_PLUS_ACCESS_COUNT | bigint(20) | 索引表被使用超过1000次 |
BUCKET_1000_PLUS_ROWS_RETURNED | bigint(20) | 索引表返回超过1000行 |
LASE_USED | timestamp | 索引表最后一次被使用的时间 |
查看监控数据
步骤一,建表和建索引
我们在新建表格和索引后插入一定量数据,然后执行 select 查询,并确认查询能走索引查询。这里可通过 explain 查看执行计划。
例如下面我新建了名为 t1 的表,表包含 c1,c2 两列数据,其中 idx1 是表的一个局部索引。
obclient [test]> explain select c1 from t1 where c1=1;
+-----------------------------------------------------------------------+
| Query Plan |
+-----------------------------------------------------------------------+
| ==================================================== |
| |ID|OPERATOR |NAME |EST.ROWS|EST.TIME(us)| |
| ---------------------------------------------------- |
| |0 |TABLE RANGE SCAN|t1(idx1)|2048 |74 | |
| ==================================================== |
| Outputs & filters: |
| ------------------------------------- |
| 0 - output([t1.c1]), filter(nil), rowset=256 |
| access([t1.c1]), partitions(p0) |
| is_index_back=false, is_global_index=false, |
| range_key([t1.c1], [t1.__pk_increment]), range(1,MIN ; 1,MAX), |
| range_cond([t1.c1 = 1]) |
+-----------------------------------------------------------------------+
12 rows in set (0.048 sec)
步骤二,查询索引使用
我们会在后台每隔 15 分钟把监控数据刷到磁盘里。因此,在等待 15 分钟后,索引使用记录会被刷新,此时我们查看 DBA_INDEX_USAGE 视图:
obclient [test]> select * from oceanbase.DBA_INDEX_USAGE;
Empty set (0.042 sec)
需要注意是的是,可能会出现查询为空的场景。这是由于集群启动索引监控为默认的采样模式,如上查询视图时并未查看到索引统计信息,但符合预期,采样模式下的数据可能会丢。如需准确信息,可以在当前租户下,设置监控信息收集模式为 _iut_stat_collection_type=ALL。如:
obclient [test]> alter system set _iut_stat_collection_type="ALL";
Query OK, 0 rows affected (0.056 sec)
备注:Oracle语法与MySQL的稍有差异,下面列举:
obclient [test]> alter system set "_iut_stat_collection_type"='ALL';
Query OK, 0 rows affected (0.064 sec)
重新执行select后,我们再过 15 分钟查询视图确认结果,如下结果的 TOTAL_ACCESS_COUNT 字段说明了 select 操作时,test 数据库下名为 idx1(__idx_500002_idx1)的索引被访问了 1 次,最后被访问的时间点为2023-11-17 16:01:54 。
obclient [test]> select * from oceanbase.DBA_INDEX_USAGE\G;
*************************** 1. row ***************************
OBJECT_ID: 500003
NAME: __idx_500002_idx1
OWNER: test
TOTAL_ACCESS_COUNT: 1
TOTAL_EXEC_COUNT: 1
TOTAL_ROWS_RETURNED: 0
BUCKET_0_ACCESS_COUNT: 0
BUCKET_1_ACCESS_COUNT: 0
BUCKET_2_10_ACCESS_COUNT: 0
BUCKET_2_10_ROWS_RETURNED: 0
BUCKET_11_100_ACCESS_COUNT: 0
BUCKET_11_100_ROWS_RETURNED: 0
BUCKET_101_1000_ACCESS_COUNT: 0
BUCKET_101_1000_ROWS_RETURNED: 0
BUCKET_1000_PLUS_ACCESS_COUNT: 0
BUCKET_1000_PLUS_ROWS_RETURNED: 0
LAST_USED: 2023-11-17 16:01:54.392561
1 row in set (0.049 sec)
清理监控数据
用户可能会担心反复删建索引造成的内存磁盘等资源占用不释放的问题。这点不需要担心,索引表被删除后,我们会在后台定期清理对应的监控数据记录,不需要用户手动干预,例如上面的例子中,我们删除了索引表:
obclient [test]> drop index idx1 on t1;
Query OK, 0 rows affected (0.679 sec)
在等待 15 分钟后,我们再次查看监控视图,记录已经被自动删除。
obclient [test]> select * from oceanbase.DBA_INDEX_USAGE\G;
Empty set (0.049 sec)
注意事项
- 由于内部使用 HashMap 保存索引表使用记录,并开放了隐藏配置项 _iut_max_entries 设置保存的索引表上限,该配置项设置过大可能会造成 HashMap 占租户内存较多,因此不建议设置超过 30000。
- 设置租户级配置项 _iut_stat_collection_type=ALL 模式可能会造成索引查询时一定的性能回退,需谨慎使用。
- 非用户租户的索引使用不在统计范围,视图仅展示用户租户数据。
FAQ
问:索引监控默认启动,会不会影响性能?
答:会,但默认(SAMPLED)模式下影响很小,几乎可以忽略。
问:采样模式下的数据是否准确 ?例如查询了 1 次,是否一定会被记录下来?
答:不一定,采样的目的是为了过滤部分数据,因此某次查询的记录可能会被丢弃。
问:备库的索引监控是只统计自己还是同步主库的?
答:统计是租户级的,备库可以查到主库同步的数据,但不能写入。