磁碟佔用高問題如何排查?三步教你搞定

OceanBase資料庫發表於2022-11-18
作者簡介:楊嘉力,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),可能還有多個版本。_

_
WechatIMG11.jpeg
圖1 查詢某臺機器table_type 大於 0 的使用情況的結果
 

image.png
圖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變大,原因可能是多版本保留位點沒有推。
 

_
WechatIMG12.jpeg
圖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  4294967295
1001    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  4294967295
1002    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

image.png
圖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資料。因此我們要檢視某個索引表的詳細情況。

_
WechatIMG13.jpeg
圖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;

WechatIMG14.png
圖6 查詢某個index_id下的snapshot_version版本所示結果
 
再進一步查詢,執行命令為 select * from _all_acquired_snapshot; 得出結果見圖7,我們可以看到保留的snapshot快照比較多,因為合併較頻繁、疊加建索引時間較久,所以需要保留多版本,故而留下來太多的major版本。

_
image.png
圖7 snapshot快照

 
第三步,我們需要排查為什麼保留了如此多的snapshot快照。

首先排查建索引歷史任務和狀態,執行命令:__all_virtual_sys_task_status,結果如圖8所示。_

_
WechatIMG15.jpeg

圖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.
image.png
圖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所示。
image.png
圖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) / 2*count(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。
image.png
圖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佔用。
 
image.png
圖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,排除多版本的影響。
 _*

*_
image.png
圖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。現在我們基本可以確定是宏塊空間使用率低導致的資料膨脹。
 _

_
image.png
圖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(以上操作僅針對磁碟放大的幾張索引所在的表)。資料重整後,磁碟空間釋放。
 

總結

總的來說,一般需要持久化的資料庫系統必然需要佔用磁碟空間,而磁碟佔用大小取決於使用者寫入的資料,可以簡單理解是一個線性正比關係。另外,出現不符合預期的磁碟佔用情況,比如磁碟資料要比實際資料要大得多時,建議先從基礎的內部表排查是否存在多版本,包括索引表的依賴版本,最後排除是否宏塊利用率低導致的原因,基本就能定位問題並獲得解決思路。

相關文章