数据结构域对象
数据库键总是字符串,值可以为字符串对象、列表对象(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的代码。
集群里的每个节点会互相通知其他节点自己正在处理哪些槽,并且维持一个槽表。