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 | redis-cli -h [ip] -p [port] -a [password] |
数据类型是 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 | struct sdshdr{ |
将字符串存储到字符类型的buf[]
中,并使用 len
、free
对buf[]
数组的长度和未使用的字符数进行描述。
当字符串所占空间小于 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(快速链表)存储元素。
队列和栈实现
右进左出(队列)
1 | Rpush book c python java |
右进右出(栈)
1 | RPUSH book c python java |
set集合
set 结构
Set 是一个字符串类型元素构成的无序集合。在 Redis 中,集合是通过哈希映射表实现的,所以无论是添加元素、删除元素,亦或是查找元素,它们的时间复杂度都为 O(1)。
当集合中最后一个成员被删除时,存储成员所用的数据结构也会被自动删除。
底层存储结构,分别是 intset(整型数组)与 hash table(哈希表),当 set 存储的数据满足以下要求时,使用 intset 结构:
- 集合内保存的所有成员都是整数值;
- 集合内保存的成员数量不超过 512 个。
当不满足上述要求时,则使用 hash table 结构。
intset 的结构体定义:
1 | typedf struct inset{ |
zset有序集合
zset 结构
zset 是一个字符串类型元素构成的有序集合,集合中的元素不仅具有唯一性,而且每个元素还会关联一 个 double 类型的分数,该分数允许重复。Redis 通过这个分数来为集合中的成员排序。适用于排行榜类型的业务场景。
使用了两种不同的存储结构,分别是 zipList(压缩列表)和 skipList(跳跃列表),当 压缩列表:
- 成员的数量小于128 个;
- 每个 member (成员)的字符串长度都小于 64 个字节。
如下:
- 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 | typedf struct zskiplist{ |
- header:指向 skiplist 的头节点指针,通过它可以直接找到跳表的头节点,时间复杂度为 O(1);
- tail:指向 skiplist 的尾节点指针,通过它可以直接找到跳表的尾节点,时间复杂度为 O(1);
- length:记录 skiplist 的长度,也就跳表中有多少个元素,但不包括头节点;
- level:记录当前跳表内所有节点中的最大层数(level);