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 适合存储对象
posted @ 2021-06-10 19:23  梦里花。  阅读(64)  评论(0)    收藏  举报