《類Redis大容量存儲-pika 主從復(fù)制原理之binlog》要點:
本文介紹了類Redis大容量存儲-pika 主從復(fù)制原理之binlog,希望對您有用。如果有疑問,可以聯(lián)系我們。
我們在《大容量類 Redis 存儲 — 有關(guān) pika 的一切》里介紹過pika的誕生、pika的特點、pika的核心以及pika的使用.本文來自pika用戶bigpyer(小米公司),他在文章中非常詳細(xì)的解析了pika同步邏輯中的重要文件:“write2file”的數(shù)據(jù)存儲方式及實現(xiàn)原理,非常值得一看!
pika 是 360 Web 平臺部 DBA 與基礎(chǔ)架構(gòu)組合作開發(fā)的大容量類 Redis 存儲,pika 的出現(xiàn)并不是為了替代 Redis,而是 Redis 的場景補(bǔ)充.pika 力求在完全兼容 Redis 協(xié)議、繼承 Redis 便捷運維設(shè)計的前提下通過持久化存儲的方式解決 Redis 在大容量場景下的問題,如恢復(fù)時間慢、主從同步代價高、單線程相對脆弱、承載數(shù)據(jù)較有限、內(nèi)存成本高昂等.
binlog相關(guān)的文件包含兩部分: manifest和write2file,其中manifest記錄了日志元信息,包括當(dāng)前日志文件編號、當(dāng)前日志文件偏移量,write2file+num記錄了pika接收到的所有redis寫命令、參數(shù).
manifest文件格式:
日志偏移量(8字節(jié))|con_offset(8字節(jié),未使用)|元素個數(shù)(4字節(jié),未使用)|日志文件編號(4字節(jié)).
Binlog文件格式:
Binlog文件固定大小為100MB,每個Binlog文件由多個Block組成,每個Block大小固定為64KB,每一個寫redis命令稱為一個Record.一個Record可以分布在多個Block中,但只會分布在一個Binlog文件里,所以Binlog文件有可能大于100MB.
Record格式:Header|Cmd?
Header: Record Length(3字節(jié))|時間戳(4字節(jié))|記錄類型(1字節(jié)).
Cmd: redis命令的一部分或者全部,取決于當(dāng)前Block剩余空間是否可以存放該Record.
基本類
Version: 元信息類,通過mmap與manifest文件映射.
Binlog: 日志類,通過mmap與write2file文件映射.
PikaBinlogSenderThread: 日志消費類,順序讀取日志文件內(nèi)容,消費日志.
構(gòu)造Binlog
//file_size可以在配置文件指定,默認(rèn)為100MB
Binlog::Binlog(const std::string& binlog_path, const int file_size)
1.1 創(chuàng)建binlog文件目錄.
1.2 檢查log目錄下manifest文件是否存在,不存在則新建.
1.3 根據(jù)manifest文件初始化Version類.
1.4 根據(jù)manifest中的filenum找到對應(yīng)的日志文
件,根據(jù)pro_offset定位到文件append的位置,初始化日志指針、記錄日志內(nèi)容長度、Block塊數(shù)量.
更新當(dāng)前日志生產(chǎn)狀態(tài)
//pro_num: 日志文件編號
//pro_offset: 日志文件偏移量
//用在需要全量同步時更新slave實例對應(yīng)的binlog信息
Status Binlog::SetProducerStatus(uint32_t pro_num, uint64_t pro_offset)
2.1 刪除write2file0.
2.2 刪除write2file+pro_num.
2.3 構(gòu)造新的write2file+pro_num文件,填充pro_offset個空格,初始化version->pro_num為pro_num,version->pro_offset為pro_offset,并刷新到manifest文件中.
2.4 初始化當(dāng)前filesize、block_offset.
更新當(dāng)前日志生產(chǎn)狀態(tài)
//filenum: 當(dāng)前日志編號
//pro_offset: 當(dāng)前日志偏移量
Status Binlog::GetProducerStatus(uint32_t* filenum, uint64_t* pro_offset)
3.1 讀取version中的pro_num、pro_offset并返回.
生產(chǎn)日志
//Put->Produce->EmitPhysicalRecord
Status Binlog::Put(const std::string &item)
4.1檢查當(dāng)前日志文件是否滿足切割條件,如果滿足則進(jìn)行切割.
4.1.1 pro_num自增加1,初始化新的日志文件,version->pro_num=pro_num,version->pro_offset = 0,binlog->filesize = 0,binlog->block_offset = 0.
4.1.2 如果當(dāng)前block剩余大小<kHeaderSize(8字節(jié)),則填充剩余空間為’\x00″.
4.1.3 Produce是一個循環(huán),保證在item大小超過kBlockSize時,可以進(jìn)行多次EmitPhysicalRecord,完成item全部數(shù)據(jù)落入binlog文件,循環(huán)正常退出的條件是left==0.
4.1.3.1 如果left<avail,代表當(dāng)前block可以存放完整的item,則type=kFullType,調(diào)用EmitPhysicalRecord一次,循環(huán)退出.
4.1.3.2 如果left > avail,代表需要多個Block存放item,則第一次Type=kFirstType,調(diào)用EmitPhysicalRecord多次.
4.1.3.3 如果left > avail,且不是第一次EmitPhysicalRecord,則Type=kMiddleType,調(diào)用EmitPhysicalRecord多次.
4.1.4EmitPhysicalRecord.
4.1.4.1 拼接RecordHeader(3字節(jié)長度+4字節(jié)時間+1字節(jié)Type),寫入數(shù)據(jù),更新block_offset、pro_offset.
消費日志
//scratch: 消費結(jié)果返回一個完整的redis cmd
//Consume->ReadPhysicalRecord,ReadPhysicalRecord每次讀取一個完整的Record,多個Record構(gòu)成一個完整的redis cmd
Status PikaBinlogSenderThread::Consume(std::string &scratch)
5.1Consume是一個循環(huán),可能多次調(diào)用ReadPhysicalRecord,循環(huán)退出的條件是讀取到的record_type==kFullType或record_type==kLastType.
5.1.1如果讀取到的kBlockSize-last_record_offset_ <= kHeaderSize代表讀到了Block的末尾,且為填充數(shù)據(jù),skip掉.
5.1.2讀取數(shù)據(jù),更新last_record_offset_,con_offset.
以上就是pika主從復(fù)制媒介write2file的全部解析了,下篇文章將會對pika主從復(fù)制的工作流程進(jìn)行拆解、分析,文章同樣來自bigpyer(小米公司),敬請期待!
文章來自微信公眾號:HULK一線技術(shù)雜談
轉(zhuǎn)載請注明本頁網(wǎng)址:
http://www.fzlkiss.com/jiaocheng/3729.html