Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

My Blog

从此烟雨落金城,一人撑伞两人行。

redis 数据类型

redis简介

Remote Dictionary Server(即远程字典服务),它是一个基于内存实现的键值型非关系(NoSQL)数据库。

具有以下特点:

  • Redis 不仅可以将数据完全保存在内存中,还可以通过磁盘实现数据的持久存储;
  • Redis 支持丰富的数据类型,包括 string、list、set、zset、hash 等多种数据类型,因此它也被称为“数据结构服务器”;
  • Redis 支持主从同步,即 master-slave 主从复制模式。数据可以从主服务器向任意数量的从服务器上同步,有效地保证数据的安全性;
  • Redis 支持多种编程语言,包括 C、C++、Python、Java、PHP、Ruby、Lua 等语言。

Redis体系架构主要分为两个部分:

  • Redis服务端:能够把数据存储到内存中,并且起到管理数据的作用。
  • Redis客户端:连接服务端。

启动redis服务端:

1
redis-server

启动redis客户端:

1
2
redis-cli -h [ip] -p [port] -a [password] 
redis-cli

数据类型是 Value(值) 的数据类型,而非 key。

key 的类型对应着 value 的类型,同样也有五种(string、list、hash、set、zset)。

在 key 的取值上,使用“见名知意”的字符串格式,便于理解 key 的含义。

定时策略:Redis 会把每个设置了过期时间的 key 存放到一个独立的字典中,并且会定时遍历这个字典来删除到期的 key。

惰性策略:当客户端访问这个 key 的时候,Redis 对 key 的过期时间进行检查,如果过期了就立即删除。

Redis 使用两种方式相结合的方法来处理过去的 key。

string 字符串

string 结构

String 是 Redis 最基本的数据类型。字符串是一组字节,一个字符串类型的值最多能够存储 512 MB 的内容。String 的实现属于特殊结构 SDS(Simple Dynamic String)即简单动态字符串)

结构定义:

1
2
3
4
5
6
7
8
struct sdshdr{
//记录buf数组中已使用字符的数量,等于 SDS 保存字符串的长度
int len;
//记录 buf 数组中未使用的字符数量
int free;
//字符数组,用于保存字符串
char buf[];
}

将字符串存储到字符类型的buf[]中,并使用 lenfreebuf[]数组的长度和未使用的字符数进行描述。

redis string结构

当字符串所占空间小于 1MB 时,Redis 对字符串存储空间的扩容是以成倍的方式增加的;而当所占空间超过 1MB 时,每次扩容只增加 1MB。Redis 字符串允许的最大值字节数是 512 MB。

string命令

1
SET key value [EX seconds|PX milliseconds] [NX|XX]

其含义如下所示:

  • EX seconds:设置指定的过期时间,以秒为单位;
  • PX milliseconds:设置指定的过期时间,以毫秒为单位;
  • NX:先判断 key 是否存在,如果 key 不存在,则设置 key 与 value;
  • XX:先判断 key 是否存在,如果 key 存在,则重新设置 value。

string 类型提供了一些专门操作数值的命令,比如 INCRBY(自增)、DECRBR(自减)、INCR(加1) 和 DECR(减1) 等命令。此时 key 对应的 value 值是必须是一个整数,或浮点数,使用命令对这个数值进行自增或自减操作。

INCRBYFLOAT该命令是 string 中唯一操作浮点数的命令,浮点数可以为正数或者负数,从而实现对数值的加减操作。

1
INCRBYFLOAT fans:num -10.5

hash 散列

hash 结构

一个包含了多个键值对的集合。一般被用来存储对象。hash(哈希散列)是由字符类型的 field(字段)和 value 组成的哈希映射表结构(也称散列表),在 hash 类型中,field 与 value 一一对应,且不允许重复。

对 value 进行查询时,这个值只能以字符串的形式返回。

其底层存储结构有两种实现方式。

第一种,当存储的数据量较少的时,hash 采用 ziplist 作为底层存储结构,此时要求符合以下两个条件:

  • 哈希对象保存的所有键值对(键和值)的字符串长度总和小于 64 个字节。
  • 哈希对象保存的键值对数量要小于 512 个。

