Redis 所有的 key(键)都是字符串。在谈基础数据结构时,我们讨论的是存储值的数据类型,主要包括常见的 5 种数据类型,分别是:String、List、Set、Zset、Hash
数据结构介绍
Redis 是一个 key-value 的数据库,key 是一般的 String,不过 value 却是多种多样的:
结构类型 | 结构存储的值 | 结构的读写能力 |
---|---|---|
String 字符串 | 可以是字符串、整数或浮点数 | 对整个字符串或字符串的一部分进行操作;对整数或浮点数进行自增或自减操作 |
List 列表 | 一个链表,链表上的每个节点都包含一个字符串 | 对链表的两端进行 push 和 pop 操作,读取单个或多个元素;根据值查找或删除元素 |
Set 集合 | 包含字符串的无序集合 | 字符串的集合,包含基础的方法有看是否存在添加、获取、删除;还包含计算交集、并集、差集等 |
Hash 散列 | 包含键值对的无序散列表 | 包含方法有添加、获取、删除单个元素 |
Zset 有序集合 | 和散列一样,用于存储键值对 | 字符串成员与浮点数分数之间的有序映射;元素的排列顺序由分数的大小决定;包含方法有添加、获取、删除单个元素以及根据分值范围或成员来获取元素 |
通用命令
-
KEYS pattern:查看复合模版的所有 keys, 不建议在生产坏境下使用
-
DEL key:删除一个指定的 key
-
EXISTS key:判断 key 是否存在
-
EXPIRE key seconds:给一个 key 设置过期时间 单位是秒
-
TTL key:查看指定 key 的剩余有效时间 。-1 表示当前 key 没有设置有效时间 ,-2 表示当前 key 不存在
-
PERSIST key:持久化 指定 key
-
TYPE key:查看指定 key 的类型
-
MOVE key num:移动指定 key 到指定数据库
-
RANDOMKEY:随即返回库中的一个 key
String 字符串命令
String 类型是 Redis 基本类型,一个键最大能存储 512MB 且是二进制安全的。意思是 redis 的 String 类型可以存储任何数据,如数字,字符串,jpg 图片或者序列化的对象。
原理
redis 中的 String 是动态字符串,内部结构类似 ArrayList。采用预分配冗余空间的方式减少内存的频繁分配。
内部为字符串分配的实际空间一般高于字符串长度,当字符串长度 <1MB 时,扩容方式是加倍 也就是原来的两倍。当字符串长度> 1MB 时,一次扩容 1MB,直到最大 512MB.
命令 | 简述 | 使用 |
---|---|---|
GET | 获取存储在给定键中的值 | GET key |
MGET | 获取多个存储在给定键中的值 | MGET key1 key2 |
SET | 设置存储在给定键中的值 | SET key value |
MSET | 设置多个存储在给定键中的值 | MSET key1 value1 key2 value2 |
SETNX | 如果不存在即设置值 | SETNX key value |
SETEX | 设置值的同时设置过期时间 | SETNX key seconds value |
DEL | 删除存储在给定键中的值 | DEL key |
INCR | 将键存储的值加 1 | INCR key |
DECR | 将键存储的值减 1 | DECR key |
INCRBY | 将键存储的值加上整数 | INCRBY key amount |
DECRBY | 将键存储的值减去整数 | DECRBY key amount |
APEEND | 字符串后添加内容 | APEEND key value |
STRLEN | 获取字符串长度 | STRLEN ke |
GETRANGE | 获取字符串指定索引范围的内容 | GETRANGE key start end |
SETRANGE | 设置字符串指定偏移量开始处插入值 | SETRANGE key offset value |
实战场景
- 缓存: 经典使用场景,把常用信息,字符串,图片或者视频等信息放到 redis 中,redis 作为缓存层,mysql 做持久化层,降低 mysql 的读写压力。
- 计数器:redis 是单线程模型,一个命令执行完才会执行下一个,同时数据可以一步落地到其他的数据源。
- session:常见方案 spring session + redis 实现 session 共享
List 链表命令
redis 的列表是一个字符链表,内部结构类似 LinkedList。left,right
都可以添加。如果键不在,则创建新链表,如果已存在则新填内容。如果当前链表没有值,则该链表也会自动删除。redis 的列表最多可存储
2^32-1 个元素(4294967295,每个列表可以存储 40 多亿个元素)
原理
底层是一个快速链表(quickList)的结构,在列表元素较少时,使用内存存储压缩列表 ziplist。当元素数量较多时,改成 quickList,也就是将多个 zipList 串起来使用,以减少内存的碎片化。
命令 | 简述 | 使用 |
---|---|---|
RPUSH | 将给定值推入到列表右端 | RPUSH key value |
LPUSH | 将给定值推入到列表左端 | LPUSH key value |
RPOP | 从列表的右端弹出一个值,并返回被弹出的值 | RPOP key |
LPOP | 从列表的左端弹出一个值,并返回被弹出的值 | LPOP key |
LRANGE | 获取列表在给定范围上的所有值 | LRANGE key 0 -1 |
LINDEX | 通过索引获取列表中的元素。你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。 | LINDEX key index |
LINSERT | 在某一个旧元素值的前边或后边插入一个新的值 | linsert key before/after old_value new_value |
LLEN | 过去链表长度 | llen key |
LTRIM | 截取 list 从 stater 到 end 位置的值并保留 | ltrim key start end |
LREM | 删除 count 个元素值为 value 的元素 | lrem key count value |
LSET | 修改索引号为 index 的元素的值为 value | LSET key index value |
使用列表的技巧
- Stack (栈) : lpush+lpop
- Queue(队列): lpush+rpop
- Capped Collection(有限集合): lpush+ltrim
- Message Queue(消息队列): lpush+brpop
Set 集合命令
redis 的集合是 String 类型的无序不重复的元素集,同时提供交集、并集、差集等操作,集合中最大的成员数为 2^32-1(40
多亿)。Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O (1)。
原理
类似 HashSet, 也是通过哈希表实现的,相当于所有的 value 都为空。通过计算 hash 的方式来快速排重,也是 set 能提供判断一个成员是否在集合内的原因。
命令使用
命令 | 简述 | 使用 |
---|---|---|
SADD | 向集合添加一个或多个成员 | SADD key value |
SPOP | 随机弹出 count 个 member | SPOP key [count] |
SREM | 删除 set 集合中的 member | SREMkey member [member…] |
SCARD | 获取集合的成员数 | SCARD key |
SMEMBERS | 返回集合中的所有成员 | SMEMBERS key member |
SISMEMBER | 判断 member 元素是否是集合 key 的成员 | SISMEMBER key member |
SRANDMEMBER | 随机返回 count 个成员 | SRANDMEMBER key count |
SMOVE | 移动 source 中的成员 member 到 destination 中 | SMOVE sourse destination member |
SDIFF | 返回 key1 与 key2 的差集 | SDIFF key1 key2 [key3…] |
SINTER | 返回 key1 与 key2 的交集 | SINTER key1 key2 [key3…] |
SUNION | 返回 key1 与 key2 的交集 | SUNION key1 key2 [key3…] |
Hash 散列命令
redis 中的无序字典是一个 String 类型的 field 和 value 的映射表,内部结构类似 HashMap,每个 hash
可以存储 2^32-1 个键值对(40 多亿)
原理
底层的实现结构,与 HashMap 一样,是 “数组 + 链表” 的二维结构,第一维 hash 的数据位置碰撞时,将碰撞元素用链表串接起来,不同的是,redis 字典的值只能是字符串,而且两者的 rehash 方式不同。java 的 hashmap 是一次全部 rehash,耗时较高,redis 为了提高性能,采用渐进式 rehash 策略。具体方式为,同时保留新旧两个 hash 结构,然后逐步搬迁,最后取代
命令使用
命令 | 简述 | 使用 |
---|---|---|
HSET(HMSET,HSETNX) | 添加键值对 | HSET hash-key sub-key1 value1 |
HGET(HMGET) | 获取指定散列键的值 | HGET hash-key key1 |
HGETALL | 获取散列中包含的所有键值对 | HGETALL hash-key |
HDEL | 如果给定键存在于散列中,那么就移除这个键 | HDEL hash-key sub-key1 |
HKEYS | 获取散列中包含的所有键 | HKEYS hash-key |
HVALS | 获取散列中包含的所有值 | HVALS hash-key |
HEXISTS | 判断 hash 中是否存在 field 值 | HEXISTS key field |
Zset 有序集合命令
Redis 有序集合和集合一样也是 string 类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个 double
类型的分数。redis 正是通过分数来为集合中的成员进行从小到大的排序。zset 的成员是唯一的,但分数是可以重复的。
原理
压缩列表 (ziplist): ziplist 是为了提高存储效率而设计的一种特殊编码的双向链表。它可以存储字符串或者整数,存储整数时是采用整数的二进制而不是字符串形式存储。它能在 O (1) 的时间复杂度下完成 list 两端的 push 和 pop 操作。但是因为每次操作都需要重新分配 ziplist 的内存,所以实际复杂度和 ziplist 的内存使用量相关
跳跃表(zSkiplist): 跳跃表的性能可以保证在查找,删除,添加等操作的时候在对数期望时间内完成,这个性能是可以和平衡树来相比较的,而且在实现方面比平衡树要优雅,这是采用跳跃表的主要原因。跳跃表的复杂度是 O (log (n))。
命令使用
命令 | 简述 | 使用 |
---|---|---|
ZADD | 将一个带有给定分值的成员添加到有序集合里面 | ZADD zset-key 178 member1 |
ZRANGE(zrevrange) | 根据元素在有序集合中所处的位置,从有序集合中获取多个元素 | ZRANGE zset-key 0-1 withccores |
ZREM | 如果给定元素成员存在于有序集合中,那么就移除这个元素 | ZREM zset-key member1 |
zrangebyscore(zrevrangebyscore) | 获取指定范围内分数的所有成员 (升序) withscores 表示带上分数,limit 表示分页 | zrangebyscore key min max [withscores] [limit offset count] |
zcard | 获取 zset 成员个数 | ZADD zset-key 178 member1 |
zcount | 获取 zset 在指定分数范围内的成员个数,“(” 左括号表示闭区间 | zcount key min max |
zrank(zrevrank) | 获取 zset 中成员 member 的排名(升序) | zrank key member |