rocksdb源碼——性能診斷


該文前三部份介紹 statistics、perf context和iostat context和thread status相關內容。最后介紹ThreadLocalPtr實現的原理。

0. 性能診斷類型

  1. statistics:所有線程的所有操作的count/time的累加。

  2. perf context和iostat context: 單個操作(比如get和put)的count/time。

  3. thread status: 用於監視線程的運行時狀態

1. statistics

開銷:

增加5%-10%

頭文件:

include/rocksdb/statistics.h
monitoring/statistics.h

使用方法:

Options options;
options.statistics = rocksdb::CreateDBStatistics();

可選統計級別:

  • 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.1 statistics實現

1.1.1 函數與成員變量

實現了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

merged_sum和merged_hist初始化時都是空的,而且當且僅當線程退出時,才調用mergeThreadValue函數將TickerInfo和HistogreamInfo中的線程局部變量累加到merged_sum和merged_hist。這個實現與ThreadLocalPtr密切相關。

1.1.2 關鍵函數實現

getTickerCount:

  • 用處:指定ticker type獲得count
  • 實現思路:遍歷對應的ticker type的TickerInfo所有線程的線程局部變量的值,累加得到thread_local_sum,再加上當前merged_sum得到最終結果。
  • 加鎖情況:首先加StatisticsImpl::aggregated_lock鎖;調用TickInfo里的ThreadLocalPtr的Fold函數會再加一個保護ThreadData的鏈表的鎖。

histogramData/getHistogramString:

  • 實現思路:遍歷對應的Histograms type的HistogramInfo所有線程的線程局部變量的值,累加得到類型為HistogramImpl的res_hist,再加上當前merged_hist得到最終結果。histogramData將結果轉化成HistogramData的結構體。而getHistogramString將結果轉化成可讀的string。
  • 加鎖情況:首先加StatisticsImpl::merge_lock鎖;調用HistogramInfo里的ThreadLocalPtr的Fold函數會再加一個保護ThreadData的鏈表的鎖。

ToString:

  • 用處:返回可讀的字符串,包括所有的ticker和histogram。
  • 實現思路:依次對TickerInfo數組調用getTickerCount,打印結果;依次對HistogramInfo數組調用histogramData,打印結果。

2. perf context和iostat context

頭文件

include/rocksdb/perf_level.h
include/rocksdb/perf_context.h
include/rocksdb/iostats_context.h

使用方法:

rocksdb::SetPerfLevel(rocksdb::PerfLevel::kEnableTimeExceptForMutex);
rocksdb::perf_context.Reset();
rocksdb::iostats_context.Reset();
... // run your query
rocksdb::SetPerfLevel(rocksdb::PerfLevel::kDisable);

可選統計級別:

  • kDisable: 關閉統計
  • kEnableCount: 統計count
  • kEnableTimeExceptForMutex: 統計count和與mutex無關的time
  • kEnableTime: 統計count和time

統計變量:

extern __thread PerfContext perf_context;
extern __thread IOStatsContext iostats_context;

計數函數(宏):

#define PERF_COUNTER_ADD(metric, value)     \
  perf_context.metric += value;
#define IOSTATS_ADD(metric, value)     \  (iostats_context.metric += value)

通過簡單調用上述兩個宏即可在對應的counter統計值上累加。

而計數相對復雜一點點,使用了一個PerfStepTimer類,實現了start、measure、stop三個函數,類在析構時調用stop函數。

perf context計時宏:

  • PERF_TIMER_GUARD(metric):實例化一個PerfStepTimer變量,將perf_context對應的metric指針寫到PerfStepTimer中。調用start函數。
  • PERF_CONDITIONAL_TIMER_FOR_MUTEX_GUARD(metric, condition) :增加一個開始調用函數的條件。
  • PERF_TIMER_START(metric):調用start函數。
  • PERF_TIMER_MEASURE(metric):調用measure函數,將計時累加perf_context的metric中,重置start時間。
  • PERF_TIMER_STOP(metric) :調用measure函數,將計時累加perf_context的metric中

iostat context計時宏:

  • IOSTATS_TIMER_GUARD(metric):實例化一個PerfStepTimer變量,將iostats_context對應的metric指針寫到PerfStepTimer中。調用start函數。

3. thread status

參考文檔:

docs/_posts/2015-10-27-getthreadlist.markdown

頭文件:

include/rocksdb/env.h
include/rocksdb/thread_status.h
util/thread_operation.h
monitoring/thread_status_updater.h
monitoring/thread_status_util.h

使用方法:

  • 將該線程的統計加入ThreadStatusUpdater:
    調用ThreadStatusUtil::RegisterThread

  • 將該線程的統計從ThreadStatusUpdater刪除:
    ThreadStatusUtil::UnregisterThread

  • 其他修改thread status的函數:
    見monitoring/thread_status_util.h

  • 通過調用env的GetThreadList()函數可以獲得當前后台線程的狀態,狀態的狀態值存放於一個vector中。將其中的內容展現出來,類似於下圖:

第一列是統計的一些參數。第二列和第三列分別是對應的兩個線程的統計值。

3.1 實現

關鍵類:

  • ThreadStatusUpdater:
    存儲了各自后台線程的狀態和所有后台線程狀態的指針。
  • ThreadStatusUtil:
    該類只有靜態變量和靜態方法。
    源碼注釋推薦通過該類的方法去更新ThreadStatusUpdater中的狀態。

他們之間的關系如下:

{{thread status.png(uploading...)}}

番外: 4. ThreadLocalPtr實現

StatisticsImpl類使用了ThreadLocalPtr的原因:
使用__thread關鍵字修飾的變量,能做到線程間的隔離,但是並不能做到實例間的隔離。舉個例子:

class A{
	static __thread int a_;
};
A a1, a2;

雖然不同的線程里,a_是線程局部的。在同一個線程里這兩者使用的是卻是同一個a_
而使用ThreadLocalPtr能使得線程局部變量既做到線程間隔離,又做到實例間隔離。

如何做到線程間隔離,也能做到實例間隔離?

  • 線程間隔離:
    ThreadLocalPtr之間共享一個線程局部變量tls_,tls_是指向ThreadData類型的指針。不同的線程通過不同的tls_地址,指向的是不同的ThreadData。

  • 實例間隔離:
    ThreadData使用vector存放不同實例之間的value。在ThreadLocalPtr實例化時會獲得一個id,id標示它的值存放在vector的位置。這個id能夠區分開不同實例對應的不同的線程局部變量。

基本原理如下:

線程的tls初始值為空。在第一次使用時,通過new實例化ThreadData,並且根據id,對ThreadData中的vector進行resize。

ThreadData的數據實際存放在堆上,ThreadLocalPtr如何管理ThreadData的數據?

  • 線程退出:
    利用pthread_key的機制,設置線程退出函數OnThreadExit,在線程退出時刪除對應的ThreadData。

  • ThreadLocalPtr的實例生命周期結束:
    在所有ThreadData的vector中刪除對應id的數據,回收id。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM