写点什么

磁盘占用高问题如何排查?三步教你搞定

  • 2022-11-18
    浙江
  • 本文字数:4626 字

    阅读完需:约 15 分钟

作者简介:杨嘉力,OceanBase 开源内核高级工程师。


通常情况下,数据库对磁盘的占用量会随着业务的接入时间和业务数据量大增而不断上升,导致磁盘空间不足,进而发生数据无法写入、数据库无法重启等问题。这时我们就需要排查问题根源,使磁盘得以平稳运行。本文以 OceanBase 开源 3.x 版本为例,分享磁盘问题的排查方法,希望对你有所帮助。

排查概括

磁盘问题排查通常包括两方面,一方面,排查磁盘数据 required_size 和实际数据 data size 的大小,如果 required_size 比 data size 要大很多,那么很可能是多版本问题;另一方面,要排查宏块使用率,如果使用率低,排查是不是宏块重用的问题。在正式进入排查阶段前,让我们先了解排查过程中涉及的虚拟表和排查指令。排查过程中涉及的虚拟表和相应的注意事项如下。


  • __all_virtual_meta_table (确认 reqiured_size 统计的是不是最后保存在磁盘的数据长度)

  • __all_virtual_table_mgr (查看 sstable 所占用的空间)

  • __all_virtual_tenant_partition_meta_table(查看租户级的 required_size 和 data_size)

  • __all_virtual_sys_variable

  • __all_acquired_snapshot (查看需要的快照表)


涉及的排查指令如下。


  • 查看压缩算法:show parameters like '%compress%';

  • 查看建表语句(确认使用的压缩算法):show create table xxx\G


现在让我们开始排查磁盘占用高的问题。

排查过程三步走

如果你熟悉 OceanBase 数据库存储架构,那么你会知道,OceanBase 的数据文件由多个 Memable、多个 Minor SSTable 及一个(或没有)Major SSTable 组成。因此,在排查过程中,我们可以遵循下述的“三步走”策略进行排查。**第一步,我们要查看某台机器的 table_type 大于 0 (所有 sstable)的使用情况,**输入指令如下:


select /*+ parallel(30) */ svr_ip, table_type, sum(size) / 1024 / 1024 / 1024 as size_G from __all_virtual_table_mgr where table_type > 0 group by 1,2 order by 3 desc;
复制代码


从查询的结果显示(见图 1),主要问题是基线(major sstable)占用大,但 data_size 和 require size 没匹配(见图 2),可能还有多个版本。_


_



图 1 查询某台机器 table_type 大于 0 的使用情况的结果 



图 2 data_size 和 require size 没匹配


如果你不清楚 table_type 的所有类型,可以参考以下列表。


enum TableType {    MEMTABLE = 0,    MAJOR_SSTABLE = 1,    MINOR_SSTABLE = 2,  // obsoleted type after 2.2    TRANS_SSTABLE = 3,  // new table type from 3.1    MULTI_VERSION_MINOR_SSTABLE = 4,    COMPLEMENT_MINOR_SSTABLE = 5,            // new table type from 3.1    MULTI_VERSION_SPARSE_MINOR_SSTABLE = 6,  // reserved table type    MINI_MINOR_SSTABLE = 7,    RESERVED_MINOR_SSTABLE = 8,    MAX_TABLE_TYPE  };
复制代码


摸清了问题方向和 table_type 的所有类型后,我们进入**第二步,排查基线数据 major sstable 涉及的版本,**指令如下:


select /*+ parallel(30) */ svr_ip, version, sum(size) / 1024 / 1024 / 1024 as size_G from __all_virtual_table_mgr where table_type = 1 group by 1,2 order by 3 desc;__
复制代码


__执行结果(见图 3)显示集群中保留了基线的多版本数据,而且有大量的历史版本,导致 required size 变大,原因可能是多版本保留位点没有推。 


_



图 3 基线数据 major sstable 涉及版本的排查结果 此时,我们输入 select * from __all_virtual_sys_variable where name like '%undo_rete%'进行查询,查询结果见图 4。


1       undo_retention  2022-06-14 20:50:15.821272  2022-06-14 20:50:15.821272  5  0  specifies (in seconds) the low threshold value of undo retention.  1  0  42949672951001    undo_retention  2022-06-14 20:58:07.599412  2022-06-14 20:58:07.599412  5  0  specifies (in seconds) the low threshold value of undo retention.  1  0  42949672951002    undo_retention  2022-06-22 18:00:03.182527  2022-06-22 18:00:03.182527  5  0  specifies (in seconds) the low threshold value of undo retention.  1  0  4294967295
复制代码



图 4 查询多版本保留位点的结果 为什么会保留这些版本呢,我们继续找一个 svr ip 分析,执行 select * from _all_virtual_table_mgr where svr_ip = xxx' and version = 100 limit 3; 结果(见图 5)显示,ip=xxx 机器下存在多个索引表,由于索引表的创建需要依赖主表的 snapshot_version,会 hold 住 snapshot_version 数据。因此我们要查看某个索引表的详细情况。


_



图 5 svr ip 分析结果 下面继续查询某个 index_id 下的 snapshot_version 版本,执行如下命令后得出图 6 所示结果:


select * from __all_virtual_table_mgr where svr_ip = xxx and index_id = 1101710651081814 and partition_id = 5 order by snapshot_version asc;
复制代码



图 6 查询某个 index_id 下的 snapshot_version 版本所示结果 再进一步查询,执行命令为 select * from _all_acquired_snapshot; 得出结果见图 7,我们可以看到保留的 snapshot 快照比较多,因为合并较频繁、叠加建索引时间较久,所以需要保留多版本,故而留下来太多的 major 版本。


_



图 7 snapshot 快照


 第三步,我们需要排查为什么保留了如此多的 snapshot 快照。


首先排查建索引历史任务和状态,执行命令:_all_virtual_sys_task_status,结果如图 8 所示。


_



图 8 排查建索引历史任务和状态


我们从图 8 中可以得到的信息是,命名为”1101710651081782“的表保留了两个 snapshot,由于新建索引对 snapshort 有依赖,可能导致 snapshort 一直不被删除,因此我们要排查 1101710651081782 table 的 index_status 状态,执行命令:select table_id, index_status from __all_virtual_table where data_table_id = 1101710651081782; 结果见图 9.



图 9 1101710651081782 table 的 index_status 状态 需要注意的是,图 9 中的 table_id 是指索引表的 table_id,data_table_id 是指主表的 table_id。从此处看 index_status=2,表示索引是 avaliable 的,理论上可以排除建索引失败导致的 snapshot 保留的问题。接下来可以查看所有在__all_acquired_snapshot 快照表中的 table 的建索引状态,排查所有在 snapshot 表中的 table 是否存在建索引异常,执行命令:select table_id, index_status from __all_virtual_table where data_table_id in (select table_id from __all_acquired_snapshot);得出结果如图 10 所示。



图 10 所有在 snapshot 表中的 table 状态查询结果我们结合图 10 中显示的查询结果与 index_status 的取值(如下),可以排除是新建索引失败导致的 snapshot 未被删除,进而导致快照点未释放,因此 major merge 的增多加剧了磁盘空间的占用。


enum ObIndexStatus {  // this is used in index virtual table:__index_process_info:  // means the table may be deleted when you get it  INDEX_STATUS_NOT_FOUND = 0,  INDEX_STATUS_UNAVAILABLE = 1,  INDEX_STATUS_AVAILABLE = 2,  INDEX_STATUS_UNIQUE_CHECKING = 3,    // not used anymore  INDEX_STATUS_UNIQUE_INELIGIBLE = 4,  // not used anymore  INDEX_STATUS_INDEX_ERROR = 5,  INDEX_STATUS_RESTORE_INDEX_ERROR = 6,  INDEX_STATUS_UNUSABLE = 7,  INDEX_STATUS_MAX = 8,};
复制代码


到此,我们的排查与分析就告一段落了,基本原因已经明确。如何解决呢?**解决办法很简单,就是将__all_acquired_snapshot 表格的记录删掉,使后台任务删除__all_acquired_snapshot 的快照即可。**指令为:delete * from __all_acquired_snapshot。但是,事情并未结束!

OceanBase 集群磁盘占用异常排查

当你操作完上述三步,你发现 OceanBase 集群的磁盘空间占用为 207TB,磁盘占用依然处于高位。这显然不符合正常情况,就需要继续定位。排查统计方法的问题,将 OceanBase 集群的数据通过导入工具,从 MySQL 集群中导出,并从 information_schema.tables 汇总的所有非 MySQL 原生表的数据量看,数据量正常的情况下存储却膨胀近一倍,从 create table 语句中可以明确用户已经开了压缩,在排除数据压缩的问题后,还有一个可能的原因是宏块中存在较多空间未被使用。我们执行以下命令查看所有宏块的空间使用情况:select /+ parallel(30) / 2count(1)/1024 as size_g, sum(occupy_size)/1024/1024/1024 as occupy_G from __all_virtual_partition_sstable_macro_info where tenant_id = 1001;从图 11 显示的查询结果可见,宏块空间未满,剩余 70TB。

图 11 宏块的空间使用情况 为什么会存在 70TB 如此大的宏块磁盘空间未被使用?一般情况下,做一次 major merge 后,存储系统会将 mini sstable、minor sstable 与 major 基线做一次全量合并,合并过程重整宏块以紧凑宏块空间,增大宏块空间利用率。因此,我们可以查询是不是基线 major sstable 未做合并或合并时未对宏块进行重整,导致了宏块空间使用率低的问题。在此之前,我们还需要排除下面两种 mini sstable 和 minor sstable 宏块多造成磁盘使用高的干扰。第一,确认有没有转储对宏块使用的影响(排查是否频繁转储生成 mini sstable),命令如下:select /+ parallel(30) / table_type, sum(size) / 1024 / 1024 / 1024 as size_G from __all_virtual_table_mgr where table_type > 0 group by 1 order by 2 desc;排查结果如图 12 所示,可见磁盘空间主要被 major sstable 占用。 
图 12 排查是否频繁转储生成 mini sstable 的结果 第二,确认有没有多个不同版本的 major (排查是否有多版本 major sstable 导致磁盘使用高),命令如下:select /+ parallel(30) / version, sum(size) / 1024 / 1024 / 1024 as size_G from __all_virtual_table_mgr where table_type =1 group by 1 order by 2 desc;排查结果见图 13,从中可知,只有一个 114 版本的 major sstable,排除多版本的影响。 _


*_



图 13 排查是否有多版本 major sstable 导致磁盘使用高的结果 排除了 mini sstable 和 minor sstable 宏块多造成磁盘使用高的干扰后,我们继续排查所有表中 data_version=114,tenant_id=1001 的 sstable 宏块使用情况,命令如下:


select  /*+ parallel(100) */ table_id, count(1), 2*count(1)/1024 as size_g, sum(occupy_size)/1024/1024/1024 as occupy_G from __all_virtual_partition_sstable_macro_info where data_version = 114 and tenant_id = 1001 group by 1 order by 2 desc limit 30;_
复制代码


_从排查结果(见图 14)中分析,table_id 为 1100611139454851 的宏块空间利用率很低。650/4000,不到 1/7。现在我们基本可以确定是宏块空间使用率低导致的数据膨胀。 _


_



图 14 所有表中 data_version=114,tenant_id=1001 的 sstable 宏块使用情况 到此,磁盘空间膨胀(膨胀空间 207TB,实际空间是 140TB)的原因已经确定是索引宏块没有写满导致的。 解决办法是进行数据重整,可以修改参数 : alter table tablename set progressive_merge_num = 1, 然后执行 alter system major freeze,合并结束后,设置 progressive_merge_num = 0(以上操作仅针对磁盘放大的几张索引所在的表)。数据重整后,磁盘空间释放。 

总结

总的来说,一般需要持久化的数据库系统必然需要占用磁盘空间,而磁盘占用大小取决于用户写入的数据,可以简单理解是一个线性正比关系。另外,出现不符合预期的磁盘占用情况,比如磁盘数据要比实际数据要大得多时,建议先从基础的内部表排查是否存在多版本,包括索引表的依赖版本,最后排除是否宏块利用率低导致的原因,基本就能定位问题并获得解决思路。

用户头像

企业级原生分布式数据库 2020-05-06 加入

github:https://github.com/oceanbase/oceanbase 欢迎大家

评论

发布
暂无评论
磁盘占用高问题如何排查?三步教你搞定_OceanBase 数据库_InfoQ写作社区