阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

大容量类 Redis 存储的场景补充-pika

53次阅读
没有评论

共计 2829 个字符,预计需要花费 8 分钟才能阅读完成。

导读 我们在《大容量类 Redis 存储 — 有关 pika 的一切》里介绍过 pika 的诞生、pika 的特点、pika 的核心以及 pika 的使用。文章中非常详细的解析了 pika 同步逻辑中的重要文件:“write2file”的数据存储方式及实现原理,非常值得一看!
pika

pika 是 360 Web 平台部 DBA 与基础架构组合作开发的大容量类 Redis 存储,pika 的出现并不是为了替代 Redis,而是 Redis 的场景补充。pika 力求在完全兼容 Redis 协议、继承 Redis 便捷运维设计的前提下通过持久化存储的方式解决 Redis 在大容量场景下的问题,如恢复时间慢、主从同步代价高、单线程相对脆弱、承载数据较有限、内存成本高昂等。

pika 主从复制原理之 binlog

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。

阿里云 2 核 2G 服务器 3M 带宽 61 元 1 年,有高配

腾讯云新客低至 82 元 / 年,老客户 99 元 / 年

代金券:在阿里云专用满减优惠券

正文完
星哥说事-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2024-07-24发表,共计2829字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中