日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

关于Titandb Ratelimiter 失效问题的一个bugfix

發(fā)布時間:2023/11/27 生活经验 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 关于Titandb Ratelimiter 失效问题的一个bugfix 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

本文簡單討論一下在TitanDB 中使用Ratelimiter的一個bug,也算是一個重要bug了,相關(guān)fix已經(jīng)提了PR到tikv 社區(qū)了pull-210。

這個問題導(dǎo)致的現(xiàn)象是ratelimiter 在titandb Flush/GC 生成blobfiled的過程中無法生效,也就是無法限制titandb的主要寫 I/O。而我們想要享受Titandb在大value的寫放大紅利,卻會引入巨量的寫帶寬(GC),這個時候?qū)τ诖蠖鄶?shù)讀敏感的場景 titandb GC出現(xiàn)時就是一場長尾災(zāi)難。

而ratelimiter則是這個場景的救星,它能夠有效控制磁盤寫入,降低了大量排隊的寫請求對讀的影響。我們現(xiàn)在使用的非Intel Optane系列的ssd/NVM等設(shè)備,請求在磁盤內(nèi)部其實(shí)都是順序處理,也就是寫請求多了,后續(xù)跟著的讀請求延時必然會上漲。這個時候,我們能夠有效控制磁盤的寫入帶寬,再加上titandb的GC并非持續(xù)性的波峰,而是間斷性得調(diào)度,這樣我們通過ratelimiter做一個均衡的限速,將GC出現(xiàn)時的磁盤I/O波峰打平到一段時間內(nèi)處理完成,這樣我們的讀請求延時就很棒了。

然而,事與愿違,titan的ratelimiter有一些細(xì)節(jié)上的bug。

使用如下測試腳本:

./titandb_bench \--benchmarks="fillrandom,stats" \--max_background_compactions=32 \--max_background_flushes=4 \--max_write_buffer_number=6 \--target_file_size_base=67108864 \--max_bytes_for_level_base=536870912 \--statistics=true \--stats_dump_period_sec=5 \--num=5000000 \--duration=300 \--threads=10 \--value_size=8192 \--key_size=16 \--key_id_range=10000000 \--enable_pipelined_write=false \--db=./db_bench_test \--wal_dir=./db_bench_test \--num_multi_db=1 \--allow_concurrent_memtable_write=true \--disable_wal=true \ # 寫入的過程中磁盤帶寬僅由flush/GC產(chǎn)生--use_titan=true \ # 使用titandb--titan_max_background_gc=2 \--rate_limiter_bytes_per_sec=134217728 \ # 開啟ratelimiter,限速到128M--rate_limiter_auto_tuned=false 

測試1:

測試rocksdb的ratelimiter是否生效 ,將--use_titan=false

能夠看到磁盤寫I/O行為非常穩(wěn)定得被限制到128M左右:

測試2:

測試titan的ratelimiter是否生效,將--use_titan=true直接打開

可以看到此時IO完全無法有效控制住,I/O線程有high和user線程池,也就是titandb的flush和GC

發(fā)現(xiàn)了問題,接下來看看問題原因:

我們上層傳入了ratelimiter,而ratelimiter的調(diào)度則在數(shù)據(jù)寫入具體文件之前調(diào)度的,titan這里復(fù)用了rocksdb的ratelimiter,也就是ratelimiter的調(diào)度最終都會通過同一個入口WritableFileWriter::Append函數(shù)。

Titan這里到達(dá)這個入口的途徑就是在Flush/GC 創(chuàng)建Blobfile的時候進(jìn)入的。

void BlobFileBuilder::Add(const BlobRecord& record, BlobHandle* handle) {if (!ok()) return;encoder_.EncodeRecord(record);handle->offset = file_->GetFileSize();handle->size = encoder_.GetEncodedSize();live_data_size_ += handle->size;// 寫blobfilestatus_ = file_->Append(encoder_.GetHeader());if (ok()) {status_ = file_->Append(encoder_.GetRecord());num_entries_++;// The keys added into blob files are in order.if (smallest_key_.empty()) {smallest_key_.assign(record.key.data(), record.key.size());}assert(cf_options_.comparator->Compare(record.key, Slice(smallest_key_)) >=0);assert(cf_options_.comparator->Compare(record.key, Slice(largest_key_)) >=0);largest_key_.assign(record.key.data(), record.key.size());}
}

我們通過systemtap確認(rèn)一下titandb這里的ratelimiter指針是否為空, 如果為空,那問題就不在writablefile那里了。

!#/bin/stapglobal timesprobe process("/home/test_binary").function("rocksdb::titandb::BlobFileBuilder::Add").call {printf("rate_limiter addr : %x\n ", $file_->ratelimiter_$)
}

發(fā)現(xiàn)有地址,且和LOG文件中打出的rate_limiter地址一樣,說明ratelimiter確實(shí)是下發(fā)到了底層文件寫入這里。

那就繼續(xù)深入唄,看看是否執(zhí)行到了ratelimiter邏輯里面。

