RedisObject
RedisObject 的结构:
typedef struct redisObject {
//类型
unsigned type:4;
// 编码
unsigned encoding:4;
// 指向数据结构的指针
void *ptr;
// 高 16 位用来记录访问时间,用于 LRU
// 低 8 位用来记录访问频率,用于 LFU
unsigned lru:24;
// 引用计数
int refcount;
// 数据指针
void *ptr;
// ...
} robj;
对象的类型
type 属性指定类型:
-------------------------------------------
类型常量 | 对象的名称 | 调用 type 命令
-------------------------------------------
REDIS_STRING | 字符串对象 | "string"
REDIS_LIST | 列表对象 | "list"
REDIS_HASH | 哈希对象 | "hash"
REDIS_SET | 集合对象 | "set"
REDIS_ZSET | 有序集合对象 | "zset"
编码和底层实现
encoding 属性指定编码:
--------------------------------------------------------------------------------------
类型常量 | 编码常量 | 数据结构 | object encoding
--------------------------------------------------------------------------------------
REDIS_STRING | REDIS_ENCODING_INT | long 类型的整数 | "int"
REDIS_STRING | REDIS_ENCODING_EMBSTR | embstr 编码的简单动态字符串 | "embstr"
REDIS_STRING | REDIS_ENCODING_RAW | 简单动态字符串 | "raw"
REDIS_LIST | REDIS_ENCODING_ZIPLIST | 压缩列表 | "ziplist" [version 3.2 之前]
REDIS_LIST | REDIS_ENCODING_LINKEDLIST | 双端列表 | "linkedlist"[version 3.2 之前]
REDIS_LIST | REDIS_ENCODING_QUICKLIST | 快速列表 | "quicklist" [version 3.2 之后]
REDIS_HASH | REDIS_ENCODING_ZIPLIST | 压缩列表 | "ziplist"
REDIS_HASH | REDIS_ENCODING_HT | 字典 | "hashtable"
REDIS_SET | REDIS_ENCODING_INTSET | 整数集合 | "intset"
REDIS_SET | REDIS_ENCODING_HT | 字典 | "hashtable"
REDIS_ZSET | REDIS_ENCODING_ZIPLIST | 压缩列表 | "ziplist"
REDIS_ZSET | REDIS_ENCODING_SKIPLIST | 跳跃表 | "skiplist"
RedisObject 的优势
- 在执行命令之前,根据对象的类型即可判断一个对象是否可以执行给定的命令
- 在不同场景采用不同数据结构实现,优化对象在不同场景下的使用效率
- 基于引用计数技术的内存回收机制和对象共享机制
- 记录访问时间,计算键的空转时间,如果服务器打开了
maxmemory
选项,并且服务器用于回收内存的算法为volatile-lru
或者allkeys-lru
,那么当服务器占用的内存数超过了maxmemory
选项所设置的上限值时,空转时长较高的那部分键会优先被服务器释放,从而回收内存
字符串对象
- int
8 个字节的长整数
- embstr
<=39
字节的字符串
- raw
>39
字节的字符串
embstr
与 raw
的区别:
- 创建和释放内存时,
raw
需要调用两次函数,而embstr
只需要调用一次函数 embstr
编码的字符串对象的所有数据都保存在一块连续的内存里面,能够更好的利用缓存的优势- 当字符串的长度增加需要重新分配内存时,
embstr
整体重新分配空间,所以embstr
实现为只读
列表对象
- 压缩列表
- 列表对象保存的所有字符串元素的长度都小于 64 字节
- 列表对象保存的元素数量小于 512 个
- 双端列表
不满足条件的列表对象采用双端列表
**注意:在 Redis 3.2 之后,列表对象的数据结构采用 quicklist**
quicklist
是一个节点为压缩列表的双端列表,设计的原因:
- 双端列表的优缺点
优点:操作方便
缺点:存储前后指针,内存开销较大,且地址不联系,容易产生碎片
- 压缩列表的优缺点:
优点:整块连续内存,存储效率高
缺点:操作不方便,每次数据变动都会引发内存的分配和数据拷贝
结合双端列表和压缩列表的 quicklist
就产生了,随之也引发了新问题:
压缩列表的长度
长度过短,退化为双端列表
长度过长,退化为压缩列表
redis 提供了可进行配置的参数:
# Lists are also encoded in a special way to save a lot of space.
# The number of entries allowed per internal list node can be specified
# as a fixed maximum size or a maximum number of elements.
# For a fixed maximum size, use -5 through -1, meaning:
# -5: max size: 64 Kb <-- not recommended for normal workloads
# -4: max size: 32 Kb <-- not recommended
# -3: max size: 16 Kb <-- probably not recommended
# -2: max size: 8 Kb <-- good
# -1: max size: 4 Kb <-- good
# Positive numbers mean store up to _exactly_ that number of elements
# per list node.
# The highest performing option is usually -2 (8 Kb size) or -1 (4 Kb size),
# but if your use case is unique, adjust the settings as necessary.
list-max-ziplist-size -2
当列表很长,两端的数据被高频访问,中间的数据被低频访问的场景
redis 提供了可进行配置的参数,可以将中间的数据进行压缩:
# Lists may also be compressed.
# Compress depth is the number of quicklist ziplist nodes from *each* side of
# the list to *exclude* from compression. The head and tail of the list
# are always uncompressed for fast push/pop operations. Settings are:
# 0: disable all list compression
# 1: depth 1 means "don't start compressing until after 1 node into the list,
# going from either the head or tail"
# So: [head]->node->node->...->node->[tail]
# [head], [tail] will always be uncompressed; inner nodes will compress.
# 2: [head]->[next]->node->node->...->node->[prev]->[tail]
# 2 here means: don't compress head or head->next or tail->prev or tail,
# but compress all nodes between them.
# 3: [head]->[next]->[next]->node->node->...->node->[prev]->[prev]->[tail]
# etc.
list-compress-depth 0
哈希对象
- 压缩列表
- 哈希对象保存的所有字符串元素的长度都小于 64 字节
- 哈希对象保存的元素数量小于 512 个
这两个上限值可进行修改:
# Hashes are encoded using a memory efficient data structure when they have a
# small number of entries, and the biggest entry does not exceed a given
# threshold. These thresholds can be configured using the following directives.
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
- 字典
不满足条件的哈希对象采用字典
Redis
使用 MurmurHash2
这种运行速度快、随机性好的哈希算法作为哈希函数,对于哈希冲突问题,Redis
采用链表法来解决。
集合对象
- 整数集合
- 集合对象保存的所有元素都是整数值
- 集合对象保存的元素数量小于 512 个
第二个条件的上限值可进行修改:
# Sets have a special encoding in just one case: when a set is composed
# of just strings that happen to be integers in radix 10 in the range
# of 64 bit signed integers.
# The following configuration setting sets the limit in the size of the
# set in order to use this special memory saving encoding.
set-max-intset-entries 512
- 字典
不满足条件的集合对象采用字典
有序集合对象
- 压缩列表
- 有序集合对象保存的所有字符串元素的长度都小于 64 字节
- 有序集合对象保存的元素数量小于 128 个
这两个上限值可进行修改:
# Similarly to hashes and lists, sorted sets are also specially encoded in
# order to save a lot of space. This encoding is only used when the length and
# elements of a sorted set are below the following limits:
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
skiplist
不满足条件的有序集合对象采用 skiplist
编码,其底层使用 zset
,其中同时包含一个字典和跳跃表