1. 背景
最开始不过是一次平淡无奇的报错: 4184 - Server out of disk space,一次社区问题的讨论: 新建索引空间膨胀问题 ,一个新功能特性的诞生: obdiag 索引空间预估 ,直到有机会在这里和大家讨论学习。OceanBase 索引磁盘空间放大问题究其根因是其存储引擎是基于 LSM-Tree 架构实现的,LSM-Tree 的设计理念是将随机写操作转化为顺序写操作,从而提高写入性能。其实现是采用分层的方式,即数据先追加写到日志,再写入内存,内存分为可写与只读两部分,通过冻结可写部分来生成只读部分,通过将只读部分下刷到磁盘形成持久化只读数据,如下图所示。

正是因为这种分层的结构特点,会存在大量冗余和无效数据占用额外的存储,放大了磁盘空间。而 OceanBase 也在权衡读放大、写放大和空间放大问题上不断迭代优化,4.2.3 版本也已经将空间膨胀系数从 5.5 缩小到了 1.5,下文会用到这个系数预估索引空间大小。下面介绍一下如何预估索引空间大小。
2. 手动预估索引空间大小
如下步骤是在 sys 租户下执行的
2.1 根据租户名获取 tenant_id
select tenant_idfrom __all_tenantwhere tenant_name = '租户名';
2.2 根据表名获取 table_id
select table_idfrom __all_virtual_tablewhere table_name = '表名' and tenant_id = '租户id';
2.3 根据表名获取表的空间大小
select svr_ip, svr_port, sum(original_size) as estimated_table_sizefrom __all_virtual_tablet_sstable_macro_infowhere tablet_id in ( select tablet_id from __all_virtual_tablet_to_table_history where tenant_id = '租户id' and table_id = '表id' ) and (svr_ip, svr_port) in ( select svr_ip, svr_port from __all_virtual_ls_meta_table where role = 1 )group by svr_ip, svr_port;
2.4 查询预估表所有列的长度之和
select table_id, sum(data_length) as all_columns_lengthfrom __all_virtual_column_historywhere tenant_id = 'table_id' and table_id = 'table_id';
2.5 查询预创建索引的列长度之和
select
table_id,
sum(data_length) as index_columns_lengthfrom
__all_virtual_column_historywhere
tenant_id = 'table_id'
and table_id = 'table_id'
and column_name in('name1','name2');2.6 根据OB不同版本的膨胀系数计算索引占用的空间大小
# 计算索引列的空间大小estimiated_index_size = (index_columns_length / all_columns_length) * estimated_table_size# 根据膨胀系数估算索引最终占用的磁盘空间大小(4.2.3及之后的版本的放大系数是1.5,之前的为5.5)observer_version >= 4.2.3 ? 1.5 * estimated_index_size : 5.5 * estimated_index_size
3. 使用 obdiag 预估索引空间大小
处理故障时如果想估算一下索引磁盘空间大小,会被手动繁琐的步骤消耗比较多的时间,obdiag 可以用一条命令搞定。
3.1 示例
# obdiag analyze index_space --tenant_name=test1 --table_name=table1 --column_names=name,addr analyze_index_space start ... start query estimated_table_data_size, please wait some minutes... ok +---------------------------------------------------------------------+ | estimated-index-space-report | +---------- ----+------+-----------------------+----------------------+ | ip | port | estimated_index_space | available_disk_space | +--------- -----+------+-----------------------+----------------------+ | 192.168.1.190 | 2882 | 68.41 MB | 55.09 GB | | 192.168.1.191 | 2882 | 68.41 MB | 55.00 GB | | 192.168.1.192 | 2882 | 68.41 MB | 55.11 GB | +---------------+------+-----------------------+----------------------+ Trace ID: c723baec-6391-11ef-9c0c-000c2934fb31 If you want to view detailed obdiag logs, please run: main.py display-trace c723baec-6391-11ef-9c0c-000c2934fb31
3.2 查看帮助
索引磁盘空间估算是 obdiag 的一键分析场景下的功能,通过 --help 查看帮助信息。
# obdiag analyze index_space --help Usage: main.py analyze index_space [options] Options: --inner_config=INNER_CONFIG change inner config. --tenant_name=TENANT_NAME tenant name --table_name=TABLE_NAME table name --index_name=INDEX_NAME specify the index name if an index already exists in the table --column_names=COLUMN_NAMES specify the column names of index that have not been created yet;eg:--column_names=c1,c2,c3 -c C obdiag custom config --config=CONFIG config options Format: --config key=value -h, --help Show help and exit. -v, --verbose Activate verbose output.
3.3 功能场景
目前 analyze 场景的 index_space 功能支持两种场景,一种是索引还未创建,通过将要创建的索引列来分析磁盘空间占用;另一种是分析已存在索引的空间大小。
第一种功能场景在 3.1示例中已经展示,下面展示下第二种场景,只需要将 --column_names 参数替换为 --index_name 即可。
注意:如果两个参数同时存在,将会只分析 --index_name 参数中已存在的索引。
# obdiag main.py analyze index_space --tenant_name=test1 --table_name=table1 --index_name=index1 analyze_index_space start ... start query estimated_table_data_size, please wait some minutes... ok +---------------------------------------------------------------------+ | estimated-index-space-report | +---------------+------+-----------------------+----------------------+ | ip | port | estimated_index_space | available_disk_space | +---------------+------+-----------------------+----------------------+ | 192.168.1.190 | 2882 | 68.41 MB | 54.95 GB | | 192.168.1.191 | 2882 | 68.41 MB | 54.81 GB | | 192.168.1.192 | 2882 | 68.41 MB | 54.91 GB | +---------------+------+-----------------------+----------------------+ Trace ID: ea61b078-661f-11ef-8869-000c2934fb31 If you want to view detailed obdiag logs, please run: main.py display-trace ea61b078-661f-11ef-8869-000c2934fb31
4. obdiag 贡献代码生命周期
obdiag 开源社区非常欢迎小伙伴参与共建,下面以给 obdiag 贡献功能代码《预估索引空间大小》为例,介绍一下贡献代码的生命周期。
4.1 需求提出
需求的提出有多种形式,或是起初功能路线的规划需求,或是实践过程中使用者提出的使用方法及体验需求,或是社区在 GitHub 上贡献的 issue等等。
预估索引空间大小是在 GitHub issue 提出的需求: [Feature]: 支持在创建索引前评估待创建的索引大小。

