typedef char *sds;
/* Note: sdshdr5 is never used, we just access the flags byte directly.
* However is here to document the layout of type 5 SDS strings. */
struct __attribute__ ((__packed__)) sdshdr5 {
unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {
uint8_t len; /* used */
uint8_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr16 {
uint16_t len; /* used */
uint16_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {
uint32_t len; /* used */
uint32_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {
uint64_t len; /* used */
uint64_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
#define SDS_TYPE_5 0
#define SDS_TYPE_8 1
#define SDS_TYPE_16 2
#define SDS_TYPE_32 3
#define SDS_TYPE_64 4
sdshdr5
只负责存储小于32字节的字符串。sdshdr5
的类型和长度存储于 flags
中,低3位存储类型,高5位存储长度。sdshdr16结构
typedef struct zskiplistNode {
sds ele; // 存储字符串类型的数据
double score; // 存储排序的分值
struct zskiplistNode *backward; // 后退指针,指向当前节点最底层的前一个节点,头节点和第一个节点的backward指向NULL,从后向前遍历跳跃表时使用。
struct zskiplistLevel {
struct zskiplistNode *forward; // 指向本层的下一个节点,尾节点的forward指向NULL
unsigned long span; // forward节点指向的节点和本节点之间的元素个数。span越大,跳过的节点个数越多。
} level[]; // 每个节点的数组长度不一样,在生成跳跃表时,随机生成一个1-64的值,值越大出现的概率越低。
} zskiplistNode;
typedef struct zskiplist {
struct zskiplistNode *header, *tail;// 表头、表尾节点。头结点是一个特殊的节点,它的level数组元素个数是64。头结点不存储数据,不计入跳跃表长度
unsigned long length; // 跳跃表长度,表示出头节点之外的节点总数
int level; // 跳跃表的高度
} zskiplist;
zslRandomLevel
函数随机生成一个1-64的值,作为新建节点的高度,值越大出现的概率越低。节点层高确定之后不会再修改。跳跃表示例
压缩列表结构示意图
/*
* The following is a ziplist containing the two elements representing
* the strings "2" and "5". It is composed of 15 bytes, that we visually
* split into sections:
*
* [0f 00 00 00] [0c 00 00 00] [02 00] [00 f3] [02 f6] [ff]
* | | | | | |
* zlbytes zltail entries "2" "5" end
*
* The first 4 bytes represent the number 15, that is the number of bytes
* the whole ziplist is composed of. The second 4 bytes are the offset
* at which the last ziplist entry is found, that is 12, in fact the
* last entry, that is "5", is at offset 12 inside the ziplist.
* The next 16 bit integer represents the number of elements inside the
* ziplist, its value is 2 since there are just two elements inside.
* Finally "00 f3" is the first entry representing the number 2. It is
* composed of the previous entry length, which is zero because this is
* our first entry, and the byte F3 which corresponds to the encoding
* |1111xxxx| with xxxx between 0001 and 1101. We need to remove the "F"
* higher order bits 1111, and subtract 1 from the "3", so the entry value
* is "2". The next entry has a prevlen of 02, since the first entry is
* composed of exactly two bytes. The entry itself, F6, is encoded exactly
* like the first entry, and 6-1 = 5, so the value of the entry is 5.
* Finally the special entry FF signals the end of the ziplist.
*
* Adding another element to the above string with the value "Hello World"
* allows us to show how the ziplist encodes small strings. We'll just show
* the hex dump of the entry itself. Imagine the bytes as following the
* entry that stores "5" in the ziplist above:
*
* [02] [0b] [48 65 6c 6c 6f 20 57 6f 72 6c 64]
*
* The first byte, 02, is the length of the previous entry. The next
* byte represents the encoding in the pattern |00pppppp| that means
* that the entry is a string of length <pppppp>, so 0B means that
* an 11 bytes string follows. From the third byte (48) to the last (64)
* there are just the ASCII characters for "Hello World".
*/
压缩列表元素结构示意图
压缩列表元素编码
压缩列表连锁更新示意图
字典结构示意图
typedef struct dictEntry {
void *key; // 存储键
union {
void *val; // db.dict中的val
uint64_t u64;
int64_t s64; // db.expires中存储过期时间
double d;
} v;
struct dictEntry *next; // 当hash冲突时,指向冲突的元素,形成单链表
} dictEntry;
/* This is our hash table structure. Every dictionary has two of this as we
* implement incremental rehashing, for the old to the new table. */
typedef struct dictht {
dictEntry **table;
unsigned long size;
unsigned long sizemask;
unsigned long used;
} dictht;
typedef struct dict {
dictType *type; // 该字典对应的特定操作函数
void *privdata; // 该字典依赖的数据
dictht ht[2]; // hash表,键值对存储于此
long rehashidx; // rehash标识。默认值为-1,表示没有进行rehash操作。不为-1时,标识正在进行rehash操作,存储的值表示hash表ht[0]的rehash操作进行到了哪个索引值
unsigned long iterators; // 记录当前运行的安全迭代器数。当有安全迭代器绑定到该字典时,会暂定rehash操作。
} dict;
rehash除了扩容时会触发,缩容时也会触发。Redis整个rehash的实现,主要分为如下几步完成。
ht[1]
申请足够的空间;扩容时空间大小为当前容量*2,即 d->ht[0].used * 2
;当使用量不到总空间10%时,则进行缩容。缩容时空间大小则为能恰好包含 d->ht[0].used
个节点的2^N次方幂整数,并把字典中字段 rehashidx
标识为0。dictRehash
函数,重新计算 ht[0]
中每个键的Hash值与索引值(重新计算就叫rehash),依次添加到新的Hash表 ht[1]
,并把老Hash表中该键值对删除。把字典中字段 rehashidx
字段修改为Hash表 ht[0]
中正在进行rehash操作节点的索引值。ht[0]
,然后对调一下 ht[1]
与 ht[0]
的值,并把字典中 rehashidx
字段标识为-1。set-max-intset-entries
)时,用intset保存。并且元素从小到大保存。 typedef struct intset {
uint32_t encoding; // 编码类型
uint32_t length; // 元素个数
int8_t contents[];
} intset;
#define INTSET_ENC_INT16 (sizeof(int16_t))
#define INTSET_ENC_INT32 (sizeof(int32_t))
#define INTSET_ENC_INT64 (sizeof(int64_t))
quicklist
由 List
和 ziplist
结合而成。quicklist结构示意图
/* quicklistNode is a 32 byte struct describing a ziplist for a quicklist.
* We use bit fields keep the quicklistNode at 32 bytes.
* count: 16 bits, max 65536 (max zl bytes is 65k, so max count actually < 32k).
* encoding: 2 bits, RAW=1, LZF=2.
* container: 2 bits, NONE=1, ZIPLIST=2.
* recompress: 1 bit, bool, true if node is temporarry decompressed for usage.
* attempted_compress: 1 bit, boolean, used for verifying during testing.
* extra: 10 bits, free for future use; pads out the remainder of 32 bits */
typedef struct quicklistNode {
struct quicklistNode *prev;
struct quicklistNode *next;
unsigned char *zl;
unsigned int sz; /* ziplist size in bytes */
unsigned int count : 16; /* count of items in ziplist */
unsigned int encoding : 2; /* RAW==1 or LZF==2 */
/* 2表示使用LZF进行压缩,此时zl指向的结构为quicklistLZF */
unsigned int container : 2; /* NONE==1 or ZIPLIST==2 */
unsigned int recompress : 1; /* was this node previous compressed? */
/* 1表示该节点之前被压缩过,使用时需要解压使用,用完再压缩 */
unsigned int attempted_compress : 1; /* node can't compress; too small */
unsigned int extra : 10; /* more bits to steal for future usage */
} quicklistNode;
/* quicklistLZF is a 4+N byte struct holding 'sz' followed by 'compressed'.
* 'sz' is byte length of 'compressed' field.
* 'compressed' is LZF data with total (compressed) length 'sz'
* NOTE: uncompressed length is stored in quicklistNode->sz.
* When quicklistNode->zl is compressed, node->zl points to a quicklistLZF */
typedef struct quicklistLZF {
unsigned int sz; /* LZF size in bytes*/
char compressed[];
} quicklistLZF;
/* quicklist is a 40 byte struct (on 64-bit systems) describing a quicklist.
* 'count' is the number of total entries.
* 'len' is the number of quicklist nodes.
* 'compress' is: -1 if compression disabled, otherwise it's the number
* of quicklistNodes to leave uncompressed at ends of quicklist.
* 'fill' is the user-requested (or default) fill factor. */
typedef struct quicklist {
quicklistNode *head;
quicklistNode *tail;
unsigned long count; /* total count of all entries in all ziplists */
/* 所有 ziplist 中 entry 的总数 */
unsigned long len; /* number of quicklistNodes */
int fill : 16; /* fill factor for individual nodes */
/* 指明每个 quicklistNode 中 ziplist 长度。*/
/* 正数时表示每个 ziplist 最多包含的 entry 个数。 */
/* 负数时表示 ziplist 节点占用内存大小,-1~-5分别表示4/8/16/32/64KB */
unsigned int compress : 16; /* depth of end nodes not to compress;0=off */
} quicklist;
hash-max-ziplist-value
(默认值64),该值可以通过配置文件配置。hash-max-ziplist-entries
(默认值512),该值也可以通过配置文件配置。dict
和 intset
存储zset-max-ziplist-entries 128
:zset采用压缩列表时,元素个数的最大值。默认128。zset-max-ziplist-value 64
:zset采用压缩列表时,每个元素的字符串长度的最大值。默认64。zset-max-ziplist-entries
zset-max-ziplist-value
save m n
,指定当m秒内发生n次变化时,会触发bgsave。save m n
的原理如下:每隔100ms,执行serverCron函数;在serverCron函数中,遍历save m n配置的保存条件,只要有一个条件满足,就进行bgsave。对于每一个save m n条件,只有下面两条同时满足时才算满足:
save m n
以外,还有一些其他情况会触发bgsave:
appendonly yes
appendfsync
控制,各个值的含义如下:
appendfsync everysec
,即每秒执行一次fsync操作。auto-aof-rewrite-percentage 100
,auto-aof-rewrite-min-size 64mb
,当AOF文件大于64MB时,并且AOF文件当前大小比基准大小增长了100%时会触发一次AOF重写。起始的基准大小为Redis重启并加载完AOF文件之后,aof_buf的大小。当执行完一次AOF重写之后,基准大小相应更新为重写之后AOF文件的大小。bgrewriteaof
命令。首先从数据库中读取键现在的值,然后用一条命令去记录键值对,代替之前记录这个键值对的多条命令,这就是AOF重写功能的实现原理。
aof_rewrite_buf_blocks
中,这个缓冲区在服务器创建子进程之后开始使用,当Redis服务器执行完一个写命令之后,它会同时将这个写命令发送给AOF缓冲区和AOF重写缓冲区。主从复制初始化流程图
slaveof no one
断开复制,不会删除已有的数据。requirepass
来设置密码,从节点配置 masterauth
参数 (与主节点 requirepass
保持一致),保证安全。检测主观下线:在默认情况下,Sentinel会以每秒一次的频率向所有与它创建了命令连接的实例(包括主服务器、从服务器、其他Sentinel在内)发送PING命令,并通过实例返回的PING命令回复来判断实例是否在线。Sentinel配置文件中的 down-after-milliseconds
选项指定了Sentinel判断实例进入主观下线所需的时间长度:如果一个实例在down-after-milliseconds毫秒内,连续向Sentinel返回无效回复,那么Sentinel会修改这个实例所对应的实例结构,在结构的flags属性中打开SRI_S_DOWN标识,以此来表示这个实例已经进入主观下线状态。
检查客观下线:当Sentinel将一个主服务器判断为主观下线之后,为了确认这个主服务器是否真的下线了,它会向同样监视这一主服务器的其他Sentinel进行询问,看它们是否也认为主服务器已经进入了下线状态(可以是主观下线或者客观下线)。当Sentinel从其他Sentinel那里接收到足够数量的已下线判断之后,Sentinel就会将从服务器判定为客观下线,并对主服务器执行故障转移操作。当认为主服务器已经进入下线状态的Sentinel的数量,超过Sentinel配置中设置的quorum参数的值,那么该Sentinel就会认为主服务器已经进入客观下线状态。
选举领头Sentinel:当一个主服务器被判断为客观下线时,监视这个下线主服务器的各个Sentinel会进行协商,选举出一个领头Sentinel,并由领头Sentinel对下线主服务器执行故障转移操作。
哨兵使用以下规则来选择新的主服务器:
slave-priority
为0,则不能被选中(slave-priority
可以在配置文件中指定。正整数,值越小优先级越高,当指定为0时,不能被选为主服务器)down-after-milliseconds*10 ms
的从服务器。slave-priority
高的,其次复制偏移量(replication offset)最大,再次带有最小运行 ID 的从服务器cluster meet <ip> <port>
构建集群。cluster-enable
配置决定是否开启集群模式。cluster addslots
将一个或多个槽指派给当前节点负责。节点会将自己负责的槽告知给集群中其它节点。16384个槽都进行了指派,集群才会进入上线状态。MOVED <slot> <ip>:<port>
,客户端收到后会向返回的ip和port请求。计算槽的公式为:CRC16(key) & 16383
。通过 cluster keyslot <key>
可以查看key属于哪个slot。cluster failover
命令之后,执行手动切换。CLUSTER SETSLOT slot MIGRATING node
,将slot从A节点迁移到指定的node节点。注意,slot必须属于A节点,否则会报错。CLUSTER SETSLOT slot IMPORTING node
:将slot从指定节点迁移到A节点。Redis的内存淘汰策略是指在Redis的用于缓存的内存不足时,怎么处理需要新写入且需要申请额外空间的数据。
volatile-lru
:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰volatile-lfu
:挑选使用频率最低的数据淘汰volatile-random
:随机淘汰volatile-ttl
:挑选将要过期的数据淘汰allkeys-lru
:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰allkeys-lfu
:挑选使用频率最低的数据淘汰allkeys-random
:随机淘汰订阅:subscribe channel_name
发布:publish channel_name message
setnx (set if not exists)
,setnx加expire可能会死锁set keyname val ex 5 nx
,当keyname不存在时,设置key,过期时间为5秒此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。