该文前三部份介绍 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。

rocksdb源码——性能诊断的更多相关文章

  1. 【性能为王】从PHP源码剖析array_keys和array_unique

    之前在[译]更快的方式实现PHP数组去重这篇文章里讨论了使用array_flip后再调用array_keys函数替换直接调用array_unique函数实现数组去重性能较好.由于原文没有给出源码分析和 ...

  2. 修改Flume-NG的hdfs sink解析时间戳源码大幅提高写入性能

    Flume-NG中的hdfs sink的路径名(对应参数"hdfs.path",不允许为空)以及文件前缀(对应参数"hdfs.filePrefix")支持正则解 ...

  3. lesson8:AtomicInteger源码解析及性能分析

    AtomicInteger等对象出现的目的主要是为了解决在多线程环境下变量计数的问题,例如常用的i++,i--操作,它们不是线程安全的,AtomicInteger引入后,就不必在进行i++和i--操作 ...

  4. 性能秒杀log4net的NLogger日志组件(附测试代码与NLogger源码)

    NLogger特性: 一:不依赖于第三方插件和支持.net2.0 二:支持多线程高并发 三:读写双缓冲对列 四:自定义日志缓冲大小 五:支持即时触发刷盘机制 六:先按日期再按文件大小滚动Rolling ...

  5. [Spark性能调优] 源码补充 : Spark 2.1.X 中 Unified 和 Static MemoryManager

    本课主题 Static MemoryManager 的源码鉴赏 Unified MemoryManager 的源码鉴赏 引言 从源码的角度了解 Spark 内存管理是怎么设计的,从而知道应该配置那个参 ...

  6. Android布局性能优化—从源码角度看ViewStub延迟加载技术

    在项目中,难免会遇到这种需求,在程序运行时需要动态根据条件来决定显示哪个View或某个布局,最通常的想法就是把需要动态显示的View都先写在布局中,然后把它们的可见性设为View.GONE,最后在代码 ...

  7. Java开源生鲜电商平台-性能优化以及服务器优化的设计与架构(源码可下载)

    Java开源生鲜电商平台-性能优化以及服务器优化的设计与架构(源码可下载) 说明:Java开源生鲜电商平台-性能优化以及服务器优化的设计与架构,我采用以下三种维度来讲解 1.  代码层面. 2.  数 ...

  8. IdentityServer4源码颁发token分析及性能优化

    IdentityServer4源码地址 IdentityModel源码地址 以下的流程用ResourceOwnerPassword类型获取token作为介绍 分两种获取形式说明 token请求地址为默 ...

  9. 大数据学习--day14(String--StringBuffer--StringBuilder 源码分析、性能比较)

    String--StringBuffer--StringBuilder 源码分析.性能比较 站在优秀博客的肩上看问题:https://www.cnblogs.com/dolphin0520/p/377 ...

随机推荐

  1. 飘逸的python - property及实现lazy property

    @property有什么用呢?表面看来,就是将一个方法用属性的方式来訪问. 上代码,代码最清晰了. class Circle(object): def __init__(self, radius): ...

  2. [转载]MVC中单用户登录

    转自:http://www.cnblogs.com/firstcsharp/archive/2013/05/19/3087481.html 把下面这段代码放在登录用户验证以后:   //用户登录验证通 ...

  3. CSS Sprite的相关概念

    原文 简书原文:https://www.jianshu.com/p/8ae3be23a642 大纲 1.什么是CSS Sprite 2.CSS Sprite的优点 3.CSS Sprite的缺点 4. ...

  4. 数学分析告诉偶们什么(vamei)

    1]人生的痛苦在于追求错误的东西.所谓追求错误的东西,就是你在无限趋近于它的时候,才猛然发现,你和它是不连续的. 2]人和人就像数轴上的有理数点,彼此能够靠得非常近非常近,但你们之间始终存在隔阂. 3 ...

  5. 【Solr专题之九】SolrJ教程 分类: H4_SOLR/LUCENCE 2014-07-28 14:31 2351人阅读 评论(0) 收藏

    一.SolrJ基础 1.相关资料 API:http://lucene.apache.org/solr/4_9_0/solr-solrj/ apache_solr_ref_guide_4.9.pdf:C ...

  6. html5的float属性超详解(display,position, float)(文本流)

    html5的float属性超详解(display,position, float)(文本流) 一.总结 1.文本流: 2.float和绝对定位都不占文本流的位置 3.普通流是默认定位方式,就是依次按照 ...

  7. 【u215】河床

    问题描述 小明是一个地理学家,经常要对一段河流进行测量分析.他从上游开始向下游方向等距离地选择了N个点测量水位深度.得到一组数据d1,d2,--,dn,回到实验室后数据分析员根据需要对数据进行分析,发 ...

  8. IDEA多模块父子依赖maven项目war包部署

    IDEA多模块父子依赖maven项目war包部署 Posted on 2018-04-25 | In IDEA | | Visitors 286 IDEA全称为IntrlliJ IDEA,它是一款非常 ...

  9. Tools:downloading and Building EDK II工具篇:安装/使用EDKII源代码获取/编译工具[2.3]

    Tools:Installing and using the Required Tools for downloading and Building EDK II工具篇:安装/使用EDKII源代码获取 ...

  10. unresolved external symbol __forceAtlDllManifest错误的解决

    作者:朱金灿 来源:http://blog.csdn.net/clever101 晚上编译一个ATL程序,出现一些诡异的错误: 1>CGreet.obj : error LNK2001: unr ...