继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Redis字符串类型内部编码剖析

青春有我
关注TA
已关注
手记 1072
粉丝 205
获赞 1007

webp

Profile


概述

我们平时用 Redis都是处于用户层面,我们可能会不加思索地操作一个 key-value 对来方便地存取数据,感觉方便之至。但你知道这些数据在背后是如何存储以及编码的吗? 了解清楚了这个问题,将对我们更加高效地使用 Redis具有指导意义。本文开始我们将结合 Redis源码来逐个探讨Redis五大数据类型的内部编码机制。

  • 实验环境:Redis 4.0.10

注: 本文原载于  My Personal Blog:CodeSheep · 程序羊



Redis数据类型内部编码概况

对于 Redis的常用 5 种数据类型(String、Hash、List、Set、sorted set),每种数据类型都提供了 最少两种 内部的编码格式,而且每个数据类型内部编码方式的选择 对用户是完全透明的,Redis会根据数据量自适应地选择较优化的内部编码格式。

如果想查看某个键的内部编码格式,可以使用 OBJECT ENCODING keyname 指令来进行,比如:

127.0.0.1:6379> 
127.0.0.1:6379> set foo bar
OK127.0.0.1:6379> 
127.0.0.1:6379> object encoding foo  // 查看某个Redis键值的编码"embstr"127.0.0.1:6379> 
127.0.0.1:6379>

Redis 的每个键值内部都是使用一个名字叫做 redisObject 这个 C语言结构体保存的,其代码如下:

webp

redisObject 结构体

解释如下:

  • type:表示键值的数据类型,包括 String、List、Set、ZSet、Hash

  • encoding:表示键值的内部编码方式,从 Redis源码看目前取值有如下几种:

#define OBJ_ENCODING_RAW 0        /* Raw representation */#define OBJ_ENCODING_INT 1        /* Encoded as integer */#define OBJ_ENCODING_HT 2         /* Encoded as hash table */#define OBJ_ENCODING_ZIPMAP 3     /* Encoded as zipmap */#define OBJ_ENCODING_LINKEDLIST 4 /* No longer used: old list encoding. */#define OBJ_ENCODING_ZIPLIST 5    /* Encoded as ziplist */#define OBJ_ENCODING_INTSET 6     /* Encoded as intset */#define OBJ_ENCODING_SKIPLIST 7   /* Encoded as skiplist */#define OBJ_ENCODING_EMBSTR 8     /* Embedded sds string encoding */#define OBJ_ENCODING_QUICKLIST 9  /* Encoded as linked list of ziplists */
  • refcount:表示该键值被引用的数量,即一个键值可被多个键引用

本文我们就从 Redis最基本的 String类型的内部编码开始探讨!



String类型的内部编码情况

字符串是 Redis最基本的数据类型,Redis 中字符串对象的编码可以是 intraw 或者 embstr 中的某一种,分别介绍如下:

  • int 编码:保存long 型的64位有符号整数

  • embstr 编码:保存长度小于44字节的字符串

  • raw 编码:保存长度大于44字节的字符串

我们不妨来做个实验实际看一下:

webp

String的各种内部编码格式

实际情况就是 Redis 内部会根据用户给的不同键值而使用不同的编码格式,而这一切对用户完全透明!

Redis 是使用 SDS(“简单动态字符串”)这个结构体来存储字符串,代码里定义了 5种 SDS结构体:

struct __attribute__ ((__packed__)) sdshdr5 {
    unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
    char buf[];
};struct __attribute__ ((__packed__)) sdshdr8 {
    uint8_t len; /* used */
    uint8_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};struct __attribute__ ((__packed__)) sdshdr16 {
    uint16_t len; /* used */
    uint16_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};struct __attribute__ ((__packed__)) sdshdr32 {
    uint32_t len; /* used */
    uint32_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};struct __attribute__ ((__packed__)) sdshdr64 {
    uint64_t len; /* used */
    uint64_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};

可以看出,除了结构体字段数据类型的不同,其字段含义相差无几,其中:

  • len:字符串的长度(实际使用的长度)

  • alloc:分配内存的大小

  • flags:标志位,低三位表示类型,其余五位未使用

  • buf:字符数组

了解了这些基本的数据结构以后,我们就来看看上面例子中:

  • set foo 123

  • set foo abc

  • set foo abcdefghijklmnopqrstuvwxyzabcdeffasdffsdaadsx

这三种情形下 Redis 内部到底是怎么存数据的!



作者:CodeSheep
链接:https://www.jianshu.com/p/666452a22855


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP