Redis存储——这些数据结构原理你都懂吗?

Redis深度解析:键值存储原理与实战技巧 本文深入探讨了Redis的基础知识,重点介绍了键值存储原理及其在实际应用场景中的应用。我们从Redis的数据结构、操作命令、高级特性以及性能优化等方面进行了详细的讲解。通过生活中的实例和实用的技巧,让你轻松掌握Redis的使用。最后,我们还介绍了Redis的高级应用,包括缓存策略、消息队列、实时排行榜和分布式锁等。希望本文能帮助你更好地理解和运用Redis。 阅读详情

一、Redis内存数据结构与编码

想要弄清楚Redis内部如何支持5种数据类型,也就是要弄清Redis到底是使用什么样的数据结构来存储、查找我们设置在内存中的数据。

虽然我们使用5种数据类型来缓存数据,但是Redis会根据我们存储数据的不同而选用不同的数据结构和编码。

这些数据类型在内存中的数据结构和编码有很多种,随着我们存储的数据类型的不同、数据量的大小不同都会引起内存数据结构的动态调整。

本文只是做数据结构和编码的一般性介绍,不做过多细节讨论,一方面是关于Redis源码分析的资料网上有很多,还有一个原因就是Redis每一个版本的实现有很大差异,一旦展开细节讨论,每一个点每一个数据结构都会很复杂,所以我们这里就不展开讨论这些,只是起到抛砖引玉作用。

1、OBJECT encoding key、DEBUG OBJECT key

我们知道使用type命令可以查看某个key是否是5种数据类型之一,但是当我们想查看某个key底层是使用哪种数据结构和编码来存储的时候,可以使用OBJECT encoding命令。

同样一个key,由于我们设置的值不同,Redis选用了不同的内存数据结构和编码。虽然Redis提供String数据类型,但是Redis会自动识别我们cache的数据类型是int还是String。

如果我们设置的是字符串,且这个字符串长度不大于39字节,那么将使用embstr来编码;如果大于39字节将使用raw来编码。Redis4.0将这个阀值扩大了45个字节。

除了使用OBJECT encoding命令外,我们还可以使用DEBUG OBJECT命令来查看更多详细信息。

DEBUG OBJECT能看到这个对象的refcount引用计数、serializedlength长度、lru_seconds_idle时间,这些信息决定了这个key缓存清除策略。

2、简单动态字符串(simple dynamic String)

简单动态字符串简称SDS,在Redis中所有涉及到字符串的地方都是使用SDS实现,当然这里不包括字面量。SDS与传统C字符串的区别就是SDS是结构化的,它可以高效的处理分配、回收、长度计算等问题。

<pre style="box-sizing: border-box; font-family: monospace; font-size: 18px; margin: 20px 0px; padding: 15px; border: 0px; background-color: rgb(244, 245, 246); white-space: pre-wrap; word-break: break-all; color: rgb(34, 34, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">struct sdshdr { 
 unsigned int len; 
 unsigned int free; 
 char buf[]; 
}; 

这是Redis3.0版本的sds.h头文件定义,3.0.0之后变化比较大。len表示字符串长度,free表示空间长度,buf数组表示字符串。

SDS有很多优点,比如,获取长度的时间复杂度O(1),不需要遍历所有charbuf[]组数,直接返回len值。

<pre style="box-sizing: border-box; font-family: monospace; font-size: 18px; margin: 20px 0px; padding: 15px; border: 0px; background-color: rgb(244, 245, 246); white-space: pre-wrap; word-break: break-all; color: rgb(34, 34, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">static inline size_t sdslen(const sds s) { 
 struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr))); 
 return sh->len; 
} 

当然还有空间分配检查、空间预分配、空间惰性释放等,这些都是SDS结构化字符串带来的强大的扩展能力。

3、链表(linked list)

链表数据结构我们是比较熟悉的,最大的特点就是节点的增、删非常灵活。Redis List数据类型底层就是基于链表来实现。这是Redis3.0实现。

<pre style="box-sizing: border-box; font-family: monospace; font-size: 18px; margin: 20px 0px; padding: 15px; border: 0px; background-color: rgb(244, 245, 246); white-space: pre-wrap; word-break: break-all; color: rgb(34, 34, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">typedef struct list { 
 listNode *head; 
 listNode *tail; 
 void *(*dup)(void *ptr); 
 void (*free)(void *ptr); 
 int (*match)(void *ptr, void *key); 
 unsigned long len; 
} list; 

typedef struct listNode { 
 struct listNode *prev; 
 struct listNode *next; 
 void *value; 
} listNode; 

在Redis3.2.0版本的时候引入了quicklist链表结构,结合了linkedlist和ziplist的优势。

<pre style="box-sizing: border-box; font-family: monospace; font-size: 18px; margin: 20px 0px; padding: 15px; border: 0px; background-color: rgb(244, 245, 246); white-space: pre-wrap; word-break: break-all; color: rgb(34, 34, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">typedef struct quicklist { 
 quicklistNode *head; 
 quicklistNode *tail; 
 unsigned long count; /* total count of all entries in all ziplists */ 
 unsigned int len; /* number of quicklistNodes */ 
 int fill : 16; /* fill factor for individual nodes */ 
 unsigned int compress : 16; /* depth of end nodes not to compress;0=off */ 
} quicklist; 

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 */ 
 unsigned int container : 2; /* NONE==1 or ZIPLIST==2 */ 
 unsigned int recompress : 1; /* was this node previous compressed? */ 
 unsigned int attempted_compress : 1; /* node can't compress; too small */ 
 unsigned int extra : 10; /* more bits to steal for future usage */ 
} quicklistNode; 

quicklist提供了灵活性同时也兼顾了ziplist的压缩能力,quicklist->encoding指定了两种压缩算法。quickList->compress表示我们可以进行quicklist node的深度压缩能力。Redis提供了两个有关于压缩的配置:

  • List-max-zipList-size:zipList长度控制;
  • List-compress-depth:控制链表两端节点的压缩个数,越是靠近两端的节点被访问的机率越大,所以可以将访问机率大的节点不压缩,其他节点进行压缩。

对比Redis3.2的quicklist与Redis3.0,很明显quicklist提供了更加丰富的压缩功能。Redis3.0的版本是每个listnode直接缓存值,而quicklistnode还有强大的有关于压缩能力。

<pre style="box-sizing: border-box; font-family: monospace; font-size: 18px; margin: 20px 0px; padding: 15px; border: 0px; background-color: rgb(244, 245, 246); white-space: pre-wrap; word-break: break-all; color: rgb(34, 34, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">LPUSH list:products:mall 100 200 300 
(integer) 3 

OBJECT encoding list:products:mall 
"quicklist" 

4、字典(dict)

dict字典是基于Hash算法来实现,是Hash数据类型的底层存储数据结构。我们来看下Redis3.0.0版本的dict.h头文件定义:

<pre style="box-sizing: border-box; font-family: monospace; font-size: 18px; margin: 20px 0px; padding: 15px; border: 0px; background-color: rgb(244, 245, 246); white-space: pre-wrap; word-break: break-all; color: rgb(34, 34, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">typedef struct dict { 
 dictType *type; 
 void *privdata; 
 dictht ht[2]; 
 long rehashidx; 
 int iterators; 
} dict; 

typedef struct dictht { 
 dictEntry **table; 
 unsigned long size; 
 unsigned long sizemask; 
 unsigned long used; 
} dictht; 

typedef struct dictEntry { 
 void *key; 
 union { 
 void *val; 
 uint64_t u64; 
 int64_t s64; 
 double d; 
 } v; 
 struct dictEntry *next; 
} dictEntry; 

说到Hash table有两个东西是我们经常会碰到的,首先就是Hash碰撞问题,Redis dict是采用链地址法来解决,dictEntry->next就是指向下个冲突key的节点。

还有一个经常碰到的就是rehash的问题,提到rehash我们还是有点担心性能的。那么Redis实现是非常巧妙的,采用惰性渐进式rehash算法。

在dict struct里有一个ht[2]组数,还有一个rehashidx索引。Redis进行rehash的大致算法是这样的:

首先会开辟一个新的dictht空间,放在ht[2]索引上,此时将rehashidx设置为0,表示开始进入rehash阶段,这个阶段可能会持续很长时间,rehashidx表示dictEntry个数。每次当有对某个ht[1]索引中的key进行访问时,获取、删除、更新,Redis都会将当前dictEntry索引中的所有key rehash到ht[2]字典中。一旦rehashidx=-1表示rehash结束。

5、跳表(skip list)

skip list是Zset的底层数据结构,有着高性能的查找排序能力。

我们都知道一般用来实现带有排序的查找都是用Tree实现,不管是各种变体的B Tree还是B+Tree,本质都是用来做顺序查找。

skip list实现起来简单,性能也与B Tree相接近。

<pre style="box-sizing: border-box; font-family: monospace; font-size: 18px; margin: 20px 0px; padding: 15px; border: 0px; background-color: rgb(244, 245, 246); white-space: pre-wrap; word-break: break-all; color: rgb(34, 34, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">typedef struct zskiplistNode { 
 robj *obj; 
 double score; 
 struct zskiplistNode *backward; 
 struct zskiplistLevel { 
 struct zskiplistNode *forward; 
 unsigned int span; 
 } level[]; 
} zskiplistNode; 

typedef struct zskiplist { 
 struct zskiplistNode *header, *tail; 
 unsigned long length; 
 int level; 
} zskiplist; 

zskiplistNode->zskiplistLevel->span这个值记录了当前节点距离下个节点的跨度。每一个节点会有最大不超过zskiplist->level节点个数,分别用来表示不同跨度与节点的距离。

每个节点会有多个forward向前指针,只有一个backward指针。每个节点会有对象*obj和score分值,每个分值都会按照顺序排列。

6、整数集合(int set)

int set整数集合是set数据类型的底层实现数据结构,它的特点和使用场景很明显,只要我们使用的集合都是整数且在一定的范围之内,都会使用整数集合编码。

<pre style="box-sizing: border-box; font-family: monospace; font-size: 18px; margin: 20px 0px; padding: 15px; border: 0px; background-color: rgb(244, 245, 246); white-space: pre-wrap; word-break: break-all; color: rgb(34, 34, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">SADD set:userid 100 200 300 
(integer) 3 

OBJECT encoding set:userid 
"intset" 

int set使用一块连续的内存来存储集合数据,它是数组结构不是链表结构。

<pre style="box-sizing: border-box; font-family: monospace; font-size: 18px; margin: 20px 0px; padding: 15px; border: 0px; background-color: rgb(244, 245, 246); white-space: pre-wrap; word-break: break-all; color: rgb(34, 34, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">typedef struct intset { 
 uint32_t encoding; 
 uint32_t length; 
 int8_t contents[]; 
} intset; 

intset->encoding用来确定contents[]是什么类型的整数编码,以下三种值之一:

