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