Redis的Hash是如何实现的

近几天看到了一篇面试被虐文,被文中的题目狠狠虐了一遍,感觉到自己的底层知识和基础知识是多么的薄弱,这几天准备填一填文中的一些坑,今天是第一个部分,原题是redis的hash是如何实现的?
很奇怪吧,这个题目从来没有想过,但是想想一下,这和Java中HashMap结构和源码问题一样,对结构的了解确实能带来很大的提升,并不只是一个造火箭的题目。下面好好描述下了解的步骤

Redis里的对象类型

送分题,redis5种数据结构?String、set、zSet、list、hash?


如果你这么回答了,那么你可能会得到一个回答:先回家等消息吧~我们有消息会通知你的。


开个玩笑,不过为什么我会这么说呢?redis的新版本中为我们带来了全新的数据类型,你可以没用到过,但是不可以不知道的。


目前版本描述redis data type的有六个文件t_hash.ct_list.ct_set.ct_string.ct_zset.ct_stream.c

  1. hash-哈希结构
  2. list-链表结构
  3. set-无需列表
  4. string-SDS字符串
  5. zset-有序列表
  6. stream-流

Redis里的数据结构

server.h文件里描述了所有的数据结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* Objects encoding. Some kind of objects like Strings and Hashes can be
* internally represented in multiple ways. The 'encoding' field of the object
* is set to one of this fields for this object. */
#define OBJ_ENCODING_RAW 0 /* Raw representation 原始数据类型,SDS字符串 */
#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 废弃类型,转为使用ZIPLIST */
#define OBJ_ENCODING_LINKEDLIST 4 /* No longer used: old list encoding. 废弃类型,转为使用QUICKLIST */
#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 分配较小的字符串,EMBSTR字符串 */
#define OBJ_ENCODING_QUICKLIST 9 /* Encoded as linked list of ziplists 压缩列表组成的双向链表 */
#define OBJ_ENCODING_STREAM 10 /* Encoded as a radix tree of listpacks 消息队列 */

对象类型和数据结构的对应关系

server.h文件里描述了所有的创建关系,散列在各个方法中,这里整理出了一份,并不代表每一种都会用到,每种对象会在不同的情况下选择不同的数据结构,比如String类型在44字符一下会采用EMBSTR方式,以用来节省内存(随版本有变化)。

  • OBJ_STRING

OBJ_ENCODING_RAW、OBJ_ENCODING_INT、OBJ_ENCODING_EMBSTR_SIZE_LIMIT

  • OBJ_LIST

OBJ_ENCODING_LINKEDLIST、OBJ_ENCODING_QUICKLIST、OBJ_ENCODING_ZIPLIST

  • OBJ_SET

OBJ_ENCODING_HT、OBJ_ENCODING_INTSET

  • OBJ_HASH

OBJ_ENCODING_ZIPLIST、OBJ_ENCODING_HT

  • OBJ_ZSET

OBJ_ENCODING_SKIPLIST、OBJ_ENCODING_ZIPLIST

  • OBJ_STREAM

OBJ_ENCODING_STREAM

ZIPLIST和HT

回到原题上,我们已经知道了OBJ_HASH由OBJ_ENCODING_ZIPLIST和OBJ_ENCODING_HT方式实现,
那这两种数据结构有什么异同呢?
OBJ_ENCODING_HT可以用一个dict来表示,可以用hashMap的思想去看,也就是我们常用的字符串,
而OBJ_ENCODING_ZIPLIST则使用了一个压缩列表来放置键值对,数据获取的时候先寻找键所在的位置,
然后用ziplistNext在下个节点里获取值。
redis目前默认创建hash结构的时候使用的是ZIPLIST编码类型,这种类型有什么优点呢?

顾名思义这种类型需要的空间更少,毕竟一个HASH类型组成,至少需要一个dict,
两个dictht。一个dictEntry指针数组和N个dictEntry。

而如果列表中的键值对过多,或者单个键或者值长度过长,就会转换为OBJ_ENCODING_HT方式,
当我了解到这些的时候,还能认为redis操作非常简单么?值得反思。