這里被titan寫的單測誤導(dǎo)了很久blob_gc_job_test.cc,他們自己實(shí)現(xiàn)了一個ratelimiter的RequestToken,乍一看和rocksdb的RequestToken很像,但少了一個條件,一般人還看不出來:

size_t RequestToken(size_t bytes, size_t alignment,Env::IOPriority io_priority, Statistics* stats,RateLimiter::OpType op_type) override {// 少了一個對io_priority 的判斷if (IsRateLimited(op_type)) {if (op_type == RateLimiter::OpType::kRead) {read = true;} else {write = true;}}return bytes;
}

因?yàn)檫@個單測除了少了一個判斷之外,其他邏輯都沒有問題,結(jié)果老是認(rèn)為問題出在了RequestToken上某一個函數(shù)里,可能是從AppendRequestToken之間的某一個邏輯沒有進(jìn)入到,也就是無法進(jìn)入到實(shí)際的RequestToken里面。

然而抓遍了中間部分函數(shù)的調(diào)用棧,人正常的邏輯,,,沒有絲毫問題,通過Append進(jìn)入之后需要不斷填充一個buffer,當(dāng)這個buffer達(dá)到1M之后(可以通過參數(shù)writable_file_max_buffer_size配置)會調(diào)用一次WritableFileWriter::Flush,沒有direct_io的配置的話這里面必然會進(jìn)入到WriteBufferred函數(shù)中,和rocksdb的邏輯一毛一樣。。。wtf

萬般無奈,只能回到RequestToken邏輯中了,stap打印了一下進(jìn)入函數(shù)之后的各個參數(shù)的值。。。發(fā)現(xiàn)io_priority為啥大多數(shù)是2,偶爾是0/1。。。而rocksdb都是0/1,顯然2肯定是無法進(jìn)入到實(shí)際的Request邏輯的,被屏蔽在了外面。

如下是rocksdb的令牌桶限速入口:

size_t RateLimiter::RequestToken(size_t bytes, size_t alignment,Env::IOPriority io_priority, Statistics* stats,RateLimiter::OpType op_type) {// 必須保證io_priority < 2才能實(shí)際進(jìn)入到 Request邏輯if (io_priority < Env::IO_TOTAL && IsRateLimited(op_type)) {bytes = std::min(bytes, static_cast<size_t>(GetSingleBurstBytes()));if (alignment > 0) {// Here we may actually require more than burst and block// but we can not write less than one page at a time on direct I/O// thus we may want not to use ratelimiterbytes = std::max(alignment, TruncateToPageBoundary(alignment, bytes));}Request(bytes, io_priority, stats, op_type);}return bytes;
}

問題顯然出現(xiàn)在了io_priority這里,然后大概看了一下什么時候會對io_priority進(jìn)行賦值。

它描述的是一個文件被ratelimiter拿到的時候該以什么樣的優(yōu)先級處理,如果設(shè)置的是高優(yōu)先級IO_HIGH,則ratelimiter會優(yōu)先滿足這個文件的寫入,不會限速得太狠;如果是IO_LOW則會盡可能得對它進(jìn)行限速;而IO_HIGH則是文件創(chuàng)建時的默認(rèn)優(yōu)先級, 不會進(jìn)行任何限速。

WritableFile(): last_preallocated_block_(0),preallocation_block_size_(0),io_priority_(Env::IO_TOTAL),write_hint_(Env::WLTH_NOT_SET),strict_bytes_per_sync_(false) {}

所以,rocksdb實(shí)際會在compaction/Flush 創(chuàng)建sst文件的時候?qū)λ麄冞M(jìn)行各自的優(yōu)先級賦值,保證能夠被限速。

邏輯分別在WriteL0Table–>BuildTableOpenCompactionOutputFile–>writable_file->SetIOPriority(Env::IO_LOW)中,然而我們在titan中的主體IO在blobfile的寫入上,也就是創(chuàng)建Blobfile 的handle之后需要對blobfile的io_priority進(jìn)行設(shè)置,才能保證ratelimiter能夠拿到有效的I/O優(yōu)先級。

看一下titan的FileManager::NewFile的邏輯:

  Status NewFile(std::unique_ptr<BlobFileHandle>* handle) override {auto number = db_->blob_file_set_->NewFileNumber();auto name = BlobFileName(db_->dirname_, number);Status s;std::unique_ptr<WritableFileWriter> file;{std::unique_ptr<WritableFile> f;s = db_->env_->NewWritableFile(name, &f, db_->env_options_);if (!s.ok()) return s;file.reset(new WritableFileWriter(std::move(f), name, db_->env_options_));}handle->reset(new FileHandle(number, name, std::move(file)));{MutexLock l(&db_->mutex_);db_->pending_outputs_.insert(number);}return s;}

到這里基本就清楚問題的原因了,顯然titan創(chuàng)建blobfile并沒有添加有效的io_priority,而且ratelimiter的單測寫的不夠嚴(yán)謹(jǐn)導(dǎo)致誤導(dǎo)了很多人。

修復(fù)的話可以之間看這個pull-210 就可以了。

總結(jié)

以上是生活随笔為你收集整理的关于Titandb Ratelimiter 失效问题的一个bugfix的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。