第二种,dict(字典结构)是一个无序的字典,并采用了数组和链表相结合的方式存储数据。dict 是基于哈希表算法实现的,因此其查找性能非常高效,其时间复杂度为 O(1)。

list列表

list 结构

List 中的元素是字符串类型,其中的元素按照插入顺序进行排列,允许重复插入。可以添加一个元素到列表的头部(左边)或者尾部(右边)。

是一个链表而非数组,其插入、删除元素的时间复杂度为 O(1),但是查询速度欠佳,时间复杂度为 O(n)。

Redis 列表的底层存储结构,是一个快速链表(quicklist)的结构。当列表中存储的元素较少时,Redis 会使用一块连续的内存来存储这些元素,这个连续的结构被称为 ziplist(压缩列表),它将所有的元素紧挨着一起存储。当数据量较大时,Redis 列表就会是用 quicklist(快速链表)存储元素。

rerdis快速链表

队列和栈实现

右进左出(队列)

1
2
3
Rpush book c python java
lpop book
lpop book

右进右出(栈)

1
2
3
RPUSH book  c python java
rpop book
rpop book

set集合

set 结构

Set 是一个字符串类型元素构成的无序集合。在 Redis 中,集合是通过哈希映射表实现的,所以无论是添加元素、删除元素,亦或是查找元素,它们的时间复杂度都为 O(1)。

当集合中最后一个成员被删除时,存储成员所用的数据结构也会被自动删除。

底层存储结构,分别是 intset(整型数组)与 hash table(哈希表),当 set 存储的数据满足以下要求时,使用 intset 结构:

  • 集合内保存的所有成员都是整数值;
  • 集合内保存的成员数量不超过 512 个。

当不满足上述要求时,则使用 hash table 结构。

intset 的结构体定义:

1
2
3
4
5
typedf struct inset{
uint32_t encoding;//指定编码方式,默认为INSET_ENC_INT16
uint32_t length;//集合内成员的总个数
int8_t contents[];//实际存储成员的数组,并且数组中的数值从小到大依次排列
}inset;

zset有序集合

zset 结构

zset 是一个字符串类型元素构成的有序集合,集合中的元素不仅具有唯一性,而且每个元素还会关联一 个 double 类型的分数,该分数允许重复。Redis 通过这个分数来为集合中的成员排序。适用于排行榜类型的业务场景。

使用了两种不同的存储结构,分别是 zipList(压缩列表)和 skipList(跳跃列表),当 压缩列表:

  • 成员的数量小于128 个;
  • 每个 member (成员)的字符串长度都小于 64 个字节。

ziplist组成

如下:

  • zlbytes 是一个无符号整数,表示当前 ziplist 占用的总字节数;
  • zltail 指的是压缩列表尾部元素相对于压缩列表起始元素的偏移量。
  • zllen 指 ziplist 中 entry 的数量。当 zllen 比2^16 - 2大时,需要完全遍历 entry 列表来获取 entry 的总数目。
  • entry 用来存放具体的数据项(score和member),长度不定,可以是字节数组或整数,entry 会根据成员的数量自动扩容。
  • zlend 是一个单字节的特殊值,等于 255,起到标识 ziplist 内存结束点的作用。

跳跃列表(skipList)又称“跳表”是一种基于链表实现的随机化数据结构,其插入、删除、查找的时间复杂度均为 O(logN)。

skipList 节点最高可以达到 64 层,一个“跳表”中最多可以存储 2^64 个元素,每个节点都是一个 skiplistNode(跳表节点)。

skipList 的结构体定义:

1
2
3
4
5
6
7
8
9
10
typedf struct zskiplist{
//头节点
struct zskiplistNode *header;
//尾节点
struct zskiplistNode *tail;
// 跳表中的元素个数
unsigned long length;
//表内节点的最大层数
int level;
}zskiplist;
  • header:指向 skiplist 的头节点指针,通过它可以直接找到跳表的头节点,时间复杂度为 O(1);
  • tail:指向 skiplist 的尾节点指针,通过它可以直接找到跳表的尾节点,时间复杂度为 O(1);
  • length:记录 skiplist 的长度,也就跳表中有多少个元素,但不包括头节点;
  • level:记录当前跳表内所有节点中的最大层数(level);

评论