概要概要
通常、Redisはユーザーレベルで使用しますが、データに簡単にアクセスするために、キーと値のペアを考えずに操作する場合があります。非常に便利です。しかし、これらのデータが舞台裏でどのように保存およびエンコードされているか知っていますか?この問題を明確に理解することは、Redisをより効率的に使用するための指針となる重要性を持っています。この記事の冒頭で、Redisのソースコードを組み合わせて、Redisの5つのデータ型の内部コーディングメカニズムについて1つずつ説明します。
実験環境:Redis 4.0.10
注: この記事は、My public account CodeSheepで最初に公開されました。以下の注意を長押しするかスキャンして、サブスクライブできます↓↓↓
Redisデータ型の内部コーディングの概要
Redisの5つの一般的に使用される データ型(String、Hash、List、Set、sorted set)の場合、各データ型は少なくとも2つの 内部エンコード形式を提供し、各データ型の 内部エンコードの選択 は完全にユーザー向けです。Transparent、Redisデータ量に応じて、より最適化された内部エンコーディング形式を適応的に選択します。
キーの内部エンコード形式を表示する場合は、次のようなコマンドを使用できます OBJECT ENCODING keyname
。
127.0.0.1:6379> 127.0.0.1:6379> set foo bar OK 127.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
の各キー値は、 このC言語構造と呼ばれる名前で内部的に redisObject
保存され、コードは次のとおりです。
説明は次のとおりです。
type
:String、List、Set、ZSet、Hashなどのキー値のデータ型を表しますencoding
:キー値の内部エンコード方法を表します。Redisソースコードから、現在の値は次のとおりです。
#define OBJ_ENCODING_RAW 0 / *生の表現* / #define OBJ_ENCODING_INT 1 / *整数としてエンコード* / #define OBJ_ENCODING_HT 2 / *ハッシュテーブルとしてエンコード* / #define OBJ_ENCODING_ZIPMAP 3 / * zipmapとしてエンコード* / #define OB使用されなくなった:古いリストエンコーディング。* / の#define OBJ_ENCODING_ZIPLIST 5 / *エンコードziplist * /など の#define OBJ_ENCODING_INTSET 6 / *エンコードINTSETなど* / の#define OBJ_ENCODING_SKIPLIST 7 / *エンコードskiplistなど* / * /コードの#define OBJ_ENCODING_EMBSTR 8 / * SDS埋め込み文字列 の#define OBJ_ENCODING_QUICKLISTを9 / * ziplistのリンクリストとしてエンコードされます* /
refcount
:キー値への参照数を示します。つまり、キー値は複数のキーで参照できます。
この記事では、Redisの最も基本的な文字列型の内部コーディングから始めます!
文字列型の内部エンコーディング
字符串是 Redis最基本的数据类型,Redis 中字符串对象的编码可以是 int
, raw
或者 embstr
中的某一种,分别介绍如下:
int 编码:保存long 型的64位有符号整数
embstr 编码:保存长度小于44字节的字符串
raw 编码:保存长度大于44字节的字符串
我们不妨来做个实验实际看一下:
实际情况就是 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 内部到底是怎么存数据的!
INT 编码格式
命令示例: setfoo123
当字符串键值的内容可以用一个 64位有符号整形 来表示时,Redis会将键值转化为 long型来进行存储,此时即对应 OBJ_ENCODING_INT
编码类型。
OBJ_ENCODING_INT
编码类型内部的内存结构可以形象地表示如下:
而且 Redis 启动时会预先建立 10000 个分别存储 0~9999 的 redisObject 变量作为共享对象,这就意味着如果 set字符串的键值在 0~10000 之间的话,则可以 直接指向共享对象 而不需要再建立新对象,此时键值不占空间!
因此,当执行如下指令时:
set key1 100set key2 100
其实 key1 和 key2 这两个键值都直接引用了一个 Redis 预先已建立好的共享 redisObject 对象,就像下面这样:
源码之前,了无秘密,我们再对照下面的源码,来理解一下上述过程
EMBSTR 编码格式
命令示例: setfoo abc
Redis 在保存长度小于 44 字节的字符串时会采用 OBJ_ENCODING_EMBSTR
编码方式,口说无凭,我们来瞅瞅源码:
从上述代码中很容易看出,对于长度小于 44的字符串,Redis 对键值采用 OBJ_ENCODING_EMBSTR
方式,EMBSTR 顾名思义即:embedded string,表示嵌入式的String。从内存结构上来讲 即字符串 sds结构体与其对应的 redisObject 对象分配在 同一块连续的内存空间,这就仿佛字符串 sds 嵌入在 redisObject 对象之中一样,这一切从下面的代码即可清楚地看到:
因此,对于指令 setfoo abc
所设置的键值,其内存结构示意图如下:
RAW 编码格式
指令示例: setfoo abcdefghijklmnopqrstuvwxyzabcdeffasdffsdaadsx
キー値ときだけ命令の例のように、文字列が ある非常に長い文字列とより長大きく 44、Redisのは、鍵の内部エンコーディング変化する OBJ_ENCODING_RAW
フォーマット。 相違点上記 OBJ_ENCODING_EMBSTR
符号化は、動的な文字があるということですこの時点で、文字列sdsのメモリとそれが依存するredisObjectのメモリは連続してい ません 。上記のコマンドを例にとると、そのキー値のメモリ構造は次のとおりです。
これで、最も基本的な文字列データ型の内部コーディングは終わりです。それでも、非常に簡単に理解できます。
後で、Redisのハッシュデータ型の内部エンコード形式の分析を続けます。
追記
能力が限られているため、間違いや不正があった場合は、批判して訂正し、一緒に学び、コミュニケーションしてください!
私の個人ブログ:www.codesheep.cn
興味がある場合は、コンテナ化とマイクロサービスに関する著者の記事を読むこともできます。
公開番号CodeSheepを作成するための、より実用的で、読みやすく、再現可能な元の記事については、⬇️⬇️⬇️を購読してください。