首页 > 编程知识 正文

哔咔破解版(哔咔acg版官网ios隐藏入口)

时间:2023-05-06 17:25:04 阅读:65929 作者:3506

Pika的设计与实现Pika

pika是360奇虎公司的开源redis存储系统,主要解决用户使用redis的内存大小超过50G、80G等问题。 存在启动恢复时间长、主要成本高、硬件成本高、缓冲区容易填满等问题。

Pika是这些场景的解决方案。

Pika单线程的性能一定不如Redis。 由于Pika是多线程结构,在线程数较多的情况下,一些数据结构的性能可以优于Redis; Pika肯定不是完全优于Redis的计划,但在某些场景中更适合。 DBA可以根据业务场景选择合适的方案。

Pika体系结构下图是Pika的实现框架。

主要组成

网络模块pink、网络编程封装、用户实现一个高性能服务器只需要实现对应的DealMessage函数,支持单线程模型、多线程工作器模型线程模块,见下文; 根据对存储引擎nemo、Rocksdb的修改,封装Hash、List、Set、Zset等数据结构; 日志模块binlog,解决同步缓冲区太小的问题;

线程模块

Pika基于pink封装线程,使用多个工作线程进行读写操作,并通过底层nemo引擎确保线程安全。 线程分为11种。

pika server:client worker,监听两个主线程分布式端口并接受用户的连接请求;每个线程有多个用户客户端的连接;处理用户命令和生成的结果ReplicaSender :存在多个,尝试与添加到binlog的trysync:Master进行首次连接,并在以后发生故障后重新连接,每个线程根据与slave节点的同步偏移,从binlog指定的偏移开始存在一个(动态创建销毁,一个slave节点同时只能有一个master ),用户将指定的偏移或当前偏移发送到master节点,并接收master实时发送的同步命令本地使用与主节点完全匹配的偏移,binlogSlavePing:slave用于向主节点发送心跳的生存检测HeartBeat:master用于接收和恢复来自所有slave的心跳的生存检测

nemostorageenginenemo本质上是rocksdb的改造和封装,支持多种数据结构的存储。 rocksdb数据库仅支持kv存储。 nemo通常支持五种类型的数据结构的存储: KV密钥-值对、散列结构、列表结构、Set结构和ZSet结构。 由于rocksdb的存储方式只有kv的一种结构,上述五种数据结构的存储方式最终落入rocksdb的kv存储方式。

1、KV键值对

KV存储不添加额外的元信息,只是在value的末尾添加8字节的附加信息(前4字节表示version,最后4字节表示ttl )作为最后的故障KV的值部分。 具体如下图所示。

version字段用于标记键-值对,以便以后处理。 例如,如果删除键值对,则可以标记该版本,然后实际删除,从而减少删除操作阻止服务的时间。

2、混叠结构

每个散列存储器都包含散列密钥[key]、散列密钥下的域名field以及保存的值value。 nemo的保存方法是将key和field组合为新key,并将此新生成的key和要保存的value组合为最终崩溃的kv键值对。 另外,对于每个hash密钥,nemo添加了包含元信息的放弃kv。 它保存了相应hash键下的所有字段值对的数量。 具体的实现方法如下。

每个hash键的元信息的落盘kv的存储格式:

上一条显示了用于存储每个散列密钥的kv密钥和值对的密钥部分,由两个字段组成。

第一个字段是“h”字符,表示保存时hash密钥的元信息。 第二个字段是对应的hash键字符串的内容。 后面的栏表示相应散列密钥中的字段值对(字段值对)的数量,大小为8字节(类型为int64_t )。

每个hash键、field、value到落盘kv的映射转换:

上一条对应于放置kv键和值对的键部分。

第一个字段是字符“h”,表示hash结构的密钥; 第二个字段是hash密钥的字符串长度,以1字节(uint8_t类型)表示。 第三个字段是hash键的内容。 因为第二个字段是1字节,

所以这里限定hash键的最大字符串长度是254个字节;第四个字段是field的内容。

后面的横条代表的是落盘kv键值对的值部分,和KV结构存储一样,它是存入的value值加上8个字节的version字段和8个字节的ttl字段得到的。 

 

 

 

3、List结构

每个List结构的底层存储也是采用链表结构来完成的。对于每个List键,它的每个元素都落盘为一个kv键值对,作为一个链表的一个节点,称为元素节点。和hash一样,每个List键也拥有自己的元信息。

每个元信息的落盘kv的存储格式 

前面横条表示存储元信息的落盘kv的键部分,和前面的hash结构是类似的;

后面的横条表示存储List键的元信息,它有四个字段,从前到后分别为该List键内的元素个数、最左边的元素节点的sequence(相当于链表头)、最右边的元素节点的sequence(相当于链表尾)、下一个要插入元素节点所应该使用的sequence。

 

 

