当前位置: 首页 > 科技观察

手把手教你玩转MyRocks-RocksDB——STATISTICSandBackgroundThreads_0

时间:2023-03-19 10:10:23 科技观察

0。Intro在Facebook的MySQL版本(以下简称MyRocks)中,RocksDB是一个可选的存储引擎。与InnoDB引擎相比,RocksDB的一个重要优势是它使用的磁盘空间更少。在生产系统中,尤其是超过1亿用户的互联网应用中,磁盘空间是比较大的成本之一,而能够占用较少磁盘空间的RocksDB无疑具有吸引力。然而,在生产系统中使用新的存储引擎自然有其潜在的风险。除了通过各种外部基准测试工具测试获取各种性能数据外,全方位的内部指标可以帮助我们真正了解数据库内部发生了什么。对于性能调优和开发都具有指导意义。MyRocks通过SHOWENGINEROCKSDBSTATUS和多个INFORMATION_SCHEMA表提供更全面的内部指标。本文将介绍SHOWENGINEROCKSDBSTATUS中STATISTICS统计和后台线程的实现原理。在理解了实现原理的基础上,更容易通过扩展其功能使其更好地为我们服务。调用SHOWENGINEROCKSDBSTATUS命令会返回多行数据,包括:STATISTICS:RocksDB引擎所有线程的所有操作的各种计数/时间的累加,如rocksdb.block.cache.hit和rocksdb.db。写.micros。BG_THREADS:后台线程的状态。DBSTATS:数据库操作的统计信息。CF_COMPACTION:每个Columnfamily的compaction相关的统计。MEMORY_STATS:内存使用情况。调用SHOWENGINEROCKSDBSTATUS将返回几行数据。但是,这些数据并没有事先存储在一个表中,而是通过调用位于rocksdb/ha_rocksdb.cc文件中的rocksdb_show_status函数将内存中对应的值归一化返回给用户。1.STATISTICS根据RocksDB官方文档介绍STATISTICS。启用STATISTICS将增加5%-10%的额外开销。STATISTICS统计值记录了RocksDB引擎所有线程的所有操作的各种计数/时间的累加。RocksDB引擎在Put/Get/Delete等各种操作中设置了很多埋点。以函数GetEntryFromCache为例,它的作用是返回可用的块缓存。特别是可以看到statistics是GetEntryFromCache和block_cache->Lookup的一个参数。没错,它就是靠统计参数到处采集数据的。当有可用的blockcache时,调用RecordTick3次,增加三个统计值的计数;如果没有可用的块缓存,则BLOCK_CACHE_MISS和block_cache_miss_ticker的计数也会增加。缓存::处理*从缓存中获取条目(缓存*块缓存,constSlice&key,Tickersblock_cache_miss_ticker,Tickersblock_cache_hit_ticker,统计*统计){autocache_handle=block_cache->查找(键,统计);if(cache_handle!=nullptr){PERF_COUNTER_ADD(block_cache_hit_count);//overallcachehitRecordTick(statistics,BLOCK_CACHE_HIT);//totalbytesreadfromcacheRecordTick(statistics,BLOCK_CACHE_BYTES_READ,block_cache->GetUsage(cache_handle));//特定于块类型的cachehitRecordTick(statistics,block_cache_hit_ticker);}else{//overallcachemissRecordTick(statistics,BLOCK_CACHE_BYTES_READ,block_cache->GetUsage(cache_handle));//overallcachemissRecordBick(statistics)block-typespecificcachemissRecordTick(statistics,block_cache_miss_ticker);}returncache_handle;}1.1RocksDB的STATISTICS接口使用STATISTICS的方法也很简单。其头文件位于:include/rocksdb/statistics.hmonitoring/statistics.h使用方法:Optionsoptions;options.statistics=rocksdb::CreateDBStatistics();可选统计级别:kExceptDetailedTimers:去除互斥锁等待和压缩计时kExceptTimeForMutex:去除互斥锁等待计时kAll:所有数据统计类型分为两种:ticker:计数,类型为64位无符号整数。用于测量计数器(例如“rocksdb.block.cache.hit”)、累积字节数(例如“rocksdb.bytes.written”)或时间(例如“rocksdb.l0.slowdown.micros”)。直方图:统计数据的统计分布,包括最大值、最小值、均值、中值和标准差。统计函数接口:MeasureTime:函数名有歧义。实际上,该值记录在直方图中。RecordTick:累积行情。获取结果的接口:Statistics::getTickerCount:指定代码类型获取计数。Statistics::histogramData:指定Histograms类型,返回一个HistogramData结构,其成员为统计值,包括最大值、最小值、平均值、中值、标准差。Statistics::getHistogramString:指定直方图类型并返回直方图的可读字符串。Statistics::ToString():返回一个人类可读的字符串,包括所有代码和直方图。1.2RocksDBSTATISTICS的实现RocksDB实现了StatisticsImpl类,继承了Statistics的接口。主接口:getTickerCounthistogramDatagetHistogramStringgetAndResetTickerCountrecordTickmeasureTimeToString成员变量:TickerInfotickers_[INTERNAL_TICKER_ENUM_MAX];HistogramInfohistograms_[INTERNAL_HISTOGRAM_ENUM_MAX];这里的TickerInfo和HistogramInfo是一个类似的数据结构或者是一个非本地线程或者线程本地的统计信息,用于累加计数器或者时间。TickerInfo类型包含两个参数:ThreadLocalPtr类型的thread_value(实型ThreadTickerInfo),包括:整数类型的值,指向merged_sum的指针HistogramInfo类型的merged_sum包含两个参数:ThreadLocalPtr类型的thread_value(实型ThreadHistogramInfo),包含:HistogramImpltypevaluePointertomerged_histPointertomerge_lockHistogramImpltypemerged_histMutextypemerge_lock其实STATISTICS的相关实现还是比较巧妙的,使用STATISTICS只增加5%-10%也是关键。为了避免线程间共享数据导致CPU缓存频繁失效,初始化时merged_sum和merged_hist均为空,当且仅当线程退出时,调用mergeThreadValue函数将线程局部变量累加到TickerInfo和HistogreamInfo中到merged_sum和merged_hist。1.3MyRocks的使用MyRocks使用RocksDB提供的接口进行数据统计。rocksdb_stats变量是在RocksDB引擎启动时通过rocksdb_init_func函数声明和初始化的。rocksdb_stats=rocksdb::CreateDBStatistics();rocksdb_db_options->statistics=rocksdb_stats;MyRocks除了使用所有RocksDB引擎层的统计数据外,还定义了commit_latency_stats=newrocksdb::HistogramImpl();在rocksdb_commit_by_xid和rocksdb_commit方式这两个函数中,统计了每次commit所花费的时间。rocksdb::StopWatchNanotimer(rocksdb::Env::Default(),true);...commit_latency_stats->Add(timer.ElapsedNanos()/1000);在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后台线程通过调用SHOWENGINEROCKSDBSTATUS可以得到与BG_THREADS相关结果,它的输出结果类似于:Type:BG_THREADSName:140173379593984Status:thread_type:LowPri##cf_name:defaultoperation_type:Compactionoperation_stage:CompactionJob::ProcessKeyValueCompactionelapsed_time_ms:6172.244msBaseInputLevel:0BytesRead:992806363BytesWritten:992071408IsDeletion:0IsManual:0IsTrivialMove:0JobID:1936OutputLevel:5TotalInputBytes:1586832446state_type:可以看到很多信息:这个线程在Compaction过程中,在CompactionJob::ProcessKeyValue阶段2.2.2.2.617字节是992806363,写入的字节数是992071408。但是,它不包括Compaction可能感兴趣的源文件和目标文件等信息。文章开头提到,理解实现原理可以让我们更好的进行扩展。2.1线程状态接口和MyRocks中SHOWENGINEROCKSDBSTATUS命令的实现通过RocksDB中线程状态接口展示了BG_THREAD的机制。它的头文件位于:include/rocksdb/env.hinclude/rocksdb/thread_status.hutil/thread_operation.hmonitoring/thread_status_updater.hmonitoring/thread_status_util.h关键类:ThreadStatusUpdater:存储了各个后台线程的状态和指针状态所有后台线程。ThreadStatusUtil:这个类只有静态变量和静态方法。建议通过该类的方法更新ThreadStatusUpdater中的状态。使用方法:将线程统计信息添加到ThreadStatusUpdater中:调用ThreadStatusUtil::RegisterThread从ThreadStatusUpdater中删除线程统计信息:调用ThreadStatusUtil::UnregisterThread其他修改线程状态的函数:参见monitoring/thread_status_util.h调用env的GetThreadList()函数即可获取当前后台线程的状态,状态的状态值存储在一个vector中。显示里面的内容,类似下图:从代码可以看出,实现threadstatus的目的是显示flush和compaction的运行状态。当然,我们也可以将用户线程的状态存储在threadstatus中,通过调用SHOWENGINEROCKSDBSTATUS命令显示出来。特别地,可以看到compaction特别有的状态有价值:enumCompactionPropertyType:int{COMPACTION_JOB_ID=0,compaction_input_output_level,compaction_prop_flags,compaction_total_input_bytes,compaction_bytes_bytes_read,compaction_bytes_written,num_compaction_properties};2.2MyRocks/RocksDB的使用在RocksDB的线程池实现中,每一个启动的后台线程都会通过调用ThreadStatusUtil::RegisterThread加入到观察后台线程集合中。ThreadPoolImpl::Impl::StartBGThreads-->BGThreadWrapper-->ThreadStatusUtil::RegisterThread在rocksdb_show_status函数中输出BG_THREAD的过程如下:通过调用GetThreadList(&thread_list)获取所有后台线程的ThreadStatus集合。通过遍历ThreadStatus集合,依次输出各个后台线程的状态。3.总结本文介绍SHOWENGINEROCKSDBSTATUS命令中STATISTICS和BG_THREAD的相关内容。