1.原理
短結構
Redis為列表、集合、散列和有序集合提供了一組配置選項,讓Redis以跟節(jié)約內(nèi)存的方式存儲這些變量
阿里云主從版提供的選項(全是和短結構有關的,ziplist可以說是除了hash字典之外最常用的結構)
在列表、散列、有序集合的長度較短或者體積較小的時候,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))。
對列表進行分片
有序集合進行分片:
有序集合的大部分操作都屬于特別不適合分片的場景。-
分片式散列:將字符串鍵存儲到散列里面也可以明顯降低內(nèi)存占用
- 實現(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ù)從舊的分片遷移到新的分片
- 分片集合
shard_add、shard_rem