降低Redis內(nèi)存的占用

1.原理

短結構

Redis為列表、集合、散列和有序集合提供了一組配置選項,讓Redis以跟節(jié)約內(nèi)存的方式存儲這些變量

阿里云主從版提供的選項(全是和短結構有關的,ziplist可以說是除了hash字典之外最常用的結構)

image

在列表、散列、有序集合的長度較短或者體積較小的時候,redis會使用壓縮列表(ziplist)代替通常情況下這三種結構的底層實現(xiàn)方式作為這幾種結構的底層實現(xiàn)。

配置:

  • entries:最大元素數(shù)量
  • value:壓縮列表的每個節(jié)點的最大體積

當任一條件被突破時,Redis就會把底層實現(xiàn)改為通常的結構,而且當數(shù)據(jù)被刪除之后也不會再次使用ziplist

體積較小的集合使用intset來作為底層實現(xiàn)

條件:

  • 整數(shù)集合包含的所有成員都可以被解釋為十進制數(shù)
  • 這些整數(shù)又處于平臺的有符號整數(shù)范圍之內(nèi)
  • 集合成員數(shù)量足夠少

短結構是以上幾種結構的緊湊表示方式

減少鍵的長度也是應該注意的一個點。

短結構ziplist

Redis中ziplist的定義(序列化字符串)和接口

unsigned char *ziplistNew(void);
unsigned char *ziplistPush(unsigned char *zl, unsigned char *s, unsigned int slen, int where);
unsigned char *ziplistIndex(unsigned char *zl, int index);
unsigned char *ziplistNext(unsigned char *zl, unsigned char *p);
unsigned char *ziplistPrev(unsigned char *zl, unsigned char *p);
unsigned int ziplistGet(unsigned char *p, unsigned char **sval, unsigned int *slen, long long *lval);
unsigned char *ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen);
unsigned char *ziplistDelete(unsigned char *zl, unsigned char **p);
unsigned char *ziplistDeleteRange(unsigned char *zl, unsigned int index, unsigned int num);
unsigned int ziplistCompare(unsigned char *p, unsigned char *s, unsigned int slen);
unsigned char *ziplistFind(unsigned char *p, unsigned char *vstr, unsigned int vlen, unsigned int skip);
unsigned int ziplistLen(unsigned char *zl);
size_t ziplistBlobLen(unsigned char *zl);

壓縮列表由節(jié)點序列和四個控制位組成。

影響性能的因素:每次讀取數(shù)據(jù)時都需要對進行解碼,寫入也需要進行局部的重新編碼,并且有可能觸發(fā)連鎖更新,但是這些操作在對小列表進行處理時對性能的損耗并不很明顯(平均O(N))。

短結構intset

Redis中整數(shù)集合的定義和接口,可見其最底層實現(xiàn)就是一個有序的C語言數(shù)組。

typedef struct intset {
    
    // 編碼方式
    uint32_t encoding;

    // 集合包含的元素數(shù)量
    uint32_t length;

    // 保存元素的數(shù)組
    int8_t contents[];

} intset;

intset *intsetNew(void);
intset *intsetAdd(intset *is, int64_t value, uint8_t *success);
intset *intsetRemove(intset *is, int64_t value, int *success);
uint8_t intsetFind(intset *is, int64_t value);
int64_t intsetRandom(intset *is);
uint8_t intsetGet(intset *is, uint32_t pos, int64_t *value);
uint32_t intsetLen(intset *is);
size_t intsetBlobLen(intset *is);

影響性能的因素:對整數(shù)集合進行插入或者刪除操作時需要進行數(shù)據(jù)移動,并有可能引起底層升級。

隨著短結構的體積變得越來越大,操作這些結構的速度也會變得越來越慢,所以可以引入分片技術以追求性能和空間之前的平衡。

2.分片結構

  • 特別適合分片的場景:執(zhí)行分片版命令的時候只需要訪問指定分片即可。
  • 特別不適合分片的場景:需要訪問指定分片,完事還得進行歸并聚合的操作。
  • 可以通過分片提升性能的場景:搜索
  • 替代分片的一種場景:只對前n位和后n位操作的有序集合(用單獨維護的兩個最大有序集合和最小有序集合實現(xiàn))。
  1. 對列表進行分片

  2. 有序集合進行分片:
    有序集合的大部分操作都屬于特別不適合分片的場景。

  3. 分片式散列:將字符串鍵存儲到散列里面也可以明顯降低內(nèi)存占用

    1. 實現(xiàn)分片方法即可
asbtract class ShardHlist { 
/*
** $base 基礎散列 
** $key 將要保存到分片散列里面的鍵
** $total_elements 預計元素總量
** $shard_size 預期分片大小
*/
abstract public function shard_key($base, $key, $total_elements, $shard_size);
abstract public function shard_hset($shard_key, key, value, $total_elements, $shard_size);
abstract public function shard_hset($shard_key, key, $total_elements, $shard_size);
}

如果不是輔助緩存那么不應該隨便更改$total_elements 和 $shard_size,一旦不得不更改需要resharding方法將數(shù)據(jù)從舊的分片遷移到新的分片

  1. 分片集合
    shard_add、shard_rem

3.二進制位和字節(jié)打包用來進一步降低存儲空間

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容