更多騰訊海量技術文章, 請關注雲+社區:https://cloud.tencent.com/developer
作者:騰訊雲資料庫內核團隊
在facebook的MySQL版本(以下稱為MyRocks)中, RocksDB是可選的存儲引擎。 相比於InnoDB引擎, RocksDB的一個重要的優勢是它使用更少的磁碟空間。 在生產系統中, 特別是使用者數在億級以上的互聯網應用, 磁碟空間是其中比較大的成本之一, 而能夠使用更少的磁碟空間的RocksDB無疑是具有吸引力的。 然而在生產系統中使用新的存儲引擎自然有它的潛在風險, 除了通過外部的各種benchmark工具測試得到各種性能資料, 全方位的內部指標可以説明我們真正瞭解資料庫內部正在發生的事情,
本文將介紹SHOW ENGINE ROCKSDB STATUS中關於STATISTICS統計值與後臺執行緒的實現原理。 在瞭解實現原理的基礎上, 便可以較容易地通過擴展功能使它更好地為我們服務。
調用SHOW ENGINE ROCKSDB STATUS指令會返回多行資料, 其中包括:
STATISTICS:RocksDB引擎所有執行緒的所有操作的各類count/time的累加, 比如rocksdb.block.cache.hit和rocksdb.db.write.micros。
BG_THREADS: 後臺執行緒的狀態。
DBSTATS: 資料庫操作的統計。
CF_COMPACTION: 各個Column family進行compaction的相關指標統計。
MEMORY_STATS: 記憶體使用情況。
調用SHOW ENGINE ROCKSDB STATUS會返回若干行資料, 然而這些資料並非事先存儲於某個表格中, 而是通過調用位於rocksdb/ha_rocksdb.cc檔中的rocksdb_show_status函數將記憶體中對應的數值進行規整返回給用戶。
1. STATISTICS根據RocksDB官方相關文檔介紹STATISTICS, 開啟STATISTICS會增加增加5%-10%額外開銷。
STATISTICS統計值記錄著RocksDB引擎所有執行緒的所有操作的各類count/time的累加。 RocksDB引擎在它的各類操作如Put/Get/Delete中的代碼都設立了很多埋點。
以函數GetEntryFromCache為例, 它的作用是返回可用的block cache。 特別地, 可以看到statistics是GetEntryFromCache和block_cache->Lookup的一個參數。 沒錯, 就是靠著statistics這個參數它到處收集資料。 當有可用的block cache時, 調用了三次RecordTick為其中三個統計值增加計數;沒有可用的block cache, 同樣也為BLOCK_CACHE_MISS和block_cache_miss_ticker增加計數。
1.1 RocksDB的STATISTICS介面
使用STATISTICS的方法也很簡單。
它的標頭檔位於:
使用方法:
可選統計級別:
kExceptDetailedTimers: 除去mutex等待和壓縮的計時
kExceptTimeForMutex: 除去mutex等待的計時
kAll: 所有
資料統計類型分成兩種:
ticker:計數, 類型是64位元無符號整型。 用於度量counters (e.g. “rocksdb.block.cache.hit”), cumulative bytes (e.g. “rocksdb.bytes.written”) 或者 time (e.g. “rocksdb.l0.slowdown.micros”)。
histogram:統計資料的統計分佈, 包括最大值、最小值、平均值、中位數、標準差。
統計函數的介面:
MeasureTime:函數名有歧義。 實際上是把value記錄到histogram中。
RecordTick:累加ticker。
獲取結果的介面:
Statistics::getTickerCount:指定ticker type獲得count。
Statistics::histogramData:指定Histograms type, 返回一個HistogramData結構體, 成員是統計值, 包括最大值、最小值、平均值、中位數、標準差。
Statistics::getHistogramString:指定Histograms type, 返回長條圖可讀的字串。
Statistics::ToString():返回可讀的字串, 包括所有的ticker和histogram。
1.2 RocksDB的STATISTICS實現
RocksDB實現了StatisticsImpl類, 繼承了Statistics的介面。
主要介面:
getTickerCount
histogramData
getHistogramString
getAndResetTickerCount
recordTick
measureTime
ToString
成員變數:
TickerInfo tickers_[INTERNAL_TICKER_ENUM_MAX];
HistogramInfo histograms_[INTERNAL_HISTOGRAM_ENUM_MAX];
這裡的TickerInfo和HistogramInfo類型的資料結構是相似的:一個執行緒局部的counter或者time;加上一個非執行緒局部的統計值用來累加counter或者time。
TickerInfo類型包含兩個參數:
ThreadLocalPtr類型(真實類型ThreadTickerInfo)的thread_value, 包含:
整型類型的value
指向merged_sum的指標
整型類型的merged_sum
HistogreamInfo類型包含兩個參數:
ThreadLocalPtr類型(真實類型ThreadHistogramInfo)的thread_value, 包含:
HistogramImpl類型的value
指向merged_hist的指標
指向merge_lock的指標
HistogramImpl類型的merged_hist
Mutex類型的merge_lock
事實上, STATISTICS相關實現是比較巧妙的, 也是使用STATISTICS僅增加5%-10%的關鍵。 為了避免執行緒間共用資料導致CPU的cache頻繁失效, merged_sum和merged_hist初始化時都是空的, 而且當且僅當執行緒退出時, 才調用mergeThreadValue函數將TickerInfo和HistogreamInfo中的執行緒區域變數累加到merged_sum和merged_hist。
1.3 MyRocks的使用
MyRocks使用了RocksDB提供的介面進行資料統計。 通過聲明了變數rocksdb_stats, 並且隨著RocksDB引擎啟動時通過rocksdb_init_func函數進行初始化。
除了使用所有RocksDB引擎層的統計, MyRocks還通過定義了
在rocksdb_commit_by_xid和rocksdb_commit兩個函數中通過計時的方式, 統計了每一次commit所花費的時間。
在rocksdb_show_status函數中, 輸出Statistics統計的過程如下:
如果定義rocksdb_stats, 則調用rocksdb_stats->ToString()將統計值轉化為可讀的字串;
commit_latency_stats是長條圖的類型, 輸出對應的50%, 95%, 99%, 100%四個位點的對應的值。
假如定義了is-write-stopped或者actual-delayed-write-rate等Property變數, 同樣會將它們輸出。
2. 後臺執行緒通過調用SHOW ENGINE ROCKSDB STATUS可以得到與BG_THREADS相關結果, 它的輸出結果類似於:
可以看到較多的信息量:這個執行緒正在進行Compaction,處於CompactionJob::ProcessKeyValueCompaction階段,已經耗時6172.244 ms,讀取的位元組數為992806363,寫出的位元組數為992071408。然而並不包括可能感興趣的正在進行Compaction的原始檔案和目的檔案等資訊。正如文章開頭提到的,瞭解實現原理能夠使我們更好地進行擴展。
2.1 thread status的介面與實現
MyRocks中的SHOW ENGINE ROCKSDB STATUS指令展示BG_THREAD的機制使用了RocksDB中關於thread status的介面。
它的標頭檔位於:
關鍵類:
ThreadStatusUpdater:存儲了各自後臺執行緒的狀態和所有後臺執行緒狀態的指標。ThreadStatusUtil:該類只有靜態變數和靜態方法,推薦通過該類的方法去更新ThreadStatusUpdater中的狀態。
使用方法:
將該執行緒的統計加入ThreadStatusUpdater:調用ThreadStatusUtil::RegisterThread
將該執行緒的統計從ThreadStatusUpdater刪除:調用ThreadStatusUtil::UnregisterThread
其他修改thread status的函數:見monitoring/thread_status_util.h
通過調用env的GetThreadList()函數可以獲得當前後臺執行緒的狀態,狀態的狀態值存放於一個vector中。將其中的內容展現出來,類似於下圖:
從代碼中可以看到,實現thread status的目的展示flush和compaction的運行狀態。當然,我們也可以將使用者執行緒的狀態存儲到thread status,通過調用SHOW ENGINE ROCKSDB STATUS指令展示。
特別地,可以看到compaction特有的狀態值有:
flush特有的狀態值有:
2.2 MyRocks/RocksDB的使用
在RocksDB的執行緒池實現中,每一個啟動的後臺執行緒都會通過調用ThreadStatusUtil::RegisterThread加入被觀測的後臺執行緒的集合中。
在rocksdb_show_status函數中,輸出BG_THREAD的過程如下:
通過調用GetThreadList(&thread_list)獲得所有後臺執行緒的ThreadStatus的集合。
通過遍歷ThreadStatus的集合將每一個後臺執行緒的狀態依次輸出。
3. 小結本文章介紹了SHOW ENGINE ROCKSDB STATUS指令中關於STATISTICS與BG_THREAD的相關內容。
可以看到較多的信息量:這個執行緒正在進行Compaction,處於CompactionJob::ProcessKeyValueCompaction階段,已經耗時6172.244 ms,讀取的位元組數為992806363,寫出的位元組數為992071408。然而並不包括可能感興趣的正在進行Compaction的原始檔案和目的檔案等資訊。正如文章開頭提到的,瞭解實現原理能夠使我們更好地進行擴展。
2.1 thread status的介面與實現
MyRocks中的SHOW ENGINE ROCKSDB STATUS指令展示BG_THREAD的機制使用了RocksDB中關於thread status的介面。
它的標頭檔位於:
關鍵類:
ThreadStatusUpdater:存儲了各自後臺執行緒的狀態和所有後臺執行緒狀態的指標。ThreadStatusUtil:該類只有靜態變數和靜態方法,推薦通過該類的方法去更新ThreadStatusUpdater中的狀態。
使用方法:
將該執行緒的統計加入ThreadStatusUpdater:調用ThreadStatusUtil::RegisterThread
將該執行緒的統計從ThreadStatusUpdater刪除:調用ThreadStatusUtil::UnregisterThread
其他修改thread status的函數:見monitoring/thread_status_util.h
通過調用env的GetThreadList()函數可以獲得當前後臺執行緒的狀態,狀態的狀態值存放於一個vector中。將其中的內容展現出來,類似於下圖:
從代碼中可以看到,實現thread status的目的展示flush和compaction的運行狀態。當然,我們也可以將使用者執行緒的狀態存儲到thread status,通過調用SHOW ENGINE ROCKSDB STATUS指令展示。
特別地,可以看到compaction特有的狀態值有:
flush特有的狀態值有:
2.2 MyRocks/RocksDB的使用
在RocksDB的執行緒池實現中,每一個啟動的後臺執行緒都會通過調用ThreadStatusUtil::RegisterThread加入被觀測的後臺執行緒的集合中。
在rocksdb_show_status函數中,輸出BG_THREAD的過程如下:
通過調用GetThreadList(&thread_list)獲得所有後臺執行緒的ThreadStatus的集合。
通過遍歷ThreadStatus的集合將每一個後臺執行緒的狀態依次輸出。
3. 小結本文章介紹了SHOW ENGINE ROCKSDB STATUS指令中關於STATISTICS與BG_THREAD的相關內容。