Redis中所有数据结构都是以唯一key字符串作为名称,然后通过这个唯一key获取相应的value,不同的数据结构类型就是指的value的类型。本文只简单介绍基础数据结构,对应数据结构的深入解析会在后面文章中叙述。

String

字符串是一种十分常见的redis数据存储结构,通常可以将数据序列化成json存储在redis中,使用时伴随着一次反序列化的过程。

Redis中的字符串是动态字符串,是可以修改的,有点类似于Java中的ArrayList,采用预分配冗余空间的方式进行存储,所获取的空间要大于实际使用的内存空间,当字符串大小小于1M的时候都是加倍现有空间,当大于1M的时候每次增加1M,同时字符串最大的容量为512M。

在底层采用的是一种Simple Dynamic String(简称SDS)的数据结构:

struct SDS<T>{
	T capacity;//数组容量
	T len;//数组长度
	byte flags;//特殊标志位
	byte[] content;//数组内容
}

在Redis中存储字符串有两种形式,一种是embstr,一种是raw。当字符串长度不超过44的时候使用embstr存储,否则使用row存储。现在分析一下为什么44会是临界点,且有什么区别?
首先我们看一下Redis对象头的结构:

struct RedisObject {
	int4 type; // 4bits
	int4 encoding; // 4bits
	int24 lru; // 24bits
	int32 refcount; // 4bytes
	void *ptr; // 8bytes,64-bit system
} robj;

所有的Redis对象都会使用这个结构,通过计算我们可以知道存储一个对象头需要的内存空间为(4+4+24)/8+4+8=16个字节。
当字符串较小的时候SDS的结构如下:

struct SDS {
	int8 capacity; // 1byte
	int8 len; // 1byte
	int8 flags; // 1byte
	byte[] content; // 内联数组,长度为 capacity
}

计算可知最小为3个字节,所以分配一个字符串最少需要16+3=19个字节。

redis数据结构json redisstring数据结构_字符串


如上图,embstr 这种存储结构对象头和SDS是连续存储的,而raw不是连续的。内存分配器 jemalloc/tcmalloc在分配内存的时候都是2,4,6,8这样进行分配的,当字符串占用空间超过64个字节的时候,就认为这是一个大字符串了就不再使用embstr这种而是选择raw存储。这样计算64-19=45个字节。而在Redis中的SDS结构,content都是以\0结束作为结尾字符串(就像C++以null为结束),所以会占用一个字节,所以embstr最大存储字符串长度为44个字节。

List

Redis中的List相当于Java中的LinkedList,它是链表的数据结构,这也就导致它的新增和删除操作非常快,但是查询效率非常低,当列表的最后一个元素弹出后,列表自动删除,内存被回收。

Redis的List通常被用作异步队列来使用, 将任务结构体序列化成字符串放入Llist中,然后另一个线程读取处理。内部实现其实是比较复杂的,是被称之为quickList的结构来完成的。

Hash

Redis中的字典相当于Java中的HashMap,是无序字典,由数组+链表实现的,碰撞的元素有链表连接起来。不同的是Redis字典的value值只能是字符串,此外他们的rehash的方式是不同的。JDK1.7采用的是全rehash,JDK1.8采用的是部分rehash,通过高位与运算来计算是否需要移动。而Redis中采用的是渐进式的rehash策略。

它是在rehash的时候保留了新旧两种hash结构,查询时会查询到两种结构,然后在后续的定时任务和hash子指令中,循序渐进的将旧hash迁移到新hash中,当hash最后一个元素迁移完成的时候,原结构删除。

Set

Redis中的Set是一个无序集合,相当有Java中的HashSet,内部实现相当于一个特殊的字典,value都是null,当集合的最后一个元素移除的时候,原结构删除,内存被回收。

zset

zset是Redis中最为有特色的数据结构了,它类似于Java中SortedSet和HashMap的结合体,一方面保证了内部key值得唯一性,同时可以给每一个key赋予一个score值,作为排序的依据,内部实现基于跳跃列表的数据结构。