<pre style="box-sizing: border-box; font-family: monospace; font-size: 18px; margin: 20px 0px; padding: 15px; border: 0px; background-color: rgb(244, 245, 246); white-space: pre-wrap; word-break: break-all; color: rgb(34, 34, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">#define INTSET_ENC_INT16 (sizeof(int16_t)) 
#define INTSET_ENC_INT32 (sizeof(int32_t)) 
#define INTSET_ENC_INT64 (sizeof(int64_t)) 

Redis会根据我们设置的值类型动态sizeof出一个对应的空间大小。如果我们集合原来是int16,然后往集合里添加了int32整数将触发升级,一旦升级成功不会触发降级操作。

7、压缩表(zip list)

zip list压缩表是List、Zset、Hash数据类型的底层数据结构之一。它是为了节省内存通过压缩数据存储在一块连续的内存空间中。

<pre style="box-sizing: border-box; font-family: monospace; font-size: 18px; margin: 20px 0px; padding: 15px; border: 0px; background-color: rgb(244, 245, 246); white-space: pre-wrap; word-break: break-all; color: rgb(34, 34, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">typedef struct zlentry { 
 unsigned int prevrawlensize, prevrawlen; 
 unsigned int lensize, len; 
 unsigned int headersize; 
 unsigned char encoding; 
 unsigned char *p; 
} zlentry; 

它最大的优点就是压缩空间,空间利用率很高。缺点就是一旦出现更新可能就是连锁更新,因为数据在内容空间中都是连续的,最极端情况下就是可能出现顺序连锁扩张。

压缩列表会由多个zlentry节点组成,每一个zlentry记录上一个节点长度和大小,当前节点长度lensize和大小len包括编码encoding。

这取决于业务场景,Redis提供了一组配置,专门用来针对不同的场景进行阈值控制:

<pre style="box-sizing: border-box; font-family: monospace; font-size: 18px; margin: 20px 0px; padding: 15px; border: 0px; background-color: rgb(244, 245, 246); white-space: pre-wrap; word-break: break-all; color: rgb(34, 34, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">hash-max-ziplist-entries 512 
hash-max-ziplist-value 64 
list-max-ziplist-entries 512 
list-max-ziplist-value 64 
zset-max-ziplist-entries 128 
zset-max-ziplist-value 64 

上述配置分别用来配置ziplist作为Hash、List、Zset数据类型的底层压缩阈值控制。

8、Redis Object类型与映射

Redis内部每一种数据类型都是对象化的,也就是我们所说的5种数据类型其实内部都会对应到Redis Object对象,然后再由Redis Object来包装具体的存储数据结构和编码。

<pre style="box-sizing: border-box; font-family: monospace; font-size: 18px; margin: 20px 0px; padding: 15px; border: 0px; background-color: rgb(244, 245, 246); white-space: pre-wrap; word-break: break-all; color: rgb(34, 34, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">typedef struct redisObject { 
 unsigned type:4; 
 unsigned encoding:4; 
 unsigned lru:REDIS_LRU_BITS; 
 int refcount; 
 void *ptr; 
} robj; 

这是一个很OO的设计,redisObject->type是5种数据类型之一,redisObject->encoding是这个数据类型所使用的数据结构和编码。

我们看下Redis提供的5种数据类型与每一种数据类型对应的存储数据结构和编码。

<pre style="box-sizing: border-box; font-family: monospace; font-size: 18px; margin: 20px 0px; padding: 15px; border: 0px; background-color: rgb(244, 245, 246); white-space: pre-wrap; word-break: break-all; color: rgb(34, 34, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">/* Object types */ 
#define REDIS_STRING 0 
#define REDIS_LIST 1 
#define REDIS_SET 2 
#define REDIS_ZSET 3 
#define REDIS_HASH 4 

#define REDIS_ENCODING_RAW 0 
#define REDIS_ENCODING_INT 1 
#define REDIS_ENCODING_HT 2 
#define REDIS_ENCODING_ZIPMAP 3 
#define REDIS_ENCODING_LINKEDLIST 4 
#define REDIS_ENCODING_ZIPLIST 5 
#define REDIS_ENCODING_INTSET 6 
#define REDIS_ENCODING_SKIPLIST 7 
#define REDIS_ENCODING_EMBSTR 8 

REDIS_ENCODING_ZIPMAP 3这个编码可以忽略了,在特定的情况下有性能问题,在Redis 2.6版本之后已经废弃,为了兼容性保留。

图是Redis 5种数据类型与底层数据结构和编码的对应关系:

但是这种对应关系在每一个版本中都会有可能发生变化,这也是Redis Object的灵活性所在,有着OO的这种多态性。

  • redisObject->refcount表示当前对象的引用计数,在Redis内部为了节省内存采用了共享对象的方法,当某个对象被引用的时候这个refcount会加1,释放的时候会减1。
  • redisObject->lru表示当前对象的空转时长,也就是idle time,这个时间会是Redis lru算法用来释放对象的时间依据。可以通过OBJECT idletime命令查看某个key的空转时长lru时间。

二、Redis内存管理策略

Redis在服务端分别为不同的db index维护一个dict,这个dict称为key space键空间。每一个RedisClient只能属于一个db index,在Redis服务端会维护每一个链接的RedisClient。

<pre style="box-sizing: border-box; font-family: monospace; font-size: 18px; margin: 20px 0px; padding: 15px; border: 0px; background-color: rgb(244, 245, 246); white-space: pre-wrap; word-break: break-all; color: rgb(34, 34, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">typedef struct redisClient { 
 uint64_t id; 
 int fd; 
 redisDb *db; 
} redisClient; 

在服务端每一个Redis客户端都会有一个指向redisDb的指针。

<pre style="box-sizing: border-box; font-family: monospace; font-size: 18px; margin: 20px 0px; padding: 15px; border: 0px; background-color: rgb(244, 245, 246); white-space: pre-wrap; word-break: break-all; color: rgb(34, 34, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">typedef struct redisDb { 
 dict *dict; 
 dict *expires; 
 dict *blocking_keys; 
 dict *ready_keys; 
 dict *watched_keys; 
 struct evictionPoolEntry *eviction_pool; 
 int id; 
 long long avg_ttl; 
} redisDb; 

key space键空间就是这里的redisDb->dict。redisDb->expires是维护所有键空间的每一个key的过期时间。

1、键过期时间、生存时间

对于一个key我们可以设置它多少秒、毫秒之后过期,也可以设置它在某个具体的时间点过期,后者是一个时间戳。例如:

  • EXPIRE命令可以设置某个key多少秒之后过期;
  • PEXPIRE命令可以设置某个key多少毫秒之后过期;
  • EXPIREAT命令可以设置某个key在多少秒时间戳之后过期;
  • PEXPIREAT命令可以设置某个key在多少毫秒时间戳之后过期;
  • PERSIST命令可以移除键的过期时间。

其实上述命令最终都会被转换成对PEXPIREAT命令。在redisDb->expires指向的key字典中维护着一个到期的毫秒时间戳。

TTL、PTTL可以通过这两个命令查看某个key的过期秒、毫秒数。

Redis内部有一个事件循环,这个事件循环会检查键的过期时间是否小于当前时间,如果小于则会删除这个键。

2、过期键删除策略

在使用Redis的时候我们最关心的就是键是如何被删除的,如何高效准时地删除某个键。其实Redis提供了两个方案来完成这件事情:惰性删除、定期删除双重删除策略。

惰性删除:当我们访问某个key的时候,Redis会检查它是否过期。

<pre style="box-sizing: border-box; font-family: monospace; font-size: 18px; margin: 20px 0px; padding: 15px; border: 0px; background-color: rgb(244, 245, 246); white-space: pre-wrap; word-break: break-all; color: rgb(34, 34, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">robj *lookupKeyRead(redisDb *db, robj *key) { 
 robj *val; 

 expireIfNeeded(db,key); 
 val = lookupKey(db,key); 
 if (val == NULL) 
 server.stat_keyspace_misses++; 
 else 
 server.stat_keyspace_hits++; 
 return val; 
} 

int expireIfNeeded(redisDb *db, robj *key) { 
 mstime_t when = getExpire(db,key); 
 mstime_t now; 

 if (when < 0) return 0; /* No expire for this key */ 

 if (server.loading) return 0; 

 now = server.lua_caller ? server.lua_time_start : mstime(); 
 if (server.masterhost != NULL) return now > when; 

 /* Return when this key has not expired */ 
 if (now <= when) return 0; 

 /* Delete the key */ 
 server.stat_expiredkeys++; 
 propagateExpire(db,key); 
 notifyKeyspaceEvent(REDIS_NOTIFY_EXPIRED,"expired",key,db->id); 
 return dbDelete(db,key); 
} 

定期删除:Redis通过事件循环,周期性地执行key的过期删除动作。

<pre style="box-sizing: border-box; font-family: monospace; font-size: 18px; margin: 20px 0px; padding: 15px; border: 0px; background-color: rgb(244, 245, 246); white-space: pre-wrap; word-break: break-all; color: rgb(34, 34, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) { 
 /* Handle background operations on Redis databases. */ 
 databasesCron(); 
} 

void databasesCron(void) { 
 /* Expire keys by random sampling. Not required for slaves 
 * as master will synthesize DELs for us. */ 
 if (server.active_expire_enabled && server.masterhost == NULL) 
 activeExpireCycle(ACTIVE_EXPIRE_CYCLE_SLOW); 
} 

要注意的是:

  • 惰性删除是每次只要有读取、写入都会触发惰性删除代码;
  • 定期删除是由Redis EventLoop来触发的。Redis内部很多维护性工作都是基于EventLoop。

3、AOF、RDB处理过期键策略

既然键会随时存在过期问题,那么涉及到持久化Redis是如何帮我们处理的?

当Redis使用RDB方式持久化时,每次持久化的时候就会检查这些即将被持久化的key是否已经过期,如果过期将直接忽略,持久化那些没有过期的键。

当Redis作为master主服务器启动的时候,载入rdb持久化键时也会检查这些键是否过期,将忽略过期的键,只载入没过期的键。

当Redis使用AOF方式持久化时,每次遇到过期的key Redis会追加一条DEL命令到AOF文件,也就是说只要我们顺序载入执行AOF命令文件就会删除过期的键。

如果Redis作为从服务器启动的话,它一旦与master主服务器建立链接就会清空所有数据进行完整同步。当然新版本的Redis支持SYCN2的半同步,如果是已经建立了master/slave主从同步之后,主服务器会发送DEL命令给所有从服务器执行删除操作。

4、Redis LRU算法

在使用Redis的时候我们会设置maxmemory选项,64位的默认是0不限制。线上的服务器必须要设置的,要不然很有可能导致Redis宿主服务器直接内存耗尽,最后链接都上不去。

所以基本要设置两个配置:

  • maxmemory最大内存阈值;
  • maxmemory-policy到达阈值的执行策略。

可以通过CONFIG GET
maxmemory/maxmemory-policy分别查看这两个配置值,也可以通过CONFIG SET去分别配置。

  • maxmemory-policy有一组配置,可以用在很多场景下:
  • noeviction:客户端尝试执行会让更多内存被使用的命令直接报错;
  • allkeys-lru:在所有key里执行lru算法;
  • volatile-lru:在所有已经过期的key里执行lru算法;
  • allkeys-random:在所有key里随机回收;
  • volatile-random:在已经过期的key里随机回收;
  • volatile-ttl:回收已经过期的key,并且优先回收存活时间(TTL)较短的键。

关于cache的命中率可以通过info命令查看键空间的命中率和未命中率。

<pre style="box-sizing: border-box; font-family: monospace; font-size: 18px; margin: 20px 0px; padding: 15px; border: 0px; background-color: rgb(244, 245, 246); white-space: pre-wrap; word-break: break-all; color: rgb(34, 34, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"># Stats 
keyspace_hits:33 
keyspace_misses:5 

maxmemory在到达阈值的时候会采用一定的策略去释放内存,这些策略我们可以根据自己的业务场景来选择,默认是noeviction 。

Redis LRU算法有一个取样的优化机制,可以通过一定的取样因子来加强回收的key的准确度。CONFIG GET maxmemory-samples查看取样配置,具体可以参考更加详细的文章。

三、Redis持久化方式

Redis本身提供持久化功能,有两种持久化机制,一种是数据持久化RDB,一种是命令持久化AOF,这两种持久化方式各有优缺点,也可以组合使用。一旦组合使用,Redis在载入数据的时候会优先载入aof文件,只有当AOF持久化关闭的时候才会载入rdb文件。

1、RDB(Redis DataBase)

RDB是Redis数据库,Redis会根据一个配置来触发持久化:

<pre style="box-sizing: border-box; font-family: monospace; font-size: 18px; margin: 20px 0px; padding: 15px; border: 0px; background-color: rgb(244, 245, 246); white-space: pre-wrap; word-break: break-all; color: rgb(34, 34, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">#save <seconds> <changes> 

save 900 1 
save 300 10 
save 60 10000 

CONFIG GET save 
1) "save" 
2) "3600 1 300 100 60 10000" 

表示在多少秒之类的变化次数,一旦达到这个触发条件Redis将触发持久化动作。

Redis在执行持久化的时候有两种模式BGSAVE、SAVE:

  • BGSAVE是后台保存,Redis会fork出一个子进程来处理持久化,不会block用户的执行请求;
  • SAVE则会block用户执行请求。
<pre style="box-sizing: border-box; font-family: monospace; font-size: 18px; margin: 20px 0px; padding: 15px; border: 0px; background-color: rgb(244, 245, 246); white-space: pre-wrap; word-break: break-all; color: rgb(34, 34, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">struct redisServer { 
long long dirty;/* Changes to DB from the last save */ 
time_t lastsave; /* Unix time of last successful save */ 
long long dirty_before_bgsave; 
pid_t rdb_child_pid;/* PID of RDB saving child */ 
struct saveparam *saveparams; /* Save points array for RDB */ 
} 

struct saveparam { 
 time_t seconds; 
 int changes; 
}; 

RedisServer包含的信息很多,其中就包含了有关于RDB持久化的信息。

redisServer->dirty至上次save到目前为止的change数。redisServer->lastsave上次save时间。

saveparam struct保存了我们通过save命令设置的参数,time_t是个long时间戳。

<pre style="box-sizing: border-box; font-family: monospace; font-size: 18px; margin: 20px 0px; padding: 15px; border: 0px; background-color: rgb(244, 245, 246); white-space: pre-wrap; word-break: break-all; color: rgb(34, 34, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">typedef __darwin_time_t time_t; 

typedef long __darwin_time_t; /* time() */ 

int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) { 
 for (j = 0; j < server.saveparamslen; j++) { 
 struct saveparam *sp = server.saveparams+j; 
 if (server.dirty >= sp->changes && 
 server.unixtime-server.lastsave > sp->seconds && 
 (server.unixtime-server.lastbgsave_try > 
 REDIS_BGSAVE_RETRY_DELAY || 
 server.lastbgsave_status == REDIS_OK)) 
 { 
 redisLog(REDIS_NOTICE,"%d changes in %d seconds. Saving...", 
 sp->changes, (int)sp->seconds); 
 rdbSaveBackground(server.rdb_filename); 
 break; 
 } 
 } 
} 

Redis事件循环会周期性的执行serverCron方法,这段代码会循环遍历server.saveparams参数链表。

如果server.dirty大于等于我们参数里配置的变化并且
server.unixtime-server.lastsave大于参数里配置的时间,并且server.unixtime-server.lastbgsave_try减去bgsave重试延迟时间,或者当前server.lastbgsave_status== REDIS_OK则执行rdbSaveBackground方法。

2、AOF(Append-only file)

AOF持久化是采用对文件进行追加对方式进行,每次追加都是Redis处理的命令。有点类似command sourcing命令溯源的模式,只要我们可以将所有的命令按照执行顺序在重放一遍就可以还原最终的Redis内存状态。

AOF持久化最大的优势是可以缩短数据丢失的间隔,做到秒级的丢失率。RDB会丢失上一个保存周期到目前的所有数据,只要没有触发save命令设置的save seconds changes阈值数据就会一直不被持久化。

<pre style="box-sizing: border-box; font-family: monospace; font-size: 18px; margin: 20px 0px; padding: 15px; border: 0px; background-color: rgb(244, 245, 246); white-space: pre-wrap; word-break: break-all; color: rgb(34, 34, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">struct redisServer { 
 /* AOF buffer, written before entering the event loop */ 
 sds aof_buf; 
 } 

struct sdshdr { 
 unsigned int len; 
 unsigned int free; 
 char buf[]; 
}; 

aof_buf是命令缓存区,采用sds结构缓存,每次当有命令被执行当时候都会写一次到aof_buf中。有几个配置用来控制AOF持久化的机制。

<pre style="box-sizing: border-box; font-family: monospace; font-size: 18px; margin: 20px 0px; padding: 15px; border: 0px; background-color: rgb(244, 245, 246); white-space: pre-wrap; word-break: break-all; color: rgb(34, 34, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">appendonly no 
appendfilename "appendonly.aof" 

appendonly用来控制是否开启AOF持久化,appendfilename用来设置aof文件名。

<pre style="box-sizing: border-box; font-family: monospace; font-size: 18px; margin: 20px 0px; padding: 15px; border: 0px; background-color: rgb(244, 245, 246); white-space: pre-wrap; word-break: break-all; color: rgb(34, 34, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">appendfsync always 
appendfsync everysec 
appendfsync no 

appendfsync用来控制命令刷盘机制。现在操作系统都有文件cache/buffer的概念,所有的写入和读取都会走cache/buffer,并不会每次都同步刷盘,因为这样性能一定会受影响。所以Redis也提供了这个选项让我们来自己根据业务场景控制。

  • always:每次将aof_buf命令写入aof文件并且执行实时刷盘。
  • everysec:每次将aof_buf命令写入aof文件,但是每隔一秒执行一次刷盘。
  • no:每次将aof_buf命令写入aof文件不执行刷盘,由操作系统来自行控制。

AOF也是采用后台子进程的方式进行,与主进程共享数据空间也就是aof_buf,但是只要开始了AOF子进程之后Redis事件循环文件事件处理器会将之后的命令写入另外一个aof_buf,这样就可以做到平滑的切换。

AOF会不断的追加命令进aof文件,随着时间和并发量的加大aof文件会极速膨胀,所以有必要对这个文件大小进行优化。Redis基于rewrite重写对文件进行压缩。

<pre style="box-sizing: border-box; font-family: monospace; font-size: 18px; margin: 20px 0px; padding: 15px; border: 0px; background-color: rgb(244, 245, 246); white-space: pre-wrap; word-break: break-all; color: rgb(34, 34, 34); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">no-appendfsync-on-rewrite no/yes 
auto-aof-rewrite-percentage 100 
auto-aof-rewrite-min-size 64mb 

no-appendfsync-on-rewrite控制是否在bgrewriteaof的时候还需要进行命令追加,如果追加可能会出现磁盘IO跑高现象。

上面说过,当AOF进程在执行的时候原来的事件循环还会正常的追加命令进aof文件,同时还会追加命令进另外一个aof_buf,用来做新aof文件的重写。这是两条并行的动作,如果我们设置成yes就不追加原来的aof_buf 因为新的aof文件已经包含了之后进来的命令。

auto-aof-rewrite-percentage和auto-aof-rewrite-min -size64mb这两个配置前者是文件增长百分比来进行rewrite,后者是按照文件大小增长进行rewrite。

Redis——Redis数据结构底层原理 本博文主要介绍Redis数据结构底层原理。帮助大家更好的学习和理解Redis数据结构。Redis发展到现在已经有 9 种数据类型了,其中最基础、最常用的数据类型有 5 种,它们分别是:字符串类型、列表类型、哈希表类型、集合类型、有序集合类型,而在这 5 种数据类型中最常用的是字符串类型。这五种数据结构的底层实现丰富。 阅读详情

相关推荐

Vitis Model Composer闪退?手把手教你用命令行启动Vivado 2021后的System Generator

本文详细解析了Vitis Model Composer闪退问题的根本原因,并提供了命令行启动Vivado 2021后System Generator的完整解决方案。从环境变量配置、组件缺失排查到权限问题处理,手把手教你通过命令行深度排错,并分享高级自动化脚本和系统优化技巧,帮助开发者高效使用Vitis Model Composer与System Generator进行HDL开发。

weixin_26905487的博客 347

DEBUG OBJECT key

DEBUG OBJECT是一个不应该被客户端使用的调试命令。 请参考OBJECT命令 simple-string-reply 本文作者:陈群 本文来自云栖社区合作伙伴rediscn,了解相关信息可以关注redis.cn网站。 ...

weixin_33736649的博客 521

各种AI的调试日记——辅助工作篇

记录使用各种AI(语言大模型)的心得和总结

m0_73056986的博客 1389

redis查看某一个key的大小_分析redis key大小的几种方法

当redis被用作缓存时,有时我们希望了解key的大小分布,或者想知道哪些key占的空间比较大。本文提供了几种方法。一. bigKeys这是redis-cli自带的一个命令。对整个redis进行扫描,寻找较大的key。例:redis-cli -h b.redis -p 1959 --bigkeys1输出:# Scanning the entire keyspace to find biggest ...

weixin_35478664的博客 5658

H5 获取手机GPS坐标

只要手机有GPS模块,可以用HTML5的Geolocation接口获取 在HTML5中,geolocation作为navigator的一个属性出现,它本身是一个对象,拥有三个方法: getCurrentPosition watchPosition clearWatch 1.简单 function getLocation(){ if(navigator.geolocation){ //判断是否有这个对象 navigator.geoloc

达拉崩巴斑得贝迪卜多比鲁翁_ 4668

2026年软考高级网络规划设计师备考指南

在国家网络安全法律法规和政策的指导下,能够依据网络技术发展的趋势进行计算机网络领域的需求分析、规划设计、部署实施、评测、运行维护等工作。 在需求分析阶段,写出网络系统需求规格说明书;在规划设计阶段,完成逻辑结构设计、物理结构设计,选用适宜的网络设备,按照标准规范编写系统设计文档及项目开发计划;在部署实施阶段,组织项目施工,对项目实施过程进行质量控制、进度控制、成本管理,能具体指导项目实施;在评测运维阶段,能根据相关标准和规范对网络进行评估测试,能制定运行维护、故障分析与处理机制,确保网络提供正常服务;

鹿鸣天涯 1万+

分析redis key大小的几种方法

本文系转载,便于整理和归档。原文地址 https://cloud.tencent.com/developer/article/1757281 当redis被用作缓存时,有时我们希望了解key的大小分布,或者想知道哪些key占的空间比较大。本文提供了几种方法。 一. bigKeys 这是redis-cli自带的一个命令。对整个redis进行扫描,寻找较大的key。例: redis-cli -h b.redis -p 1959 --bigkeys 输出: # Scanning the entire key.

wejack的专栏 2295

最新Python爬虫实战(入门爬虫篇)——案例15:微博热搜数据采集(详细爬虫思路截图+抓包动图演示+完整爬虫代码+详细注释)

本次案例通过 Python 爬虫技术,成功实现了对微博热搜 API 接口数据的批量爬取与 Excel 保存。代码采用模块化设计,包含接口请求、JSON 解析、数据提取、Excel 持久化等完整流程;通过模拟真实浏览器的请求头和 cookies,有效规避基础反爬策略,保证请求的稳定性。整个爬虫逻辑遵循“API请求 → JSON解析 → 数据提取 → 保存到Excel”的标准流程,与异步加载类网站的爬取范式高度一致。AJAX 接口抓包与分析。

努力让自己发光,对的人才能迎着光而来 7万+

无涯教程-Redis - DEBUG OBJECT 命令函数

Redis DEBUG OBJECT 是一个调试命令,客户端不应使用,而是检查OBJECT命令。

无涯教程 842

用 ABAP 驾驭 OAuth 2.0:从 Authorization Code 到 SAML Bearer 的全栈落地指南

ABAP平台OAuth 2.0客户端开发实践 本文为SAP开发者详细解析ABAP平台内置OAuth 2.0客户端的使用方法,涵盖四种授权流的最佳实践: 授权流选择: Authorization Code:适用于用户交互场景 SAML 2.0 Bearer:适合已有SAML体系的企业 Client Credentials:系统间通信 Password Credentials:过渡方案(不推荐) 配置流程: 通过SE80定义Client Profile 使用SOAUTH2_CLIENT/OA2C_CONFIG配

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 309

TC3XX - MCAL知识点(三十八):AURIX™ TC3xx 的EDSADC实现旋变解码功能(理论知识+手册阅读)

本文系统梳理了英飞凌AURIX系列芯片EDSADC模块的技术手册内容。首先概述了该模块的架构特点,包括模拟/数字输入通道和总线地址信息。重点解析了手册中的关键知识点:1)模拟量输入的差分/单端模式;2)数字量连接的触发、数据源和时钟源选择;3)功能配置中的寄存器操作注意事项;4)输入通道的三种数据来源模式;5)滤波器链的组成和工作原理;6)旋变应用中的载波信号生成和延迟补偿机制。最后对SRM、SAUL、SBLL等专业术语进行了详细解释。全文为理解EDSADC模块的硬件特性和配置方法提供了系统的技术参考。

梦想技术家 589

每天学些redis命令(3)

redis命令

课前的博客 411

Unreal 从入门到精通之VR常用操作

本文总结了使用Unreal5开发VR项目时遇到的常见问题及其解决方案。首先,介绍了如何通过VRPawn的TryTeleport函数设置玩家视角位置,确保头显移动时安全区域的正确传送。其次,详细说明了如何在Unreal中播放视频,包括导入视频文件、创建媒体播放器和媒体纹理,并将其应用到3D物体上。接着,讲解了如何播放VR全景视频,建议使用MediaPlate媒体板并调整几何体为球体。最后,讨论了如何手动打开和关闭VR模式,以便在PC模式和头显模式之间切换。这些方法为VR开发提供了实用的技术指导。

TxNet 950

深入浅出Redis:一文掌握Redis底层数据结构与实现原理

Redis之所以高效,不仅在于它的内存操作,更在于巧妙的数据结构设计。本文带你揭开Redis底层数据结构的神秘面纱,包括动态字符串SDS如何解决C语言字符串问题、IntSet如何高效处理整数集合、Dict哈希字典如何快速实现键值对存储与扩容、ZipList如何节省空间但又引发“连锁更新”的问题、QuickList如何兼具空间高效与操作性能、SkipList跳表如何做到类似红黑树的查询效率,以及RedisObject如何统一不同类型的数据存储。在阅读完本文后,你将对Redis数据结构的设计思想与源码实现原理有

weixin_47762494的博客 8728

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

Redis的五种数据结构的底层实现原理

Redis的五种数据结构的底层实现原理: 1、String底层实现方式:动态字符串sds 或者 long; 2、Hash底层实现方式:压缩列表ziplist 或者 字典dict; 3、List在Redis3.2之前的底层实现方式:压缩列表ziplist 或者 双向循环链表linkedlist; 4、List在Redis3.2及之后的底层实现方式:quicklist 5、Set底层实现方式:有序整数集合intset 或者 字典dict 6、Sorted set底层实现方式:压缩列表ziplist 或者zset

张维鹏的博客 3万+

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

【Redis】Redis是什么、能干什么、主要功能和工作原理的详细讲解

【Redis】Redis是什么、能干什么、主要功能和工作原理的详细讲解

H1727548的博客 6万+

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

深入解析 Redis 原理:架构、数据结构与高效存储

Redis 是一款高性能的内存键值数据库,以其单线程 + 多路复用的架构,实现了极高的吞吐量。本文深入解析 Redis 的核心原理,包括数据结构(如 SDS、跳表、哈希表等)、持久化机制(RDB 快照与 AOF 日志)、高并发优化(惰性删除、定期删除)及分布式架构(主从复制、Sentinel、Cluster)。通过合理的缓存策略和数据结构选择,Redis 在高并发系统中表现卓越,为大规模应用提供可靠的存储与加速方案。

whl 博客专栏 3375

Redis原理篇 之数据结构

介绍了Redis的数据结构原理,包括五种数据类型和6中编码方式

weixin_49429082的博客 1389

Redis为什么那么快-存储原理篇

学好 Redis 是每个后端工程师必备的技能,本篇文章从Redis的存储结构开始,为你重新打开 Redis 大门,了解 Redis 真正的强大之处

MClink的博客 2333

图解Redis底层数据结构实现原理

待更新中

Seky_fei的博客 9434

Redis存储原理

我们知道,通过链表组织就是一种应对hash冲突的策略,hash冲突会使索引效率降低,然鹅从源码可以看到有一个负载因子,也就是已有元素与数组长度之比(used/size),负载因子越大表示hash冲突就越大,而我们知道hash时间复杂度是O(1),hash冲突到后面会使时间复杂度增加到O(n)。当负载因子大于1的时候就要进行扩容操作,也就是将数组长度翻倍,增大分母减小负载因子。而当负载因子小于0.1(恰好大于used的2^n)的时候就要进行缩容,因为这是占着茅坑不拉屎,浪费空间。不管扩容还是缩容,数组长度都会

oxygen3000的博客 1833

【Redis原理】Redis数据结构底层原理

Redis采用多种高效数据结构实现底层存储,主要包括:SDS(动态字符串)优化C语言字符串,支持O(1)长度获取和自动扩容;IntSet(整数集合)实现有序唯一整数存储,支持动态类型升级;ZipList(压缩列表)作为紧凑型双向链表,但存在连锁更新问题;Dict(哈希表)采用渐进式rehash平衡性能;SkipList(跳表)通过多层结构实现有序数据的快速查找;QuickList结合双向链表和压缩列表优点。RedisObject统一封装所有数据类型。

gadiaola的博客 1205

Redis为什么速度快:数据结构、存储及IO网络原理总结

Redis的高性能主要得益于其内存存储、优化的数据结构和算法、I/O多路复用技术、单线程模型与非阻塞式I/O以及网络协议与序列化方式等关键技术。持续监控与分析性能瓶颈:定期监控Redis的性能指标,如内存使用率、网络延迟、命令执行时间等,并通过性能分析工具找出性能瓶颈所在,针对性地进行优化。合理配置硬件资源:根据应用的需求和Redis的性能特点合理配置硬件资源,如内存大小、CPU核数、网络带宽等,确保Redis能够充分利用硬件资源发挥最佳性能。

码到三十五 1634

Redis原理篇——Redis数据结构

因为zset是是可以根据key找到score并且可以根据score进行排序的功能,所以底层采用的是跳表和哈希表,哈希表是用来进行查询,可以根据key来找到score,跳表可以根据score得分,并且能够快速的根据得分查询。当储存的所有数据都是整数的时候,set会采用整数集合Intset来存储,以节省内存。查找过程:先从第一个节点的最高级指针开始,找到下一个节点,然后下一个节点和要找的节点的得分,如果要找的得分大,就继续往后查找,如果要找的得分小,那就使用下一级的指针,类似与二分查找。

maruofan666的博客 1389

Redis命令详解以及存储原理

节点数量大于512(hash-max-ziplist-entries)或者所有字符串长度大于64(hash-max-ziplist-value),则使用dict实现;该字符串是动态字符串raw,字符串长度小于1M时,加倍扩容;双向链表的实现,列表首尾操作(删除和增加)时间复杂度为o(1);节点数量小于等于128且所有字符串长度小于等于64,则使用`ziplist`;元素中有一个不是整数或者节点数量大于512,则使用字典存储;元素都为整数且节点数量小于等于512,则使用整数数组存储;数据多的时候,访问性能;

Lenn Louis' Scribe 1205

Redis字符串(String):最常用的数据结构,这些用法你都懂吗?

SDS(Simple Dynamic String)是Redis自己实现的字符串结构,相比C语言原生字符串有显著优势。SDS结构定义int len;// 字符串长度int free;// 剩余空间长度char buf[];// 字符数组。

m0_75126572的博客 788
上一篇: 消息队列延时聚合通知的重要性
下一篇: NIO与BIO的区别、NIO的运行原理和并发使用场景
Java_ttcd
博客等级 码龄4年 198粉丝 190原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值