每个元素节点对应的落盘kv存储格式

前面横条代表的是最终落盘kv结构的键部分,总共4个字段,前面三个字符段分别为一个字符’l’(表明是List结构的结存),List键的字符串长度(1个字节)、List键的字符串内容(最多254个字节),第四个字段是该元素节点所对应的索引值,用8个字节表示(int64_t类型),对于每个元素节点,这个索引(sequence)都是唯一的,是其他元素节点访问该元素节点的唯一媒介;往一个空的List键内添加一个元素节点时,该添加的元素节点的sequence为1,下次一次添加的元素节点的sequence为2,依次顺序递增,即使中间有元素被删除了,被删除的元素的sequence也不会被之后新插入的元素节点使用,这就保证了每个元素节点的sequence都是唯一的。

后面的横条代表的是具体落盘kv结构的值,它有5个字段,后面的三个字段分别为存入的value值、version、ttl,这和前面的hash结构存储是类似的;前两个字段分别表示的是前一个元素节点的sequence、和后一个元素节点的sequence、通过这两个sequence,就可以知道前一个元素节点和后一个元素节点的罗盘kv的键内容,从而实现了一个双向链表的结构。

 

 

4、Set结构

每个Set键的元信息对应的落盘kv存储格式

 

每个元素节点对应的落盘kv存储格式 

值的部分只有version和ttl,没有value字段。

 

5、ZSet结构

ZSet存储结构是一个有序Set,所以对于每个元素,增加了一个落盘kv,在这个增加的罗盘 kv的键部分,把该元素对应的score值整合进去,这样便于依据Score值进行排序(因为从rocksdb内拿出的数据时按键排序的),下面是落盘kv的存储形式。

 

存储元信息的落盘kv的存储格式 

 

 

score值在value部分的落盘kv存储格式 

 

score值在key部分的落盘kv存储格式 

 

score是从double类型转变过来的int64_t类型,这样做是为了可以让原来的浮点型的score直接参与到字符串的排序当中(浮点型的存储格式与字符串的比较方式不兼容)。

 

 

 

 

 

日志模块

Pika 的主从同步是使用 Binlog 来完成的:master 执行完一条写命令就将命令追加到 Binlog 中,ReplicaSender 将这条命令从 Binlog 中读出来发送给 slave,slave 的 ReplicaReceiver 收到该命令,执行,并追加到自己的 Binlog 中。

binlog 本质是顺序写文件,通过 Index + offset 进行同步点检查,支持全同步 + 增量同步;

当发生主从切换以后,slave 仅需要将自己当前的 Binlog Index + offset 发送给 master,master 找到后从该偏移量开始同步后续命令。

为了防止读文件中写错一个字节则导致整个文件不可用,所以Pika采用了类似 leveldb log 的格式来进行存储,具体如下:

 

 

 

 

主从同步

先说下slave的连接状态:

No Connect,不尝试成为任何其他节点的slave;Connect,Slaveof后尝试成为某个节点的slave,发送trysnc命令和同步点;Connecting,收到master回复可以slaveof,尝试跟master建立心跳;Connected, 心跳建立成功;WaitSync,不断检测是否DBSync完成,完成后更新DB并发起新的slaveof;

 

全同步

Pika 支持 master/slave 的复制方式,通过 slave 端的 slaveof 命令激发:

salve 端处理 slaveof 命令,将当前状态变为 slave,改变连接状态;slave的trysync线程向 master 发起 trysync,同时将要同步点传给 master;master处理trysync命令,发起对slave的同步过程,从同步点开始顺序发送 binlog 或进行全同步;

pika同步依赖于binlog,binlog 文件会自动或手动删除,当同步点对应的 binlog 文件不存在时,需要通过全同步进行数据同步。

需要进行全同步时,master 会将 db 文件 dump 后发送给 slave(通过 rsync 的 deamon 模式实现 db 文件的传输),实现逻辑:

slave 向 master 发送trysnc命令(此时需要开启rsync后台服务);master 发现需要全同步时,判断是否有备份文件可用,如果没有先 dump 一份;master 通过 rsync 向 slave 发送 dump 出的文件;slave 用收到的文件替换自己的 db;slave 用最新的偏移量再次发起 trysnc;完成同步;

 

 Slave 的流程:

 

 Master 的流程: 

 

 

增量同步

一主多从的结构master节点也可以给多个slave复用一个Binlog,只不过不同的slave在binglog中有自己的偏移量而已,master执行完一条写命令就将命令追加到Binlog中,ReplicaSender将这条命令从Binlog中读出来发送给slave,slave的ReplicaReceiver收到该命令,执行,并追加到自己的Binlog中。

