Redis 是一种键值( Key-Value )数据库。相对于关系型数据库(比如MySQL),Redis也被叫作 非关系型 数据库。
Redis中,键的数据类型是字符串,值的数据类型有很多,常用的分别是字符串、列表、字典、集合、有序集合。
“字符串(string)"这种数据类型非常简单,对应到数据结构里,就是字符串。
列表这种数据类型支持存储一组数据。其对应两种实现,一种是压缩列表(ziplist),另一种是双向循环链表。
压缩列表,并不是基础数据结构,是Redis自己设计的一种数据存储结构。它有点类似数组,通过一片连续的内存,来存储数据。它跟数组不同的一点是,它允许存储的 数据大小不同 。存储结构如图。
压缩列表中的“压缩”如何理解?
Redis的双向链表实现方式,非常值得借鉴。它额外定义一个list结构体,来组织链表的首、尾指针,还有长度等信息。在使用的时候非常方便。
// 以下是 C 语言代码,因为 Redis 是用 C 语言实现的。
typedef struct listnode
{
struct listNode *prev;
struct listNode *next;
void *value;
} listNode;
typedef struct list
{
listNode *head;
listNode *tail;
unsigned long len;
// .... 省略其他定义
} list;
字典类型用来存储一组数据对。每个数据对又包含键值两部分。字典类型也有两种实现方式。
同样,当存储数据量比较小的情况下,Redis 才使用压缩列表来实现字典类型。需要满足两个条件:
不能同时满足上面两个条件,Redis 就使用散列表来实现字典类型。
扩容缩容要做大量的数据搬移和哈希值的重新计算,所以比较耗时。Redis 使用渐进式扩容缩容策略,将数据搬移分批进行,避免大量数据一次性搬移导致的服务停顿。
集合用来存储一组不重复的数据。有两种实现方法,一是基于有序数组,另一种基于散列表。
有序集合用来存储一组数据,并且每个数据会附带一个得分。通过得分大小,将数据组织成跳表这样的数据结构,以支持快速地按照得分值、得分区间获取数据。
当数据量比较小时,Redis会用压缩列表来实现有序集合。前提有两个:
尽管Redis经常会被用作内存数据库,但它也支持数据落盘,当机器断电时,存储在Redis中的数据不会丢。重启后,Redis 只需再将存储在硬盘中的数据,重新读到内存,就可以继续工作了。
“持久化”,可以笼统地可以理解为“存储到磁盘"。如何持久化到硬盘?
压缩列表(可以看作一种特殊的数组)、有序数组、链表、散列表、跳表。实际上,Redis就是这些常用数据结构的封装。
夯实基础很重要。基础很好,不但能知其然,还能知其所以然,从而真正理解Redis作者设计的动机。不但能有助于我们理解所用的开源软件,还能为我们自己创新添砖加瓦。