您的位置:首页 > 数据库 > Redis

redis设计与实现

2015-08-12 10:06 477 查看

数据结构域对象

数据库键总是字符串,值可以为字符串对象、列表对象(list)、哈希对象(hash)、集合对象(set)、有序集合(sorted set object)这5种中的其中一种

redis使用SDS保存字符串(simple dynamic string),即简单动态字符串抽象类型。

struct SDS

int len;

int free;

char buf[];

与redis中的字符串对比

1、能够在常量时间类获得字符串长度,而C语言中为log(n)。

2、由于记录了free,能够杜绝缓冲区溢出。

3、减少修改字符串时带来的内存重分配次数

二进制安全的字符串,因为可以存储\0

字典的实现

typedef struct dictht

dictEntry** table;//哈希表数组

unsigned long size;//哈希表大小

unsigned long sizemask;//哈希表大小掩码

unsigned long used;//哈希表已有节点数

typedef struct dictEntry

void * key;

union {

void * val;

uint64_t u64;

int64_t s64;

struct dictEntry * next;

}dictEntry

渐进式hash

dict中保存两个hash表,并且还有一个是否hash的标识位。在渐进式hash中,新添加的字典键值 一律保存在ht[1]中,而ht[0]中不再进行任何添加操作。

typedef struct dict{

dictType * type;

void* privatedata;

dictht ht[2];

int rehashidx;

复杂度在log(n)与n之间,还可以批量处理节点。

跳表的效率可以与平衡树相媲美,并且实现比平衡树简单。

redis使用跳跃表作为有序集合键的底层实现之一: 当有序集合键包含的元素较多时,又或者有序集合键中是比较长的字符串时,redis就可以使用跳跃表作为有序集合键的底层实现。

zskiplist节点有以下属性:

1、层,每个层带有两个属性,前进指针和跨度。

2、后退节点,后退指针在表尾向表头遍历时使用。

3、score值

4、obj对象

每个跳跃表节点的层高都是1-32之间的随机数。

升级 的好处

可以提升灵活性,与节约内存

字符串对象编码可以使int、raw、或者embstr

列表对象编码可以是ziplist或者linkedlist

hash对象可以是ziplist或者hashtable

集合对象可以是intset或者hashtable

有序集合的编码可以是ziplist和skiplist

有序集合同时使用字典和跳表,字典能够在o(1)复杂度内找到元素,但是无序的,而跳表能够是有序的,但需要log(n)复杂度查找元素。

当键的长度或者值的长度过长时,会引起编码的变化。例如由ziplist编程hashtable

redis会初始化分配0-9999个数字,用于共享,使用引用计数,节省内存。但redis只对包含整数字符串的对象进行共享。因为检测字符串对象的复杂度过高。

对象空转时间

由当前时间与最后一次访问时间相减得到。可以用于数据过多时,删除空转时间过长的对象。

RDB持久化

save命令由服务端执行,将会阻塞服务器

bgsave命令由子进程执行保存操作,该命名不会阻塞服务器。

RDB是一个二进制文件,由多个部分组成。

主从同步,通过load RDB文件,然后重放写命令实现主从同步,同步完成后,主服务器再进行命令广播,主服务器上的写命令都将发送给从服务器。

从redis2.8开始,Redis使用psync代替sync命令,最大的改进是支持部分重同步,Psync可以让主服务器只向从服务器同步断线后的缺失数据,而不用向从服务器同步整个数据库。

复制导致的数据一致性的问题: redis只保证最终一致性,如果程序不能容忍过期数据,就应该读取主服务器,

AOF持久化

AOF持久化可以实现命令追加,文件写入和文件同步三个步骤。

当AOF持久化打开时,服务端执行一个命令完成后,会以协议格式追加到服务器状态的aof_buf缓冲区末尾。

AOF持久化三个选项: always、every second、no。 写入AOF文件(缓冲区文件),第一种每次都同步到磁盘、第二种每隔一秒中同步到磁盘,第三种何时同步到磁盘又操作系统决定。

redis文件事件都是包装常见的select、epoll、kqueue实现的。

Reactor模式:事件句柄注册自己感兴趣的io事件。多路事件分发器等待io事件的到来。 io事件到来唤醒多路事件分发器,事件分发器根据io事件调用应用事件句柄。事件句柄进行实际的操作,并将控制权返回给多路分发器。

redis集群

可以通过发送cluster meet 指令,让一个节点与其他节点进行握手,从而构建集群。

频道的订阅与退订

通过链表实现

multi命令的执行标识着事务的开始。如果客户端发送的命令是EXEC、DISCARD、WATCH、MUILI四个命令其中的一种,那么服务器端立即执行这个命令。如果不是,则将命令放入一个事务队列中,然后向客户端返回QUEUED回复。

事务状态包括一个事务队列,以及一个已入队命令计数器。

当处于一个事务状态的客户端发送EXEC命令时,EXEC命令会立即执行,服务端会立即遍历这个客户端的事务队列,执行队列中的所有命令,最后将执行结果返回给客户端。

事务提供一种将多个命令打包,并一次性,有序执行的机制。

redis的事务总是保证ACID中的原子性、一致性和隔离性。当服务器运行在AOF持久化模式下时,并且appendfsync选项也为always时,事务具有耐久性。

sentinel

sentinel是一个监视器,它根据被监视实例的身份和状态判断应该采取何种动作。

sentinel根据用户给定的配置文件发现主服务器。

sentinel通过向主服务器发送Info命令来自动获取所有从服务器的地址。

sentinel通过向主从服务器发送hello信息,向其他sentinel宣告自己的存在,与此同时,sentinel通过订阅连接其他sentinel的hello信息,发现其他监视器的其他sentinel。

sentinel使用ping来检测实例的状态,如果实例在指定的时间内没有返回回复,或者返回错误的回复,那么该实例被判断为下线。

在下线主服务器的所有从服务器中,找到一个未下线的从服务器,并且数据状态最接近从服务器的作为主服务器,并且让其他从服务器复制新的主服务器。

集群的下线检测和故障转移是集成在节点里面的,而sentinel是一个独立的监控程序,他们的运行模式非常的不同,所以集群的实现并没有复用Sentinel的代码。

集群里的每个节点会互相通知其他节点自己正在处理哪些槽,并且维持一个槽表。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: