首頁  >  文章  >  資料庫  >  Redis記憶體模型(詳解)

Redis記憶體模型(詳解)

青灯夜游
青灯夜游轉載
2019-11-23 15:12:572143瀏覽

Redis是目前最火爆的記憶體資料庫之一,透過在記憶體中讀寫數據,大大提高了讀寫速度,可以說Redis是實現網站高並發不可或缺的一部分。 【推薦學習:Redis影片教學

Redis記憶體模型(詳解)

#我們使用Redis時,會接觸Redis的5種物件類型(字串、雜湊、列表、集合、有序集合),豐富的類型是Redis相對於Memcached等的一大優勢。在了解Redis的5種物件類型的用法和特點的基礎上,進一步了解Redis的記憶體模型,對Redis的使用有很大幫助,例如:

1、估算Redis記憶體使用量。目前為止,記憶體的使用成本仍然相對較高,使用記憶體不能無所顧忌;根據需求合理的評估Redis的記憶體使用量,選擇合適的機器配置,可以在滿足需求的情況下節省成本。

2、最佳化記憶體佔用。了解Redis記憶體模型可以選擇更合適的資料類型和編碼,更好的利用Redis記憶體。

3、分析解決問題。當Redis出現阻塞、記憶體佔用等問題時,盡快發現導致問題的原因,以便分析解決問題。

這篇文章主要介紹Redis的記憶體模型(以3.0為例),包括Redis佔用記憶體的情況及如何查詢、不同的物件類型在記憶體中的編碼方式、記憶體分配器(jemalloc)、簡單動態字串(SDS)、RedisObject等;然後在此基礎上介紹幾個Redis記憶體模型的應用。

一、Redis記憶體統計

工欲善其事必先利其器,在說明Redis記憶體之前先說明如何統計Redis使用記憶體的情況。

在客戶端透過redis-cli連接伺服器後(後面如無特殊說明,客戶端一律使用redis-cli),透過info指令可以查看記憶體使用情況:

info memory

其中,info指令可以顯示redis伺服器的許多訊息,包括伺服器基本資訊、CPU、記憶體、持久化、客戶端連線資訊等等;memory是參數,表示只顯示記憶體相關的資訊。

傳回結果中比較重要的幾個說明如下:

(1)used_memoryRedis分配器分配的記憶體總量(單位是位元組),包括使用的虛擬記憶體(即swap);Redis分配器後面會介紹。 used_memory_human只是顯示更友善。

(2)used_memory_rssRedis程式佔據作業系統的記憶體(單位是位元組),與top及ps指令看到的值是一致的;除了分配器分配的記憶體之外,used_memory_rss還包括進程運行本身所需的記憶體、記憶體碎片等,但不包含虛擬記憶體。

因此,used_memory和used_memory_rss,前者是從Redis角度得到的量,後者是從作業系統角度得到的量。二者之所以有所不同,一方面是因為記憶體碎片和Redis進程運行需要佔用內存,使得前者可能比後者小,另一方面虛擬內存的存在,使得前者可能比後者大。

由於在實際應用中,Redis的資料量會比較大,此時進程運行佔用的記憶體與Redis資料量和記憶體碎片相比,都會小得多;因此used_memory_rss和used_memory的比例,便成了衡量Redis記憶體碎片率的參數;這個參數就是mem_fragmentation_ratio。

(3)mem_fragmentation_ratio記憶體碎片比率,該值是used_memory_rss / used_memory的比值。

mem_fragmentation_ratio一般大於1,且該值越大,記憶體碎片比例越大。 mem_fragmentation_ratio081bcab459a1dd4e8c83af4a654df7ce1),称为共享对象。Redis为了节省内存,当有一些对象重复出现时,新的程序不会创建新的对象,而是仍然使用原来的对象。这个被重复使用的对象,就是共享对象。目前共享对象仅支持整数值的字符串对象。

共享对象的具体实现

Redis的共享对象目前只支持整数值的字符串对象。之所以如此,实际上是对内存和CPU(时间)的平衡:共享对象虽然会降低内存消耗,但是判断两个对象是否相等却需要消耗额外的时间。对于整数值,判断操作复杂度为O(1);对于普通字符串,判断复杂度为O(n);而对于哈希、列表、集合和有序集合,判断的复杂度为O(n^2)。

虽然共享对象只能是整数值的字符串对象,但是5种类型都可能使用共享对象(如哈希、列表等的元素可以使用)。

就目前的实现来说,Redis服务器在初始化时,会创建10000个字符串对象,值分别是0~9999的整数值;当Redis需要使用值为0~9999的字符串对象时,可以直接使用这些共享对象。10000这个数字可以通过调整参数REDIS_SHARED_INTEGERS(4.0中是OBJ_SHARED_INTEGERS)的值进行改变。

共享对象的引用次数可以通过object refcount命令查看,如下图所示。命令执行的结果页佐证了只有0~9999之间的整数会作为共享对象。

(5)ptr

ptr指针指向具体的数据,如前面的例子中,set hello world,ptr指向包含字符串world的SDS。

(6)总结

综上所述,redisObject的结构与对象类型、编码、内存回收、共享对象都有关系;一个redisObject对象的大小为16字节:

4bit+4bit+24bit+4Byte+8Byte=16Byte。

4、SDS

Redis没有直接使用C字符串(即以空字符’\0’结尾的字符数组)作为默认的字符串表示,而是使用了SDS。SDS是简单动态字符串(Simple Dynamic String)的缩写。

(1)SDS结构

sds的结构如下:


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

其中,buf表示字节数组,用来存储字符串;len表示buf已使用的长度,free表示buf未使用的长度。下面是两个例子。

图片来源:《Redis设计与实现》

通过SDS的结构可以看出,buf数组的长度=free+len+1(其中1表示字符串结尾的空字符);所以,一个SDS结构占据的空间为:free所占长度+len所占长度+ buf数组的长度=4+4+free+len+1=free+len+9。

(2)SDS与C字符串的比较

SDS在C字符串的基础上加入了free和len字段,带来了很多好处:

  • 获取字符串长度:SDS是O(1),C字符串是O(n)
  • 缓冲区溢出:使用C字符串的API时,如果字符串长度增加(如strcat操作)而忘记重新分配内存,很容易造成缓冲区的溢出;而SDS由于记录了长度,相应的API在可能造成缓冲区溢出时会自动重新分配内存,杜绝了缓冲区溢出。
  • 修改字符串时内存的重分配:对于C字符串,如果要修改字符串,必须要重新分配内存(先释放再申请),因为如果没有重新分配,字符串长度增大时会造成内存缓冲区溢出,字符串长度减小时会造成内存泄露。而对于SDS,由于可以记录len和free,因此解除了字符串长度和空间数组长度之间的关联,可以在此基础上进行优化:空间预分配策略(即分配内存时比实际需要的多)使得字符串长度增大时重新分配内存的概率大大减小;惰性空间释放策略使得字符串长度减小时重新分配内存的概率大大减小。
  • 存取二进制数据:SDS可以,C字符串不可以。因为C字符串以空字符作为字符串结束的标识,而对于一些二进制文件(如图片等),内容可能包括空字符串,因此C字符串无法正确存取;而SDS以字符串长度len来作为字符串结束标识,因此没有这个问题。

此外,由于SDS中的buf仍然使用了C字符串(即以’\0’结尾),因此SDS可以使用C字符串库中的部分函数;但是需要注意的是,只有当SDS用来存储文本数据时才可以这样使用,在存储二进制数据时则不行(’\0’不一定是结尾)。

(3)SDS与C字符串的应用

Redis在存储对象时,一律使用SDS代替C字符串。例如set hello world命令,hello和world都是以SDS的形式存储的。而sadd myset member1 member2 member3命令,不论是键(”myset”),还是集合中的元素(”member1”、 ”member2”和”member3”),都是以SDS的形式存储。除了存储对象,SDS还用于存储各种缓冲区。

只有在字符串不会改变的情况下,如打印日志时,才会使用C字符串。

四、Redis的对象类型与内部编码

前面已经说过,Redis支持5种对象类型,而每种结构都有至少两种编码;这样做的好处在于:一方面接口与实现分离,当需要增加或改变内部编码时,用户使用不受影响,另一方面可以根据不同的应用场景切换内部编码,提高效率。

Redis各种对象类型支持的内部编码如下图所示(图中版本是Redis3.0,Redis后面版本中又增加了内部编码,略过不提;本章所介绍的内部编码都是基于3.0的):

圖片來源:《Redis設計與實作》

關於Redis內部編碼的轉換,都符合以下法則:編碼轉換在Redis寫入資料時完成,且轉換過程不可逆,只能從小記憶體編碼轉換到大記憶體編碼。

1、字串

(1)概況

字串是最基礎的類型,因為所有的鍵都是字串類型,且字串之外的其他幾種複雜類型的元素也是字串。

字串長度不能超過512MB。

(2)內部編碼

字串類型的內部編碼有3種,它們的應用場景如下:

  • int:8個位元組的長整型。字串值是整數時,這個值使用long整數表示。
  • embstr:<=39位元組的字串。 embstr與raw都使用redisObject和sds保存數據,差別在於,embstr的使用只分配一次記憶體空間(因此redisObject和sds是連續的),而raw需要分配兩次記憶體空間(分別為redisObject和sds分配空間)。因此與raw相比,embstr的好處在於創建時少分配一次空間,刪除時少釋放一次空間,以及物件的所有資料連在一起,尋找方便。而embstr的壞處也很明顯,如果字串的長度增加需要重新分配記憶體時,整個redisObject和sds都需要重新分配空間,因此redis中的embstr實作為唯讀。
  • raw:大於39個位元組的字串

範例如下圖所示:

##embstr和raw進行區分的長度,是39;是因為redisObject的長度是16字節,sds的長度是9 字串長度;因此當字串長度是39時,embstr的長度剛好是16 9 39=64,jemalloc剛好可以分配64位元組的記憶體單元。

(3)編碼轉換

當int資料不再是整數,或大小超過了long的範圍時,自動轉換為raw。

而對於embstr,由於其實現是唯讀的,因此在對embstr對象進行修改時,都會先轉化為raw再進行修改,因此,只要是修改embstr對象,修改後的對像一定是raw的,無論是否達到了39個位元組。範例如下圖所示:

2、清單

(1)概況

#清單(list)用來儲存多個有序的字串,每個字串稱為元素;一個列表可以儲存2^32-1個元素。 Redis中的清單支援兩端插入和彈出,並可以獲得指定位置(或範圍)的元素,可充當陣列、佇列、堆疊等。

(2)內部編碼

清單的內部編碼可以是壓縮清單(ziplist)或雙端鍊錶(linkedlist)。

雙端鍊錶:由一個list結構和多個listNode結構組成;典型結構如下圖所示:

圖片來源:《Redis設計與實作》

透過圖中可以看出,雙端鍊錶同時保存了表頭指針和錶尾指針,並且每個節點都有指向前和指向後的指針;鍊錶中保存了列表的長度;dup、free和match為節點值設定類型特定函數,所以鍊錶可以用來保存各種不同類型的值。而鍊錶中每個節點指向的是type為字串的redisObject。

 

壓縮列表:壓縮列表是Redis為了節省記憶體而開發的,是由一系列特殊編碼的

連續記憶體區塊(而不是像雙端鍊錶一樣每個節點是指標)組成的順序型資料結構;具體結構相對較複雜,略。與雙端鍊錶相比,壓縮列表可以節省記憶體空間,但是進行修改或增刪操作時,複雜度較高;因此當節點數量較少時,可以使用壓縮列表;但是節點數量多時,還是使用雙端鍊錶划算。

壓縮列表不僅用於實作列表,也用於實作哈希、有序列表;使用非常廣泛。

(3)編碼轉換

只有同時滿足下面兩個條件時,才會使用壓縮列表:列表中元素數量小於512個;列表中所有字串物件都不足64字節。如果有一個條件不滿足,則使用雙端列表;且編碼只可能由壓縮列表轉換為雙端鍊錶,反方向則不可能。

下圖展示了清單編碼轉換的特點:

其中,单个字符串不能超过64字节,是为了便于统一分配每个节点的长度;这里的64字节是指字符串的长度,不包括SDS结构,因为压缩列表使用连续、定长内存块存储字符串,不需要SDS结构指明长度。后面提到压缩列表,也会强调长度不超过64字节,原理与这里类似。

3、哈希

(1)概况

哈希(作为一种数据结构),不仅是redis对外提供的5种对象类型的一种(与字符串、列表、集合、有序结合并列),也是Redis作为Key-Value数据库所使用的数据结构。为了说明的方便,在本文后面当使用“内层的哈希”时,代表的是redis对外提供的5种对象类型的一种;使用“外层的哈希”代指Redis作为Key-Value数据库所使用的数据结构。

(2)内部编码

内层的哈希使用的内部编码可以是压缩列表(ziplist)和哈希表(hashtable)两种;Redis的外层的哈希则只使用了hashtable。

压缩列表前面已介绍。与哈希表相比,压缩列表用于元素个数少、元素长度小的场景;其优势在于集中存储,节省空间;同时,虽然对于元素的操作复杂度也由O(1)变为了O(n),但由于哈希中元素数量较少,因此操作的时间并没有明显劣势。

 

hashtable:一个hashtable由1个dict结构、2个dictht结构、1个dictEntry指针数组(称为bucket)和多个dictEntry结构组成。

正常情况下(即hashtable没有进行rehash时)各部分关系如下图所示:

 

图片改编自:《Redis设计与实现》

下面从底层向上依次介绍各个部分:

dictEntry

dictEntry结构用于保存键值对,结构定义如下:


typedef struct dictEntry{
    void *key;
    union{
        void *val;
        uint64_tu64;
        int64_ts64;
    }v;
    struct dictEntry *next;
}dictEntry;

其中,各个属性的功能如下:

  • key:键值对中的键;
  • val:键值对中的值,使用union(即共用体)实现,存储的内容既可能是一个指向值的指针,也可能是64位整型,或无符号64位整型;
  • next:指向下一个dictEntry,用于解决哈希冲突问题

在64位系统中,一个dictEntry对象占24字节(key/val/next各占8字节)。

bucket

bucket是一个数组,数组的每个元素都是指向dictEntry结构的指针。redis中bucket数组的大小计算规则如下:大于dictEntry的、最小的2^n;例如,如果有1000个dictEntry,那么bucket大小为1024;如果有1500个dictEntry,则bucket大小为2048。

dictht

dictht结构如下:


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

其中,各个属性的功能说明如下:

  • table属性是一个指针,指向bucket;
  • size属性记录了哈希表的大小,即bucket的大小;
  • used记录了已使用的dictEntry的数量;
  • sizemask属性的值总是为size-1,这个属性和哈希值一起决定一个键在table中存储的位置。

dict

一般来说,通过使用dictht和dictEntry结构,便可以实现普通哈希表的功能;但是Redis的实现中,在dictht结构的上层,还有一个dict结构。下面说明dict结构的定义及作用。

dict结构如下:

typedef struct dict{
    dictType *type;
    void *privdata;
    dictht ht[2];
    int trehashidx;
} dict;

其中,type属性和privdata属性是为了适应不同类型的键值对,用于创建多态字典。

ht属性和trehashidx属性则用于rehash,即当哈希表需要扩展或收缩时使用。ht是一个包含两个项的数组,每项都指向一个dictht结构,这也是Redis的哈希会有1个dict、2个dictht结构的原因。通常情况下,所有的数据都是存在放dict的ht[0]中,ht[1]只在rehash的时候使用。dict进行rehash操作的时候,将ht[0]中的所有数据rehash到ht[1]中。然后将ht[1]赋值给ht[0],并清空ht[1]。

因此,Redis中的哈希之所以在dictht和dictEntry结构之外还有一个dict结构,一方面是为了适应不同类型的键值对,另一方面是为了rehash。

(3)编码转换

如前所述,Redis中内层的哈希既可能使用哈希表,也可能使用压缩列表。

只有同时满足下面两个条件时,才会使用压缩列表:哈希中元素数量小于512个;哈希中所有键值对的键和值字符串长度都小于64字节。如果有一个条件不满足,则使用哈希表;且编码只可能由压缩列表转化为哈希表,反方向则不可能。

下图展示了Redis内层的哈希编码转换的特点:

4、集合

(1)概况

集合(set)与列表类似,都是用来保存多个字符串,但集合与列表有两点不同:集合中的元素是无序的,因此不能通过索引来操作元素;集合中的元素不能有重复。

一个集合中最多可以存储2^32-1个元素;除了支持常规的增删改查,Redis还支持多个集合取交集、并集、差集。

(2)内部编码

集合的内部编码可以是整数集合(intset)或哈希表(hashtable)。

哈希表前面已经讲过,这里略过不提;需要注意的是,集合在使用哈希表时,值全部被置为null。

整数集合的结构定义如下:

typedef struct intset{
    uint32_t encoding;
    uint32_t length;
    int8_t contents[];
} intset;

其中,encoding代表contents中存储内容的类型,虽然contents(存储集合中的元素)是int8_t类型,但实际上其存储的值是int16_t、int32_t或int64_t,具体的类型便是由encoding决定的;length表示元素个数。

整数集合适用于集合所有元素都是整数且集合元素数量较小的时候,与哈希表相比,整数集合的优势在于集中存储,节省空间;同时,虽然对于元素的操作复杂度也由O(1)变为了O(n),但由于集合数量较少,因此操作的时间并没有明显劣势。

(3)编码转换

只有同时满足下面两个条件时,集合才会使用整数集合:集合中元素数量小于512个;集合中所有元素都是整数值。如果有一个条件不满足,则使用哈希表;且编码只可能由整数集合转化为哈希表,反方向则不可能。

下图展示了集合编码转换的特点:

5、有序集合

(1)概况

有序集合与集合一样,元素都不能重复;但与集合不同的是,有序集合中的元素是有顺序的。与列表使用索引下标作为排序依据不同,有序集合为每个元素设置一个分数(score)作为排序依据。

(2)内部编码

有序集合的内部编码可以是压缩列表(ziplist)或跳跃表(skiplist)。ziplist在列表和哈希中都有使用,前面已经讲过,这里略过不提。

跳跃表是一种有序数据结构,通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。除了跳跃表,实现有序数据结构的另一种典型实现是平衡树;大多数情况下,跳跃表的效率可以和平衡树媲美,且跳跃表实现比平衡树简单很多,因此redis中选用跳跃表代替平衡树。跳跃表支持平均O(logN)、最坏O(N)的复杂点进行节点查找,并支持顺序操作。Redis的跳跃表实现由zskiplist和zskiplistNode两个结构组成:前者用于保存跳跃表信息(如头结点、尾节点、长度等),后者用于表示跳跃表节点。具体结构相对比较复杂,略。

(3)编码转换

只有同时满足下面两个条件时,才会使用压缩列表:有序集合中元素数量小于128个;有序集合中所有成员长度都不足64字节。如果有一个条件不满足,则使用跳跃表;且编码只可能由压缩列表转化为跳跃表,反方向则不可能。

下图展示了有序集合编码转换的特点:

五、应用举例

了解Redis的内存模型之后,下面通过几个例子说明其应用。

1、估算Redis内存使用量

要估算redis中的数据占据的内存大小,需要对redis的内存模型有比较全面的了解,包括前面介绍的hashtable、sds、redisobject、各种对象类型的编码方式等。

下面以最简单的字符串类型来进行说明。

假设有90000个键值对,每个key的长度是7个字节,每个value的长度也是7个字节(且key和value都不是整数);下面来估算这90000个键值对所占用的空间。在估算占据空间之前,首先可以判定字符串类型使用的编码方式:embstr。

90000个键值对占据的内存空间主要可以分为两部分:一部分是90000个dictEntry占据的空间;一部分是键值对所需要的bucket空间。

每个dictEntry占据的空间包括:

1)一个dictEntry,24字节,jemalloc会分配32字节的内存块

2)一个key,7字节,所以SDS(key)需要7+9=16个字节,jemalloc会分配16字节的内存块

3)一个redisObject,16字节,jemalloc会分配16字节的内存块

4)一个value,7字节,所以SDS(value)需要7+9=16个字节,jemalloc会分配16字节的内存块

5)综上,一个dictEntry需要32+16+16+16=80个字节。

bucket空间:bucket数组的大小为大于90000的最小的2^n,是131072;每个bucket元素为8字节(因为64位系统中指针大小为8字节)。

因此,可以估算出这90000个键值对占据的内存大小为:90000*80 + 131072*8 = 8248576。

下面写个程序在redis中验证一下:

public class RedisTest {

  public static Jedis jedis = new Jedis("localhost", 6379);

  public static void main(String[] args) throws Exception{
    Long m1 = Long.valueOf(getMemory());
    insertData();
    Long m2 = Long.valueOf(getMemory());
    System.out.println(m2 - m1);
  }

  public static void insertData(){
    for(int i = 10000; i < 100000; i++){
      jedis.set("aa" + i, "aa" + i); //key和value长度都是7字节,且不是整数
    }
  }

  public static String getMemory(){
    String memoryAllLine = jedis.info("memory");
    String usedMemoryLine = memoryAllLine.split("\r\n")[1];
    String memory = usedMemoryLine.substring(usedMemoryLine.indexOf(&#39;:&#39;) + 1);
    return memory;
  }
}

运行结果:8247552

理论值与结果值误差在万分之1.2,对于计算需要多少内存来说,这个精度已经足够了。之所以会存在误差,是因为在我们插入90000条数据之前redis已分配了一定的bucket空间,而这些bucket空间尚未使用。

作为对比将key和value的长度由7字节增加到8字节,则对应的SDS变为17个字节,jemalloc会分配32个字节,因此每个dictEntry占用的字节数也由80字节变为112字节。此时估算这90000个键值对占据内存大小为:90000*112 + 131072*8 = 11128576。

在redis中验证代码如下(只修改插入数据的代码):

public static void insertData(){
  for(int i = 10000; i < 100000; i++){
    jedis.set("aaa" + i, "aaa" + i); //key和value长度都是8字节,且不是整数
  }
}

运行结果:11128576;估算准确。

对于字符串类型之外的其他类型,对内存占用的估算方法是类似的,需要结合具体类型的编码方式来确定。

2、优化内存占用

了解redis的内存模型,对优化redis内存占用有很大帮助。下面介绍几种优化场景。

(1)利用jemalloc特性进行优化

上一小节所讲述的90000个键值便是一个例子。由于jemalloc分配内存时数值是不连续的,因此key/value字符串变化一个字节,可能会引起占用内存很大的变动;在设计时可以利用这一点。

例如,如果key的长度如果是8个字节,则SDS为17字节,jemalloc分配32字节;此时将key长度缩减为7个字节,则SDS为16字节,jemalloc分配16字节;则每个key所占用的空间都可以缩小一半。

(2)使用整型/长整型

如果是整型/长整型,Redis会使用int类型(8字节)存储来代替字符串,可以节省更多空间。因此在可以使用长整型/整型代替字符串的场景下,尽量使用长整型/整型。

(3)共享对象

利用共享对象,可以减少对象的创建(同时减少了redisObject的创建),节省内存空间。目前redis中的共享对象只包括10000个整数(0-9999);可以通过调整REDIS_SHARED_INTEGERS参数提高共享对象的个数;例如将REDIS_SHARED_INTEGERS调整到20000,则0-19999之间的对象都可以共享。

考虑这样一种场景:论坛网站在redis中存储了每个帖子的浏览数,而这些浏览数绝大多数分布在0-20000之间,这时候通过适当增大REDIS_SHARED_INTEGERS参数,便可以利用共享对象节省内存空间。

(4)避免过度设计

然而需要注意的是,不论是哪种优化场景,都要考虑内存空间与设计复杂度的权衡;而设计复杂度会影响到代码的复杂度、可维护性。

如果数据量较小,那么为了节省内存而使得代码的开发、维护变得更加困难并不划算;还是以前面讲到的90000个键值对为例,实际上节省的内存空间只有几MB。但是如果数据量有几千万甚至上亿,考虑内存的优化就比较必要了。

3、关注内存碎片率

内存碎片率是一个重要的参数,对redis 内存的优化有重要意义。

如果内存碎片率过高(jemalloc在1.03左右比较正常),说明内存碎片多,内存浪费严重;这时便可以考虑重启redis服务,在内存中对数据进行重排,减少内存碎片。

如果内存碎片率小于1,说明redis内存不足,部分数据使用了虚拟内存(即swap);由于虚拟内存的存取速度比物理内存差很多(2-3个数量级),此时redis的访问速度可能会变得很慢。因此必须设法增大物理内存(可以增加服务器节点数量,或提高单机内存),或减少redis中的数据。

要减少redis中的数据,除了选用合适的数据类型、利用共享对象等,还有一点是要设置合理的数据回收策略(maxmemory-policy),当内存达到一定量后,根据不同的优先级对内存进行回收。

以上是Redis記憶體模型(詳解)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:cnblogs.com。如有侵權,請聯絡admin@php.cn刪除