4.2 需求分析
该功能主要是获取索引列的空间占用,通过 OceanBase 底层字典表进行统计即可,详细过程可参见第 2 章节手动预估索引空间大小。
4.3 设计方案
4.3.1 标题
《obdiag 评估预创建索引大小》
| 编号 | 文档版本 | 修订章节 | 修订原因 | 修订日期 | 修订人 |
| 1 | 0.1 |
|
初稿 | 2024-08-15 | xxx |
4.3.2 需求背景
- 需求来源: [Feature]: 支持在创建索引前评估待创建的索引大小 · Issue #229 · oceanbase/obdiag (github.com)
- 背景:在创建索引时会存在一定的空间放大,特别是大表,非常有必要在创建索引前评估一下待创建索引的占用空间。
4.3.3 功能性设计
4.3.3.1 功能流程图

4.3.3.2 命令选项
obdiag analyze index_space --tenant_name=test1 ---table_name=table1 [--index_name=xx] [--column_names=c1,c2,c3]
4.3.3.3 详细步骤流程
详见第 2 章节手动预估索引空间大小,这里就不重复描述了。
4.3.4 非功能性设计
4.3.4.1 安全
暂未涉及
4.3.4.2 文档
文档补充: 索引空间分析使用说明。
4.3.5 影响评估
4.3.5.1 兼容性评估
命令行参数兼容 json 和 key-value 两种格式。
4.3.5.2 风险评估
暂无风险
4.3.5.3 性能评估
暂无
4.3.6 测试分析
详见下文:4.4提测
4.3.7 工作量评估
4.3.7.1 任务拆解
4.3.7.2 开发计划
4.4 提测
4.4.1 标题
《obdiag 提测文档-评估预创建索引大小》
| 编号 | 文档版本 | 修订章节 | 修订原因 | 修订日期 | 修订人 |
| 1 | 0.1 |
|
初稿 | 2024-08-26 | xxx |
4.4.2 文档链接
4.4.3 提测范围
- 基于索引名称估算索引大小/python3 main.py analyze index_space --tenant_name=CHAT_BI --table_name=t555 --index_name=k1
- 基于列名预估算索引大小/python3 main.py analyze index_space --tenant_name=CHAT_BI --table_name=t555 --column_names=name,addr
4.4.4 影响范围
无影响。
4.4.5 验收状态
- 无需验收
- 已验收
- 未验收
4.4.6 测试项目
| 测试项目 | 项目编号 | 测试内容 | 测试步骤 | 期望结果 |
|---|---|---|---|---|
| 评估预创建索引大小 | 1 | 预估索引大小 | CREATE TABLE `t555` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(100) DEFAULT NULL, `age` int(11) DEFAULT '19', `addr` varchar(200) DEFAULT 'beijing', KEY `k1` (`id`), KEY `k2` (`name`, `addr`)); | 正确输出索引大小 |
4.4.7 自测记录
| 项目编号 | 用例编号 | 测试步骤 | 预期结果 | 测试信息 | 测试结果 | 测试人 | 版本 |
|---|---|---|---|---|---|---|---|
| 1 | 1 | python3 main.py analyze index_space --tenant_name=CHAT_BI --table_name=t555 --index_name=k1 |
![]() |
|
|
|
|
| 1 | 2 | python3 main.py analyze index_space --tenant_name=CHAT_BI --table_name=t555 --column_names=name,addr |
![]() |
|
|
|
|
4.4.8 文档
文档: 索引空间分析使用说明。
4.4.9 附录
4.4 需求上线
在 GitHub 上提交新功能 PR,并和社区维护者讨论修改,如果没问题就可以被 Committer 或 Maintainer 提交完成贡献了。
附上该功能的PR: add analyze index space feature #393。

参考文章
一个存储引擎的“水生态”|OceanBase 转储合并技术原理(一)
与传统LSM-Tree结构的异同 | OceanBase 存储引擎技术原理(二)

