管道
客户端和Redis使用TCP协议连接。不论是客户端向Redis发送命令还是Redis向客户端返回命令执行的结果,都需要经过网络传输,这两部分的总耗时称为往返时延。
在执行多条命令时。每条命令都需要等待上一条命令执行完毕(即收到Redis的返回结果)才能执行,即使命令不需要上一条命令的执行结果。
Redis的底层通信协议对管道(pipelining)提供了支持。通过管道可以一次性发送多条命令并在执行完后一次性将结果返回,当一组命令中每条命令都不依赖于之前命令的执行结果时就可以将这组命令通过管道一起发出。管道通过减少客户端与Redis的通信次数来实现降低往返时延累计值的目的。
节省空间
减少内存占用来控制成本
- 精简键名和键值
- 内部编码优化:Redis为每种数据类型都提供了两种内部编码方式,以散列类型为例,散列类型是通过散列表实现的,这样就可以实现O(1)时间复杂度的查找和赋值操作,然而当键中元素很少时,O(1)的操作并不会比O(n)有明显的性能提高,所以这种情况下Redis会采用一种更为紧凑但性能稍差(获取元素的时间复杂度为O(n))的内部编码方式。内部编码方式对于开发者来说是透明的,Redis会根据实际情况自动调整。如果想查看一个键的内部编码方式可以使用OBJECT ENCODING命令,如:
redis> SET foo bar
OK
redis> OBJECT ENCODING foo
"raw"
Redis的每个键值都是使用一个redisObject的结构体保存的,redisObject的定义如下:
typedefstruct redisObject{
unsigned type:4;
unsigned notused:2;
unsigned encoding:4;
unsigned lru:22;
int refcount;
void* ptr;
} robj;
type字段表示的是键值的数据类型,取值如下:
#define REDIS_STRING 0
#define REDIS_LIST 1
#define REDIS_SET 2
#define REDIS_ZSET 3
#define REDIS_HASH 4
encoding字段表示的就是Redis键值的内部编码方式,取值:
#define REDIS_ENCODING_RAW 0 /*Raw representation*/
#define REDIS_ENCODING_INT 1 /*Encoded as integer*/
#define REDIS_ENCODING_HT 2 /*Encoded as hash table*/
#define REDIS_ENCODING_ZIPMAP 3 /*Encoded as zipmap*/
#define REDIS_ENCODING_LINKEDLIST 4 /*Encoded as regular linkedlist*/
#define REDIS_ENCODING_ZIPLIST 5 /*Encoded as ziplist*/
#define REDIS_ENCODING_INTSET 6 /*Encoded as intset*/
#define REDIS_ENCODING_SKIPLIST 7 /*Encoded as skiplist*/
|
数据类型 |
内部编码方式 |
OBJECT ENCODING命令结果 |
|
字符串类型 |
REDIS_ENCODING_RAW REDIS_ENCODING_INT |
"raw" "int" |
|
散列类型 |
REDIS_ENCODING_HT REDIS_ENCODING_ZIPLIST |
"hashtable" "ziplist" |
|
列表类型 |
REDIS_ENCODING_LINKEDLIST REDIS_ENCODING_ZIPLIST |
"linkedlist" "ziplist" |
|
集合类型 |
REDIS_ENCODING_HT REDIS_ENCODING_INTSET |
"hashtable" "intset" |
|
有序集合类型 |
REDIS_ENCODING_SKIPLIST REDIS_ENCODING_ZIPLIST |
"skiplist" "ziplist" |
1、字符串类型
Redis使用一个sdshdr类型的变量来存储字符串,而redisObject的ptr字段指向的是该变量的地址。
struct sdshdr{
int len;
int free;
char buf[];
};
len表示字符串的长度,free字段表示buf中的剩余空间,buf存储字符串的内容。
当键值内容可以用一个64位有符号整数表示时,Redis会将键值转换成long类型来存储。
Redis启动后会预先建立10000个分别存储从0到9999这些数字的redisObject类型变量作为共享对象。
当通过配置文件参数maxmemory设置了Redis可用的最大空间大小时,Redis不会使用共享对象。
2、散列类型
在配置文件中可以定义使用REDIS_ENCODING_ZIPLIST方式编码散列类型的时机:
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
当散列类型键的字段个数少于hash-max-ziplist-entries 参数值,且每个字段名和字段值的长度都小于hash-max-ziplist-value参数值时,Redis就会使用REDIS_ENCODING_ZIPLIST来存储该键,否则使用REDIS_ENCODING_HT。
REDIS_ENCODING_ZIPLIST的 编码结构:
|
zlbytes |
|
|
zltail |
|
|
zllen |
|
|
元素1 |
|
|
元素2 |
|
|
... |
|
|
zlend |
|
其中元素的结构如下:
|
前一个元素的大小 |
|
|
当前元素的编码类型 |
|
|
当前元素的大小 |
|
|
当前元素的类型 |
|
使用REDIS_ENCODING_ZIPLIST编码存储散列类型的内存结构:
|
zlbytes |
|
|
zltail |
|
|
zllen |
|
|
字段1 |
|
|
字段值1 |
|
|
字段2 |
|
|
字段值2 |
|
|
... |
|
|
zlend |
|
3、列表类型
在配置文件中可以定义使用REDIS_ENCODING_ZIPLIST编码方式的时机:
list-max_ziplist-entries512
list-max-ziplist-value 64
4、集合类型
当集合中的所有元素都是整数且元素的个数小于配置文件中的set-max-intset-entries参数指定值(默认512)时Redis会使用REDIS_ENCODING_INTSET编码存储该集合,否则使用REDIS_ENCODING_HT来存储。
typedef struct intset{
uint32_t encoding;
uint32_t length;
int8_t contents[];
} intset;
5、有序集合类型
配置文件中可以定义使用REDIS_ENCODING_ZIPLIST方式编码的时机:
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
当编码方式为REDIS_ENCODING_SKIPLIST时,Redis使用散列表和跳跃列表两种数据结构来存储有序集合类型键值。散列表用来存储元素值与元素分数的映射关系来实现O(1)时间复杂度的ZSCORE等命令。跳跃列表用来存储元素的分数及其到元素值的映射以实现排序的功能。
使用REDIS_ENCODING_ZIPLIST编码有序集合时按照“元素1的值,元素1的分数,元素2的值,元素2的分数”的顺序排列,并且分数是有序的。
本文详细介绍了Redis管道机制如何通过减少与客户端的通信次数来降低往返时延,同时阐述了Redis在处理不同数据类型时的内部编码优化策略,包括节省空间、减少内存占用以及精简键名的方法。
2595

被折叠的 条评论
为什么被折叠?



