Redis 数据类型
redis 支持五种常见的基本数据类型。无论是哪种数据类型,redis 的 key 都是字符串,我们在讨论数据类型时,说的是 value 的不同类型。Redis 丰富的数据类型,使得Redis 可用于缓存,事件发布订阅,或消息队列。
| 类型 | value | 读写能力 |
|---|---|---|
| string | 可以是字符串、整数或浮点数 | 可以对整个字符串或字符串的一部分进行操作;对整数或浮点数进行自增或自减操作;Redis 字符串的大小被限制在 512 M 以内 |
| list | 一个链表,链表上的每个节点都包含一个字符串 | 对链表的两端进行 push 和 pop 操作,读取单个或多个元素;根据value查找或删除元素 |
| set | 包含字符串的无序集合 | 字符串的集合,包含基础的方法有:判断是否存在,添加、获取、删除;还包含计算交集、并集、差集等 |
| zset | 和散列一样,用于存储键值对 | 字符串成员与浮点数分数之间的有序映射;元素的排列顺序由value的大小决定;包含方法有添加、获取、删除单个元素以及根据分值范围或成员来获取元素 |
| hash | 包含键值对的无序散列表 | 包含方法有添加、获取、删除单个元素 |
基本操作。 set 、get、del、incr、decr、incrby、decrby、exists、type、ttl、expire、expirat、keys、randomkey、scan 、incrfloat
1. set 、get、del 命令 操作
127.0.0.1:6379> set name "siri01"
127.0.0.1:6379> get name // GET z
"siri01"
127.0.0.1:6379> del name
(integer) 1
2. incr、decr、incrby、decrby 命令。用于 int 整形的 value 操作。如果 自增一个不存在的 key,会自动创建该 key,value 默认从1 开始自增。
127.0.0.1:6379> get math
"100"
127.0.0.1:6379> incr math
(integer) 101
127.0.0.1:6379> incr math
(integer) 102
127.0.0.1:6379> decr math
(integer) 101
127.0.0.1:6379> decr math
(integer) 100
127.0.0.1:6379> incrby math 100
(integer) 200
127.0.0.1:6379> incrby math 100
(integer) 300
127.0.0.1:6379> decrby math 100
(integer) 200
127.0.0.1:6379> decrby math 100
3. exists、type 判断key方法
127.0.0.1:6379> exists s1 // exists 判断key 是否存在
(integer) 1
127.0.0.1:6379> type s1 // type 查看 key 数据的类型
string
4. ttl、expire、expirat // 除了可以按 秒设置存活时间以为还能按 毫秒,时间戳的形式设置。可以重命名 key 的名字,或取消key 的过期时间
127.0.0.1:6379> set s1 "MyKeys info"
127.0.0.1:6379> get s1
"MyKeys info"
127.0.0.1:6379> expire s1 60 // 设置 key 的过期时间 60s
(integer) 1
127.0.0.1:6379> ttl s1 // 查看 key 剩余的存活时间
(integer) 52
127.0.0.1:6379> ttl s1
(integer) 51
127.0.0.1:6379> set s1 "MyKeys info"
127.0.0.1:6379> expireat s1 1638338402 // 时间戳,设置 s1 过期的时间是 2021-12-01 14:00
(integer) 1
127.0.0.1:6379> ttl s1
(integer) 15897388
127.0.0.1:6379> set s2 "siri02" ex 60 // 插入key 的同时,设置过期时间
127.0.0.1:6379> ttl s2
(integer) 54
5. keys、randomkey
127.0.0.1:6379> keys s* // 正则匹配 存在的 key。但是 keys 这个命令的操作逻辑是遍历,然后返回匹配到的数据,数据量大的时候非常容易导致
1) "s2" // redis 卡住,一般不建议这么使用。
2) "s3"
3) "s1"
127.0.0.1:6379> randomkey // 随机返回一个 key 的值
"nama"
127.0.0.1:6379> randomkey
"s2"
6. scan 命令: 由于keys命令是全量遍历,如果Redis中存放了太多的key,使用keys *会导致进程阻塞而产生超时等情况。为了避免这种情况,可以使用scan命令来进行增量遍 历,scan每次只会遍历一部分数据,然后通过多次遍历将库中所有的key或者指定的key查找出来。
127.0.0.1:6379> scan 2 match s* count 2 // scan 开始序号 [match 模式] [count number]
1) "10"
2) 1) "s05"
2) "s04"
3) "s02"
scan 开始序号 [match 模式] [count number]
开始序号:从0开始,每次执行后都会返回下一次执行命令时需要的序号
match:使用通配匹配
count number:返回多少个匹配的key,默认为10个
总结:
(1) TTL 适合缓存场景
(2) 自增可以用作计数器
(3) 存字符串 session 共享
string 类型操作
(1) set NX & XX: SET key value [EX seconds] [PX milliseconds] [NX|XX]
NX 参数,不存在 该 key 时才执行操作
127.0.0.1:6379> set name "siri" ex 300 NX // 操作成功
OK
127.0.0.1:6379> ttl name
(integer) 293
127.0.0.1:6379> set name "siri" ex 300 NX // 操作失败
(nil)
127.0.0.1:6379> ttl name
(integer) 283
XX 参数,和NX 相反,存在该 Key 时才执行操作
127.0.0.1:6379> set info "siri" XX // 不存在 key info,操作失败
(nil)
127.0.0.1:6379> set info "siri" NX
(2)strlen 、append 、setrange、getrange // 字符串操作
127.0.0.1:6379> get info
"siri_new"
127.0.0.1:6379> STRLEN info // 返回字符串的长度
(integer) 8
127.0.0.1:6379> append info "2020" // 追加字符
(integer) 12
127.0.0.1:6379> get info
"siri_new2020"
127.0.0.1:6379> set greeting "hello mongodb" // 当生成一个很长的字符串时, Redis 需要分配内存空间, 该操作有时候可能会造成服务器阻塞(block)。setrange 命令就很有用
127.0.0.1:6379> get greeting
"hello mongodb"
127.0.0.1:6379> setrange greeting 6 Redis // SETRANGE key offset value
(integer) 13
127.0.0.1:6379> get greeting
"hello Redisdb" // 覆盖修改字符串
127.0.0.1:6379> getrange greeting 0 3
"hell"
(3) mset,mget // 设置[返回]多个 k-v,可以结合 NX,EX
127.0.0.1:6379> mset name "songhe" age 18
OK
127.0.0.1:6379> mget name age
1) "songhe"
2) "18"
127.0.0.1:6379> get age
"18"
(4) msetnx // 原子操作,对多个键进行操作,有一个存在就全部不执行。
127.0.0.1:6379> mset name "siri" class "3201" teacher "zhang"
OK
127.0.0.1:6379> mget name class teacher
1) "siri"
2) "3201"
3) "zhang"
127.0.0.1:6379> MSETNX name "siri" addre "Beijing" iphone "3302-04"
(integer) 0 // key name 已经存在,所以全部执行失败。原子操作。
list 类型操作
Redis中列表List类型是按照插入顺序排序的字符串链表,和数据结构中的普通链表一样(双向链表),可以在头部left和尾部right添加新的元素。插入时如果键不存在Redis将为该键创建一个新的链表。如果链表中所有元素均被删除,那么该键也会被删除。
一个列表中能放 2的32次方 - 1 个元素,大约 42 亿。从元素插入和删除的效率来看,如果是在链表的两头插入或删除元素将是非常高效的操作。即使链表中已经存储了数百万条记录,该操作也能在常量时间内完成。
Redis中对列表List的操作命令中,L表示从左侧头部开始插入和弹出,R表示从右侧尾部开始插入和弹出。
Redis提供了两种方式来做消息队列,一种是生产消费模式,另一种是发布订阅模式。
(1) lpush、lrange、lpushx、rpush、rpushx、lpop、rpop
127.0.0.1:6379> lpush company a
(integer) 1
127.0.0.1:6379> lpush company b b // 加入多个元素。列表不去重。
(integer) 3
127.0.0.1:6379> lpush company c // lpush 是把值插入到了列表的头部
127.0.0.1:6379> lrange company 0 -1 // 用下标访问列表,相当于切片。-1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素
1) "c"
2) "b"
3) "b"
4) "a"
127.0.0.1:6379> lpushx greet aa // 列表存在,且非空才执行该操作
(integer) 0
127.0.0.1:6379> lpushx company x y
(integer) 6
127.0.0.1:6379> lrange company 0 -1
1) "b"
2) "a"
127.0.0.1:6379> rpush company c // 在列表尾部添加元素
(integer) 3
127.0.0.1:6379> lrange company 0 -1
1) "b"
2) "a"
3) "c"
// lpop 从头部删除一个元素,rpop 从尾部删除一个元素
(2) rpoplpush 命令能用于消息队列
127.0.0.1:6379> lpush queue_a 0 1 2 3 4
(integer) 5
127.0.0.1:6379> lrange queue_a 0 -1
1) "4"
2) "3"
3) "2"
4) "1"
5) "0"
127.0.0.1:6379> rpoplpush queue_a queue_a // rpoplpush 同一个列表即为循环模式。每次从列表的尾部移除一个元素添加到头部。移动的元素返还给客户端
127.0.0.1:6379> lrange queue_a 0 -1
1) "0" // 0 从尾部移动到了头部。
2) "4"
3) "3"
4) "2"
5) "1"
127.0.0.1:6379> lrange queue_a 0 -1
1) "0"
2) "4"
3) "3"
4) "2"
5) "1"
127.0.0.1:6379> rpoplpush queue_a queue_b // 每次从列表的尾部移除一个元素添加到一个新列表的头部。此时 queue_b 相当于 queue_a 的回收站。安全模式。
"1"
127.0.0.1:6379> lrange queue_b 0 -1 // 从 queue_a 接收到一个新的元素
1) "1"
127.0.0.1:6379> lrange queue_a 0 -1 // 尾部 1 被移出
1) "0"
2) "4"
3) "3"
4) "2"
(3) lrem、llen、lindex
127.0.0.1:6379> lpush L1 a b b b b c d
(integer) 7
127.0.0.1:6379> lrem L1 1 b // 移除从表头到表尾,最先发现的1个 b。 LREM key count value
(integer) 1
127.0.0.1:6379> lrange L1 0 -1
1) "d"
2) "c"
3) "b" //
4) "b"
5) "b"
6) "a"
127.0.0.1:6379> lrem L1 0 b // count=0 移除列表中的所有 b元素
(integer) 3
127.0.0.1:6379> lrange L1 0 -1
1) "d"
2) "c"
3) "a"
127.0.0.1:6379> llen L1 // 计算列表的长度,即元素的个数。
(integer) 3
127.0.0.1:6379> lrange L1 0 -1
1) "d"
2) "c"
3) "a"
127.0.0.1:6379> lindex L1 0
"d"
127.0.0.1:6379> lindex L1 1 // 用下标访问元素
"c"
127.0.0.1:6379> lindex L1 2
"a"
127.0.0.1:6379> lindex L1 -1
"a"
(4) linsert、lset // 插入数组,修改数组。
127.0.0.1:6379> lpush L2 1 2 3 4
(integer) 4
127.0.0.1:6379> linsert L2 before 3 5 // LINSERT key BEFORE|AFTER pivot value
(integer) 5
127.0.0.1:6379> lrange L2 0 -1
1) "4"
2) "5" // 5 插入到了 3 的前面。
3) "3"
4) "2"
5) "1"
127.0.0.1:6379> lset L1 0 "web.com" // 用下表修改列表元素的值。吧下标 0 元素改为web.com
OK
127.0.0.1:6379> lrange L1 0 -1
1) "web.com"
2) "c"
3) "a"
(5) ltrim 对列表进行修剪。 LTRIM key start stop
127.0.0.1:6379> lpush L3 a b c d e
(integer) 5
127.0.0.1:6379> ltrim L3 0 2 // 截取 0-2 下标的元素,剩下的删除。
OK
127.0.0.1:6379> lrange L3 0 -1
1) "e"
2) "d"
3) "c"
如果 start 下标比列表的最大下标 end ( LLEN list 减去 1 )还要大,或者 start > stop , LTRIM 返回一个空列表(因为 LTRIM 已经将整个列表清空)
127.0.0.1:6379> ltrim L3 1 0
OK
127.0.0.1:6379> lrange L3 0 -1
(empty list or set)
总结:
(1)列表在生产者消费者场景的应用: lpush + rpop 先进先出,这种模式消费者需要循环拉取消息。
但这里有个小问题,当队列中已经没有消息了,消费者在执行 RPOP 时,会返回 NULL。而我们在编写消费者逻辑时,一般是一个「死循环」,这个逻辑需要不断地从队列中拉取消息进行处理。如果此时队列为空,那消费者依旧会频繁拉取消息,
这会造成「CPU 空转」,不仅浪费 CPU 资源,还会对 Redis 造成压力。
要解决这个问题 Redis 确实提供了「阻塞式」拉取消息的命令:BRPOP / BLPOP,这里的 B 指的是阻塞(Block),列表空时就阻塞,不拉取消息,解决了无效拉取的问题。一旦生产了新的消息,消费者会第一时间停止阻塞。
无论是阻塞还是不阻塞,但是消息只能消费一次,这是个不太好的地方。另外还容易 消息丢失。
set 类型操作
set 是无序集合,集合中不允许存在重复元素。
(1) sadd、 smembers、spop、sismember、srandmember、srem、smove
127.0.0.1:6379> sadd set01 a b c c // sadd 向集合中添加 a,b,c,c 元素。(去重)
(integer) 3
127.0.0.1:6379> smembers set01 // 查看集合中全部元素的值。可以看出集合的元素是无序的。
1) "c"
2) "a"
3) "b"
127.0.0.1:6379> spop set01 // 随机从集合中移除一个元素
"c"
127.0.0.1:6379> sismember set01 "a" // 判断集合中是否存在 a 元素
(integer) 1
127.0.0.1:6379>
127.0.0.1:6379> srandmember set01 // 不加 count 参数时随机返回一个元素
"d"
127.0.0.1:6379> srandmember set01 3 // 加 count 参数时随机返回 count 个元素
1) "x"
2) "c"
3) "y"
127.0.0.1:6379> srem set01 a // 移除集合中指定的元素 a。可以批量移除。
(integer) 1
127.0.0.1:6379> smove set01 set02 "x" // SMOVE source destination member
(integer) 1 // 讲 set01 集合中的 x 元素移动到 set02 集合中。该操作是原子操作。
127.0.0.1:6379> smembers set02
1) "aa"
2) "x"
(2) scard、sinter(交集)/sinterstore 、sunion(并集)/sunionstore、sdiff(差集)/sdiffstore
127.0.0.1:6379> scard set01 // 返回集合中元素的总数
(integer) 4
127.0.0.1:6379> sinter set01 set02 // 取两个集合的交集。
1) "x"
127.0.0.1:6379> sinterstore new_set set01 set02
(integer) 2 // 取set01 set02 集合的交集并把结果存到一个新的集合new_set中,而不是简单的返回。
127.0.0.1:6379> smembers new_set
1) "y"
2) "x"
127.0.0.1:6379> sunion set01 set02 // 返回两个集合的并集
1) "a"
2) "b"
3) "x"
4) "aa"
5) "c"
6) "y"
127.0.0.1:6379> sunionstore n_set set01 set02
(integer) 6 取set01 set02 集合的并集并把结果存到一个新的集合n_set中,而不是简单的返回。
总结:
(1)标签(tag),给用户添加标签,或者用户给消息添加标签,这样有同一标签或者类似标签的可以给推荐关注的事或者关注的人。
(2)用户收藏功能,可以放到set中实现。
(3)随机数抽奖游戏
zset 类型操作
Redis 有序集合和集合一样也是 string 类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个 double 类型的分数。redis 正是通过分数来为集合中的成员进行从小到大的排序。有序集合的成员是唯一的,但分数(score)却可以重复。
集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)
(1) zadd、zrange、zrem、zremrangebyrank、zremrangebyscore、zunionstore、zinterstore
127.0.0.1:6379[1]> zadd score 100 aa 90 a
(integer) 2
127.0.0.1:6379[1]> zrange score 0 -1
1) "a"
2) "aa"
127.0.0.1:6379[1]> zadd score 200 aaa
(integer) 1
127.0.0.1:6379[1]> zadd score 400 aaaa
(integer) 1
127.0.0.1:6379[1]> zrange score 0 -1
1) "a"
2) "aa"
3) "aaa"
4) "aaaa"
ZREMRANGEBYRANK key start stop // 移除下标 是 start 到 stop之间的元素
127.0.0.1:6379[1]> zremrangebyscore salary 1500 3500 移除分值在 1500 3500 之间的元素
zunionstore // 计算两个或多个有序集合的并集
zinterstore // 计算两个或多个有序集合的交集
(2) zscore、zincrby // 分值操作
127.0.0.1:6379[1]> zscore score aa // 查看某个元素的分值
"100"
127.0.0.1:6379[1]> zincrby score 1000 aa // 增加某个元素的分值
"1100"
127.0.0.1:6379[1]> zscore score aa
"1100"
127.0.0.1:6379[1]> zrange score 0 -1 // aa 排序发生了变化
1) "a"
2) "aaa"
3) "aaaa"
4) "aa"
(3) zcount、zrange、zrevrange、zrank、zrevrank、zrangebylex // 排序
127.0.0.1:6379[1]> zcount score 30 200 // 分值在 30-200 之间的个数
(integer) 2
127.0.0.1:6379[1]> zrange score 0 -1 withscores // zrange 是递增排列
1) "a"
2) "90"
3) "aaa"
4) "200"
5) "aaaa"
6) "400"
7) "aa"
8) "1100"
127.0.0.1:6379[1]> zrevrange score 0 -1 // zrevrange 是 递减排序。
1) "aa"
2) "aaaa"
3) "aaa"
4) "a"
127.0.0.1:6379[1]> zrank score "a" // 返回 member的排名。(递增从 0 开始计算排名,zrevrank 与之相反)
(integer) 3
zrangebylex // 当有序集合的所有成员都具有相同的分值时, 有序集合的元素会根据成员的字典序(lexicographical ordering)来进行排序
(4) zrangebyscore、zrevrangebyscore // 范围查询
zrangebyscore 返回有序集 key 中,所有分值介于 min 和 max 之间(包括等于 min 或 max )的成员。有序集成员按 score 值递增(从小到大)次序排列
zrevrangebyscore 与 zrangebyscore 相反。
127.0.0.1:6379[1]> zrangebyscore score (30 300 // 查询 分值 30< score <= 300 的元素
1) "a"
2) "aaa"
127.0.0.1:6379[1]> zrangebyscore score (30 (300 // 查询 分值 30< score < 300 的元素
1) "a"
2) "aaa"
127.0.0.1:6379[1]> zrangebyscore score -inf 100 // 查询 分值 =< 100 的元素
1) "a"
127.0.0.1:6379[1]> zrangebyscore score -inf +inf // 查询所有元素,按低最高排列。
1) "a"
2) "aaa"
3) "aaaa"
4) "aa"
总结: 给member 打分,适合排行榜,是有序集合经典使用场景。例如小说视频等网站需要对用户上传的小说视频做排行榜,榜单可以按照用户关注数,更新时间,字数等打分,做排行。
hash 类型操作
hash 表是 string 类型的集合。类似 mongodb 的 json存储,是 k-v 键值对的集合。hash 类型,可以和关系型数据库中的二维表进行转化。形如value=[{field1,value1},...{fieldN,valueN}]。
(1) hsets、hget|hmget、hgetall、hsetnx、hexist、hdel、hlen
127.0.0.1:6379[2]> hset info name "siri" age 18 hobby "games"
(integer) 3 // hash key info={name: siri,age:18, hobby: gaes}
127.0.0.1:6379[2]> hget info name // hget 获取 name 字段的值
"siri"
127.0.0.1:6379[2]> hgetall info // 返回所有字段
1) "name"
2) "siri"
3) "age"
4) "18"
5) "hobby"
6) "games"
127.0.0.1:6379[2]> hsetnx info name "songhe" // name 字段不存在时,才设置该字段
(integer) 0
127.0.0.1:6379[2]> hexists info name // 判断 info 中是否存在 name 字段
(integer) 1
127.0.0.1:6379[2]> hdel info name // 删除 name 字段
127.0.0.1:6379[2]> hlen info // 获取 哈希表 info 的长度
(integer) 3
127.0.0.1:6379[2]> hstrlen info name // 获取 name 字段 value 的字符串长度
(integer) 4
127.0.0.1:6379[3]> hmget animal cat dog // hmget 返回多个字段的值
1) "miaomiaomiao"
2) "wangwangwang"
(2) hincrby、hincrbyfloat 算术运算
127.0.0.1:6379[2]> hincrby info age 10
(integer) 28 // // 为哈希表 key 中的域 field 的值加上增量 increment。增量也可以为负数,相当于对给定域进行减法操作。
127.0.0.1:6379[2]> hget info age // 给指定的字段的 value 加减。可以用在计数器场景中。
"28"
hincrbyfloat // 加减浮点类型
(3) hkeys、hvals
127.0.0.1:6379[3]> hkeys web // 返回所有字段
1) "baidu"
2) "google"
3) "sougou"
127.0.0.1:6379[3]> hvals web
1) "www.baidu.com"
2) "www.google.com"
3) "www.sougou.com"
总结:hash 适合存储对象

浙公网安备 33010602011771号