主要模块:

WorkerThread:接受和处理用户的命令;BinlogSenderThread:负责顺序地向对应的从节点发送在需要同步的命令;BinlogReceiverModule: 负责接受主节点发送过来的同步命令Binglog:用于顺序的记录需要同步的命令

 

主要的工作过程:

当WorkerThread接收到客户端的命令,按照执行顺序,添加到Binlog里;BinglogSenderThread判断它所负责的从节点在主节点的Binlog里是否有需要同步的命令,若有则发送给从节点;BinglogReceiverModule模块则做以下三件事情: 接收主节点的BinlogSenderThread发送过来的同步命令;把接收到的命令应用到本地的数据上;把接收到的命令添加到本地Binlog里 至此,一条命令从主节点到从节点的同步过程完成;      

 

下图是BinLogReceiverModule(在源代码中没有这个对象,这里是为了说明方便,抽象出来的)的组成,从图中可以看出BinlogReceiverModule由一个BinlogReceiverThread和多个BinlogBGWorker组成。

BinlogReceiverThread: 负责接受由主节点传送过来的命令,并分发给各个BinlogBGWorker,若当前的节点是只读状态(不能接受客户端的同步命令),则在这个阶段写BinlogBinlogBGWorker:负责执行同步命令;若该节点不是只读状态(还能接受客户端的同步命令),则在这个阶段写Binlog(在命令执行之前写)

BinlogReceiverThread接收到一个同步命令后,它会给这个命令赋予一个唯一的序列号(这个序列号是递增的),并把它分发给一个BinlogBGWorker;而各个BinlogBGWorker则会根据各个命令的所对应的序列号的顺序来执行各个命令,这样也就保证了命令执行的顺序和主节点执行的顺序一致了 之所以这么设计主要原因是:

配备多个BinlogBGWorker是可以提高主从同步的效率,减少主从同步的滞后延迟;让BinlogBGWorker在执行执行之前写Binlog可以提高命令执行的并行度;在当前节点是非只读状态,让BinglogReceiverThread来写Binlog,是为了让Binglog里保存的命令顺序和命令的执行顺序保持一致.

上图是一个主从同步的一个过程(即根据主节点数据库的操作日志,将主节点数据库的改变过程顺序的映射到从节点的数据库上),从图中可以看出,每一个从节点在主节点下都有一个唯一对应的BinlogSenderThread。 (为了说明方便,我们定一个“同步命令”的概念,即会改变数据库的命令,如set,hset,lpush等,而get,hget,lindex则不是)

 

 

 

 

快照式备份

不同于Redis,Pika的数据主要存储在磁盘中,这就使得其在做数据备份时有天然的优势,可以直接通过文件拷贝实现 实现

快照内容: 当前db的所有文件名manifest文件大小sequence_number同步点 binlog filenumoffset

 

流程 打快照:阻写,并在这个过程中或的快照内容异步线程拷贝文件:通过修改Rocksdb提供的BackupEngine拷贝快照中文件,这个过程中会阻止文件的删除

 

 

 

 

锁的应用

应用挂起指令,在挂起指令的执行中,会添加写锁,以确保,此时没有其他指令执行。其他的普通指令在会添加读锁,可以并行访问。其中挂起指令有:

trysyncbgsaveflushallreadonly

在pika系统中,对于数据库的操作都需要添加行锁,主要在应用于两个地方,在系统上层指令过程中和在数据引擎层面。在pika系统中,对于写指令(会改变数据状态,如SET,HSET)需要除了更新数据库状态,还涉及到pika的增量同步,需要在binlog中添加所执行的写指令,用于保证master和slave的数据库状态一致。故一条写指令的执行,主要有两个部分:

更改数据库状态将指令添加到binlog中

其加锁情况,如下图: 

在图中可以看到,对同一个key,加了两次行锁,在实际应用中,pika上所加的锁就已经能够保证数据访问的正确性。如果只是为了pika所需要的业务,nemo层面使用行锁是多余的,但是nemo的设计初衷就是通过对rocksdb的改造和封装提供一套完整的类redis数据访问的解决方案,而不仅仅是为pika提供数据库引擎。这种设计思路也是秉承了Unix中的设计原则:Write programs that do one thing and do it well。

这样设计大大降低了pika与nemo之间的耦合,也使得nemo可以被单独拿出来测试和使用,在pika中的数据迁移工具就是完全使用nemo来完成,不必依赖任何pika相关的东西。另外对于nemo感兴趣或者有需求的团队也可以直接将nemo作为数据库引擎而不需要修改任何代码就能使用完整的数据访问功能。

 

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。