Redis简介
Redis 是完全开源免费的,遵守BSD协议,高性能的基于键值对(key-value)的NoSQL(Not Only SQL)数据库。
-
SQL (Struct Query Lanauge 结构化的查询语言) 。引申含义 RDBMS产品,传统的关系型数据库,存储格式化的表格数据。
-
NOSQL (Not Only SQL )不仅仅只有关系型数据库。引申含义:非关系型数据库。存储半格式化和非格式化的数据,如k-v、json、xml
关系型数据库的特点
关系型数据库特点:
-
存储结构化数据:多行多列的表结构数据。
-
强事务:支持完整的ACID事务特性。
-
在磁盘中存储数据:可通过索引加速检索。
-
数据量到达一定量级,查询性能就会出现瓶颈:根据表字段不同,在百万或者千万级时会遇到。
-
支持SQL:大部分的SQL语句都可以跨越数据库。
-
原生不支持分布式:需要借助第3方中间件分库分表,以应对海量存储需求。
Redis的特点
Redis的特点:
-
存储非结构化的数据:k-v方式存储数据。
-
弱事务:支持事务的部分特性。
-
在内存中存储数据,但能同时持久化数据到磁盘。
-
查询性能非常好,但不支持连接查询。
-
不支持SQL,需要使用特定的命令。
-
天然支持集群、数据分片,扩展容易。
Redis就是一个在内存中,存储k-v格式数据,支持自动持久化的NoSQL型数据库。
Redis开发中的应用
典型应用:
-
在一定程度上代替关系型数据库,保存需要持久化,但对事务要求不高的数据:购物车
-
充当应用中的缓存,Redis查询性能非常好,可支持的并发远高于关系型数据库。可以优先从Redis中查询数据,缓存为命中后,再从数据库中查询并缓存数据到Redis中,以提升程序性能。
-
Redis提供了丰富的数据类型,可以解决关系型数据库不方便解决的问题,比如:排行榜(热度、时间等维度)、计数器(播放量、浏览数)、社交网络(赞\踩、粉丝、共同好友)
-
解决分布式系统带来的新问题,比如对tomcat集群的session进行统一的存储管理。
说明:Redis并不是万金油,有很多适合它解决的问题,但是也有很多不合适它解决的问题。
-
从数据规模的角度上看,数据可以分为大规模数据小规模数据,Redis的数据是存放在内存中的,虽然现在内存已经足够便宜,但是如果数据量非常大,例如每天有几亿的用户行为数据,使用Redis来存储的话,基本上是个无底洞,经济成本相当的高。
-
从数据冷热的角度上看,数据分为热数据和冷数据。热数据通常是指需要频繁操作的数据,反之为冷数据。如果将这些冷数据放在Redis中,基本上是对于内存的一种浪费,但是对于一些热数据可以放在Redis中加速读写,也可以减轻后端存储的负载,可以说是事半功倍。
Redis的安装
安装Redis
-
上传
redis-5.x.x.tar.gz
到linux虚拟机的/opt
文件夹 -
安装gcc
[root@localhost ~]# yum install -y gcc
-
解压缩
redis-5.x.x.tar.gz
[root@localhost opt]# tar xzvf redis-5.0.10.tar.gz
-
进入到redis根目录,进行编译、安装
[root@localhost opt]# cd redis-5.0.10 [root@localhost redis-5.0.10]# make [root@localhost redis-5.0.10]# make install
-
将
redis-5.x.x/redis.conf
复制到/etc/redis/
目录下[root@localhost redis-5.0.10]# mkdir -p /etc/redis/ [root@localhost redis-5.0.10]# cp redis.conf /etc/redis/
-
启动redis:通过redis-server启动redis
[root@localhost redis-5.0.10]# redis-server /etc/redis/redis.conf 6255:M 08 Jun 00:19:28.368 * Increased maximum number of open files to 10032 (it was originally set to 1024). _._ _.-``__ ''-._ _.-`` `. `_. ''-._ Redis 5.0.10 (00000000/0) 64 bit .-`` .-```. ```\/ _.,_ ''-._ ( ' , .-` | `, ) Running in standalone mode |`-._`-...-` __...-.``-._|'` _.-'| Port: 6379 | `-._ `._ / _.-' | PID: 6255 `-._ `-._ `-./ _.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | http://redis.io `-._ `-._`-.__.-'_.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | `-._ `-._`-.__.-'_.-' _.-' `-._ `-.__.-' _.-' `-._ _.-' `-.__.-'
说明:redis默认监听端口6379。
Redis的命令行客户端
Redis自带了一个命令行的客户端:redis-cli。下面我们介绍如何使用redis-cli连接、操作Redis服务。
-
打开客户端
# redis-cli -h 主机ip -p 端口号 -h和-p都可以省略,默认后默认为127.0.0.1和6379 [root@localhost ~]# redis-cli -h 127.0.0.1 -p 6379 127.0.0.1:6379>
-
连接成功,执行命令
# 键入各种redis命令, 比如keys * 查询redis中所有的key 127.0.0.1:6379> keys * (empty list or set)
-
停止redis服务
127.0.0.1:6379> shutdown not connected>
-
关闭客户端
not connected> exit [root@localhost ~]#
说明:可以通过redis-cli -h 主机ip -p 端口号 命令
的方式,直接执行单个命令。
Redis重大版本
-
Redis2.6(2012):服务端支持lua脚本;键的过期时间支持毫秒。
-
Redis2.8(2013):增加了增量主从复制功能,提高了主从复制性能;Redis Sentinel第二版,相较于2.6版本,次版本已经生产可用。
-
Redis3.0(2015):增加了Redis Cluster,这是官方的分布式实现。
-
Redis3.2(2016):添加了GEO相关功能;从节点读取过期数据保持一致;数据持久化方面更换了新的RDB格式,但是仍然兼容旧的格式。
-
Redis4.0(2017):提供了module功能,方便第三方开发者扩展redis功能;提供了非阻塞del和flushall/flushdb功能,有效解决删除bigkey可能造成的Redis阻塞;提供了RDB-AOF混合持久化格式,充分利用了AOF和RDB各自优势。
-
Redis5.0(2018):新的流数据类型(Stream DataType);RDB新增LFU和LRU信息;集群管理器从Ruby(redis-trib.rb)移植到了redis-cli中的C语言代码。
-
Redis6.0(2020):引入多线程IO,但执行命令仍是单线程;提供了Redis集群代理功能;改进了PSYNC2的复制协议;推出了更新的RESP3(Redis Serialization Protocol)即Redis 服务端与客户端之间通信的协议第3版;加强了对集群的支持,包括更好的负载均衡和更快的故障转移。
Redis借鉴了Linux操作系统对于版本号的命名规则:版本号第二位如果是奇数,则为非稳定版本(例如2.7、2.9、3.1),如果是偶数,则为稳定版本(例如2.6、2.8、3.0、3.2)。当前奇数版本就是下一个稳定版本的开发版本,例如2.9版本是3.0版本的开发版本。所以我们在生产环境通常选取偶数版本的Redis,如果对于某些新的特性想提前了解和使用,可以选择最新的奇数版本。
Redis初体验
Redis是k-v数据格式的数据库,其中key固定为String类型,而value则有多种类型,下面演示value为字符串类型时的操作。
-
增
127.0.0.1:6379> set name xushy OK 127.0.0.1:6379> set age 18 OK
-
查
#查看所有的key 127.0.0.1:6379> keys * 1) "age" 2) "name" # 根据key获取value 127.0.0.1:6379> get name "xushy" 127.0.0.1:6379> get age "18"
-
改
127.0.0.1:6379> set name liy OK 127.0.0.1:6379> set age 16 OK 127.0.0.1:6379> get name "liy" 127.0.0.1:6379> get age "16"
-
删
127.0.0.1:6379> del name (integer) 1 127.0.0.1:6379> del age (integer) 1 127.0.0.1:6379> get name (nil) 127.0.0.1:6379> get age (nil)
注意:
-
字符串外的单引号或双引号可以省略
-
命令结束直接回车,不要添加分号
Redis中设置key的过期时间
Redis中可以设置数据的过期时间,一旦过期自动删除数据。
-
设置过期时间
127.0.0.1:6379> set name "ok" //设置10s后过期,expire单位秒 127.0.0.1:6379> expire name 10 //设置10s后过期,pexpire 单位毫秒 127.0.0.1:6379> pexpire age 10000 (integer) 1
-
查看剩余时间
# 查看剩余存活时长,单位秒 127.0.0.1:6379> ttl name (integer) 7 # 查看剩余存活时长,单位毫秒 127.0.0.1:6379> pttl name (integer) 4006
-
取消过期
127.0.0.1:6379> set age 18 OK 127.0.0.1:6379> expire age 20 (integer) 1 127.0.0.1:6379> ttl age (integer) 15 取消过期 127.0.0.1:6379> persist age (integer) 1 ttl返回-1表示没有设置过期时间,返回-2表示数据不存在 127.0.0.1:6379> ttl age (integer) -1 127.0.0.1:6379> get age "18"
应用:手机验证码、黑名单、缓存
Redis事务
Redis通过 multi
、exec
、discard
、watch
等命令实现事务功能。Redis的事务功能相对较弱,无法和关系型数据库的事务相媲美。
multi 和 exec命令
语法:
multi 开始事务
命令1
命令2
...
exec 执行事务
discard 放弃执行事务
示例:
127.0.0.1:6379> multi //开始事务
OK
127.0.0.1:6379> set age 18
QUEUED //事务中的命令并不会立刻执行,没有返回结果而是一个QUEUED(队列)
127.0.0.1:6379> getttt //故意执行一个不存在的命令,使得事务出错
(error) ERR unknown command 'getttt'
127.0.0.1:6379> set sex F //在错误命令后,再执行一个命令
QUEUED
127.0.0.1:6379> exec //执行事务,事务失败
(error) EXECABORT Transaction discarded because of previous errors.
//事务中,错误命令前和后的正确命令都没有执行
127.0.0.1:6379> get age
(nil)
127.0.0.1:6379> get sex
(nil)
watch命令
有些应用场景需要在事务之前,确保事务中的key没有被其他客户端修改过,才执行事务,否则不执行。Redis提供了watch命令来解决这类问题。
watch
命令用在 multi
命令前,用于监控任意数量的键是否发生改变。当执行 exec
命令时,将检查监视的键是否已经被修改,如果已经修改,服务器将拒绝执行事务。
语法:
watch 键
multi 开始事务
命令1
命令2
...
exec 执行事务 如果发现watch的键在事务执行前发生改变,则拒绝执行事务
示例:
Redis事务的实现原理
Redis通过一个事务队列完成事务功能。
-
当一个客户端发送
multi
命令后,Redis服务器会将该客户端后续的命令保存到一个队列中。 -
当一个处于事务状态的客户端向Redis服务器发送
exec
命令时,服务器会遍历这个客户端的事务队列,执行队列中保存的所有命令,最后将执行命令的结果全部返回到客户端。 -
Redis在执行事务队列的命令前,如果发现入队的命令有语法错误,或者监控的值发生改变,将清空队列中的命令,拒绝执行。
Redis的弱事务性
在传统的关系型数据库中,常常使用A(原子性Atomicity)C(一致性Consistency)I(隔离性Isolation)D(持久性Durability)性质来检验事务功能的可靠性和安全性。而Redis的事务是不满足ACID特性的。
127.0.0.1:6379> set msg 'hello redis' //添加一个String数据
OK
127.0.0.1:6379> multi //开始事务
OK
127.0.0.1:6379> set name xiaohei
QUEUED
127.0.0.1:6379> rpush msg 1 2 3 //错误的使用rpush命令添加数据
QUEUED
127.0.0.1:6379> set age 18
QUEUED
127.0.0.1:6379> exec //运行时出现错误,并不会影响其它的命令
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
3) OK
127.0.0.1:6379> get name
"xiaohei"
127.0.0.1:6379> get age
"18"
通过上述案例,可以看到redis事务并不能保证事务中的命令要么同时成功,要么同时失败。并且出现运行错误后,也无法回滚事务。
为什么Redis没有实现标准的事务?
-
会引入额外的复杂性,降低Redis的性能
-
没有必要引入,Redis适用的功能场景比较简单,使用Redis事务已经足够完成功能。如果需要更加强大的事务,可以通过lua脚本实现。
Redis中常用的数据结构
传统键值存储是关联字符串值到字符串键,但是 Redis 的值不仅仅局限于简单字符串,还可以持有更复杂的数据结构。下面列的是 Redis 支持常用的数据结构,后面将逐一介绍:
-
String(字符串)
-
List(列表)
-
Set(集合)
-
Hash(哈希,键值对集合)
-
SortedSet(zset,排序集合)
String(字符串)
字符串类型是Redis最基础的数据结构。首先键都是字符串类型,而且其他几种数据结构都是在字符串类型基础上构建的,所以字符串类型能为其他四种数据结构的学习奠定基础。
Redis中字符串类型可存储的值可以是字符串(简单的字符串、复杂的字符串(例如JSON、XML))、数字(整数、浮点数),甚至是二进制(图片、音频、视频),但是值最大不能超过512MB。
读写操作
-
设置值:
set key value [ex seconds] [px milliseconds] [nx|xx]
-
ex seconds:为键设置秒级过期时间。
-
px milliseconds:为键设置毫秒级过期时间。
-
nx:键必须不存在,才可以设置成功,可用于添加。
-
xx:与nx相反,键必须存在,才可以设置成功,可用于更新。
# 省略所有可选参数,不存在则直接添加;已存在则更新 127.0.0.1:6379> set name xushy OK 127.0.0.1:6379> set age 18 OK # 不存在才添加,也可以直接使用setnx命令 127.0.0.1:6379> set sex 1 nx OK 127.0.0.1:6379> set age 19 nx (nil) 失败 #存在才更新,也可以直接使用setxx命令 127.0.0.1:6379> set sex 0 xx OK 127.0.0.1:6379> set address zhengzhou xx (nil) # 添加数据,同时设置过期时间 127.0.0.1:6379> set score 100.0 ex 10 OK
-
-
获取值:
get key
127.0.0.1:6379> get score OK
-
删除:
del key [key ...]
# 可以一次性删除多个key 127.0.0.1:6379> del name age (integer) 2 127.0.0.1:6379> get name (nil)
批量操作
-
批量设置值:
mset key value [key value ...]
127.0.0.1:6379> mset name xiaobai age 20 sex 0 OK
-
批量获取值:
mget key [key ...]
127.0.0.1:6379> mget name age sex 1) "xiaobai" 2) "20" 3) "0"
-
批量添加值:
msetnx key value [key value ...]
# 注意,有一个存在则整体失败 127.0.0.1:6379> msetnx name xiaohei salary 10000.0 (integer) 0
说明:批量操作命令可以有效提高开发效率,假如没有mget这样的命令,client和redis交互就需要产生n次的网络开销。而使用mget只会产生一次网络开销。
Redis可以支撑每秒数万的读写操作,但是这指的是Redis服务端的处理能力,对于客户端来说,一次命令指令耗时除了执行时间还是有网络时间,假设网络时间为1毫秒,命令时间为0.1毫秒(按照每秒处理1万条命令算),那么执行1000次get命令和1次mget命令的区别。因为Redis的处理能力已经足够高,对于开发人员来说,网络可能会成为性能的瓶颈。
操作 | 时间 |
---|---|
1000次get | 1000 × 1 + 1000 × 0.1 = 1100 ms = 1.1s |
1次mget(组装1000个键) | 1 × 1 + 1000 × 0.1 = 101ms = 0.101s |
学会使用批量操作,有助于提高业务处理效率,但是要注意的是每次批量操作所发送的命令数不是无节制的,如果数量过多可能造成Redis阻塞或者网络拥塞。
计数操作
-
自增操作:
incr key
-
值不是整数,返回错误。
-
值是整数,返回自增后的结果。
-
键不存在,按照值为0自增,返回结果为1。
# 不存在返回1 127.0.0.1:6379> incr video:playCount:1001 (integer) 1 # 存在返回自增后的值 127.0.0.1:6379> incr video:playCount:1001 (integer) 2 # 值不是整数,返回错误 127.0.0.1:6379> incr name (error) ERR value is not an integer or out of range
-
-
自增指定数操作:
incrby key increment
# 自增指定的数值 127.0.0.1:6379> incrby video:playCount:1001 10 (integer) 12 # 还可以加上负数,实现自减 127.0.0.1:6379> incrby video:playCount:1001 -1 (integer) 11
-
自减操作:
decr key
127.0.0.1:6379> decr video:playCount:1001 (integer) 10
-
自减指定数操作:
decrby key decrement
127.0.0.1:6379> decrby video:playCount:1001 2 (integer) 8
-
自增浮点数操作:
incrbyfloat key increment
127.0.0.1:6379> incrbyfloat score 0.1 "0.1"
总结
List(列表)
列表(list)类型是用来存储多个有序的字符串,如下图所示,a、b、c、d、e五个元素从左到右组成了一个有序的列表,列表中的每个字符串称为元素(element),一个列表最多可以存储2^{32} -1个元素。
在Redis中,可以对列表两端插入(push)和弹出(pop),还可以获取指定范围的元素列表、获取指定索引下标的元素等。
基础操作
-
插入操作
-
rpush key value [value ...]
从右边插入元素 -
lpush key value [value ...]
左边插入元素 -
linsert key before|after pivot value
向某个元素前后插入元素
# 从右边插入元素 127.0.0.1:6379> rpush news:hot:list article1 article2 (integer) 2 # 查看所有元素 127.0.0.1:6379> lrange news:hot:list 0 -1 1) "article1" 2) "article2" # 从左边插入元素 127.0.0.1:6379> lpush news:hot:list article3 article4 (integer) 4 127.0.0.1:6379> lrange news:hot:list 0 -1 1) "article4" 2) "article3" 3) "article1" 4) "article2" # 向article1 前添加元素article0 127.0.0.1:6379> linsert news:hot:list before article1 article0 (integer) 5 127.0.0.1:6379> lrange news:hot:list 0 -1 1) "article4" 2) "article3" 3) "article0" 4) "article1" 5) "article2" # 向article1后添加元素article1.0 127.0.0.1:6379> linsert news:hot:list after article1 article1.0 (integer) 6 127.0.0.1:6379> lrange news:hot:list 0 -1 1) "article4" 2) "article3" 3) "article0" 4) "article1" 5) "article1.0" 6) "article2"
-
-
删除操作
-
lpop key
从列表左侧弹出元素 -
rpop key
从列表右侧弹出元素 -
lrem key count value
删除最多count个等于value的元素。count > 0 从左向右删除;count < 0 从右向左删除; count = 0 删除所有;
# 从左侧弹出 127.0.0.1:6379> lpop news:hot:list "article4" 127.0.0.1:6379> lrange news:hot:list 0 -1 1) "article3" 2) "article0" 3) "article1" 4) "article1.0" 5) "article2" # 从右侧弹出 127.0.0.1:6379> rpop news:hot:list "article2" 127.0.0.1:6379> lrange news:hot:list 0 -1 1) "article3" 2) "article0" 3) "article1" 4) "article1.0" 127.0.0.1:6379> lpush news:hot:list article3 article4 article3 (integer) 7 127.0.0.1:6379> rpush news:hot:list article1 article2 article1 (integer) 10 127.0.0.1:6379> lrange news:hot:list 0 -1 1) "article3" 2) "article4" 3) "article3" 4) "article3" 5) "article0" 6) "article1" 7) "article1.0" 8) "article1" 9) "article2" 10) "article1" # 从左到右删除2个article3 127.0.0.1:6379> lrem news:hot:list 2 article3 (integer) 2 127.0.0.1:6379> lrange news:hot:list 0 -1 1) "article4" 2) "article3" 3) "article0" 4) "article1" 5) "article1.0" 6) "article1" 7) "article2" 8) "article1" # 从右向左删除2个article1 127.0.0.1:6379> lrem news:hot:list -2 article1 (integer) 2 127.0.0.1:6379> lrange news:hot:list 0 -1 1) "article4" 2) "article3" 3) "article0" 4) "article1" 5) "article1.0" 6) "article2"
-
下标操作
-
根据下标检索元素:
lindex key index
127.0.0.1:6379> lindex news:hot:list 0 "article4"
-
获取列表长度:
llen key
127.0.0.1:6379> llen news:hot:list (integer) 6
-
截取列表子集:
ltrim key start end
127.0.0.1:6379> lrange news:hot:list 0 -1 1) "article4" 2) "article3" 3) "article0" 4) "article1" 5) "article1.0" 6) "article2" # 截取部分 127.0.0.1:6379> ltrim news:hot:list 1 4 OK 127.0.0.1:6379> lrange news:hot:list 0 -1 1) "article3" 2) "article0" 3) "article1" 4) "article1.0"
-
修改指定索引元素:
lset key index newValue
127.0.0.1:6379> lset news:hot:list 0 article3.0 OK 127.0.0.1:6379> lrange news:hot:list 0 -1 1) "article3.0" 2) "article0" 3) "article1" 4) "article1.0"
阻塞操作
前面介绍的 LPOP 和 RPOP 命令,在 List 为空的时候会返回 nil,但是有的场景中,需要在 List 没有数据的时候,阻塞等待新元素。
-
brpop key [key ...] timeout
阻塞式的从列表右侧弹出数据,直到返回数据或者等待超时(timeout单位s,为0时一直阻塞) -
blpop key [key ...] timeout
阻塞式的从列表左侧弹出数据,直到返回数据或者等待超时(timeout单位s,为0时一直阻塞)
127.0.0.1:6379> brpop article:like:list 10
(nil)
(10.18s)
# 阻塞式的弹出数据
127.0.0.1:6379> brpop article:like:list 0
1) "article:like:list"
2) "111"
(29.63s)
# 同时,开启另外一个client,向队列添加元素
127.0.0.1:6379> lpush article:like:list 111
(integer) 1
说明:可以利用Redis的lpush+brpop命令组合即可实现阻塞队列,生产者客户端使用lrpush从列表左侧插入元素,多个消费者客户端使用brpop命令阻塞式的“抢”列表尾部的元素,多个客户端保证了消费的负载均衡和高可用性。
总结
Set(集合)
集合(set)类型也是用来保存多个的字符串元素,但和列表类型不一样的是,集合中不允许有重复元素,并且集合中的元素是无序的,不能通过索引下标获取元素。如图下图所示,集合user:1:follow
包含着"it"、"music"、"his"、"sports"四个元素,一个集合最多可以存储 2^{32}-1个元素。
Redis除了支持集合内的增删改查,同时还支持多个集合取交集、并集、差集,合理地使用好集合类型,能在实际开发中解决很多实际问题。
集合内操作
-
添加元素:
sadd key member[member ...]
# 元素不可以重复 127.0.0.1:6379> sadd user:1:tags sports game food travel sports (integer) 4
-
获取所有元素:
smembers key
127.0.0.1:6379> sadd user:1:tags sports game food travel sports (integer) 4 127.0.0.1:6379> smembers user:1:tags 1) "food" 2) "sports" 3) "travel" 4) "game"
-
删除元素:
srem key member [member ...]
127.0.0.1:6379> srem user:1:tags food game (integer) 2
-
计算元素个数:
scard key
127.0.0.1:6379> scard user:1:tags (integer) 2
-
判断元素是否在集合中:
sismember key member
127.0.0.1:6379> sismember user:1:tags game (integer) 0 127.0.0.1:6379> sismember user:1:tags sports (integer) 1
-
随机从集合中返回指定个元素:
srandmember key count
127.0.0.1:6379> srandmember user:1:tags 1 1) "travel" 127.0.0.1:6379> srandmember user:1:tags 1 1) "travel" 127.0.0.1:6379> srandmember user:1:tags 1 1) "sports"
-
从集合中随机弹出元素:
spop key [count]
127.0.0.1:6379> spop user:1:tags 1 1) "travel" 127.0.0.1:6379> spop user:1:tags 1 1) "sports"
集合间操作
Set 结构除了能支持单个集合内元素的增、删、改、查
之外,还提供了一些数学上的集合操作
,例如,求两个 Set 的并集、差集或者交集等。
-
交集
-
sinter key [key ...]
: 求多个集合的交集(共性部分) -
sinterstore destination key [key ...]
: 求多个集合的交集,并保存到一个新的集合中
# 准备2个集合 user:1:tags 和 user:2:tags 127.0.0.1:6379> smembers user:1:tags 1) "food" 2) "sports" 3) "travel" 4) "game" 127.0.0.1:6379> sadd user:2:tags sports travel entertainment fashion (integer) 4 # 求交集 用户1和用户2共同的爱好 127.0.0.1:6379> sinter user:1:tags user:2:tags 1) "sports" 2) "travel" 127.0.0.1:6379> sinterstore user:1:2:tags:inter user:1:tags user:2:tags (integer) 2 127.0.0.1:6379> smembers user:1:2:tags:inter 1) "sports" 2) "travel"
-
-
并集
-
sunion key [key ...]
: 求多个集合的交集(合并起来) -
sunionstore destination key [key ...]
: 求多个集合的交集(合并起来),并保存到一个新的集合中
# 用户1和用户2所有的爱好 127.0.0.1:6379> sunion user:1:tags user:2:tags 1) "entertainment" 2) "game" 3) "fashion" 4) "food" 5) "sports" 6) "travel" 127.0.0.1:6379> sunionstore user:1:2:tags:union user:1:tags user:2:tags (integer) 6 127.0.0.1:6379> smembers user:1:2:tags:union 1) "entertainment" 2) "game" 3) "fashion" 4) "food" 5) "sports" 6) "travel"
-
-
差集
-
sdiff key1 [key2 ...]
: 求多个集合的差集(key1中有key2中没有的部分) -
sdiffstore destination key1 [key2 ...]
: 求多个集合的差集(key1中有key2中没有的部分),并保存到一个新的集合中
# 用户1和用户2不同的爱好 127.0.0.1:6379> sdiff user:1:tags user:2:tags 1) "food" 2) "game" 127.0.0.1:6379> sdiffstore user:1:2:tags:diff user:1:tags user:2:tags (integer) 2
-
总结
Hash(键值对集合)
几乎所有的编程语言都提供了哈希(hash)类型,它们的叫法可能是Map、字典、关联数组。在Redis中,哈希类型是指键值本身又是一个键值对结构,形如value={{field1,value1},...{fieldN,valueN}},Redis键值对和哈希类型二者的关系可以用下图表示。
读写操作
-
设置值:
hset key field value
127.0.0.1:6379> hset user:1:info userId 10 (integer) 1 127.0.0.1:6379> hset user:1:info username xiaobai (integer) 1 127.0.0.1:6379> hset user:1:info password 123456 (integer) 1 127.0.0.1:6379> hset user:1:info mobile 18712345678 (integer) 1
-
获取值:
hget key field
127.0.0.1:6379> hget user:1:info userId "10"
-
添加值:
hsetnx key field value
# userId已经存在,添加失败返回0 127.0.0.1:6379> hsetnx user:1:info userId 10 (integer) 0 # fans不存在,添加成功返回1 127.0.0.1:6379> hsetnx user:1:info fans 0 (integer) 1
-
删除:
hdel key field [field ...]
127.0.0.1:6379> hdel user:1:info mobile (integer) 1
-
获取field个数:
hlen key
127.0.0.1:6379> hlen user:1:info (integer) 3
-
判断field是否存在:
hexists key field
127.0.0.1:6379> hexists user:1:info userId (integer) 1 127.0.0.1:6379> hexists user:1:info mobile (integer) 0
批量操作
-
批量设置值:
hmset key field value [field value ...]
127.0.0.1:6379> hmset user:2:info userId 20 username xiaohei password 123456 OK
-
批量获取值:
hmget key field [field ...]
127.0.0.1:6379> hmget user:2:info userId username 1) "20" 2) "xiaohei"
-
获取所有field:
hkeys key
127.0.0.1:6379> hkeys user:1:info 1) "userId" 2) "username" 3) "password"
-
获取所有子value:
hvals key
127.0.0.1:6379> hvals user:1:info 1) "10" 2) "xiaobai" 3) "123456"
-
获取所有field-value:
hgetall key
127.0.0.1:6379> hgetall user:2:info 1) "userId" 2) "20" 3) "username" 4) "xiaohei" 5) "password" 6) "123456"
计数操作
-
自增指定的数:
hincrby key field increment
# 用户1新增一个粉丝 127.0.0.1:6379> hincrby user:1:info fans 1 (integer) 1
-
自增指定的浮点数:
hincrbyfloat key field increment
127.0.0.1:6379> hincrbyfloat user:1:info score 4.9 "4.9"
总结
SortedSet(ZSet 排序集合)
有序集合保留了集合不能有重复成员的特性,但不同的是,有序集合中的元素可以排序。但是它和列表使用索引下标作为排序依据不同的是,它给每个元素设置一个分数(score)作为排序的依据。
如下图所示,该有序集合包含kris、mike、frank、tim、martin,它们的分数分别是1、91、200、220、250,有序集合提供了获取指定分数和元素范围查询、计算成员排名等功能,合理的利用有序集合,能帮助我们在实际开发中解决很多问题。
基础操作
-
添加成员:
zadd key [NX|XX] [CH] [INCR] score member [score member ...]
-
NX: member必须不存在,才可以设置成功,用于添加。
-
XX: member必须存在,才可以设置成功,用于更新。
-
CH: 返回此次操作后,有序集合元素和分数发生变化的个数
-
INCR: 对score做增加,相当于后面介绍的zincrby。
# mike 获得了3个赞 127.0.0.1:6379> zadd user:ranking:20230801 NX 3 mike (integer) 1 # mike 新增1个赞 127.0.0.1:6379> zadd user:ranking:20230801 XX INCR 1 mike "4" # 如果更新已有的成员的分数,使用CH返回更新的元素数目 127.0.0.1:6379> zadd user:ranking:20230801 7 mike (integer) 0 127.0.0.1:6379> zadd user:ranking:20230801 CH 8 mike (integer) 1
-
-
删除成员:
zrem key member [member ...]
127.0.0.1:6379> zadd user:ranking:20230801 NX 3 lucy (integer) 1 # 删除成员lucy 127.0.0.1:6379> zrem user:ranking:20230801 lucy (integer) 1
-
修改分数:
zincrby key score member
127.0.0.1:6379> zincrby user:ranking:20230801 1 mike "8"
-
获取成员个数:
zcard key
127.0.0.1:6379> zadd user:ranking:20230801 NX 3 lucy (integer) 1 # 查看成员个数 127.0.0.1:6379> zcard user:ranking:20230801 (integer) 2
-
获取某个成员的分数:
zscore key member
127.0.0.1:6379> zscore user:ranking:20230801 mike "8"
-
计算成员的排名:
zrank key member
127.0.0.1:6379> zrank user:ranking:20230801 mike (integer) 1 127.0.0.1:6379> zrank user:ranking:20230801 lucy (integer) 0
范围操作
-
返回指定下标范围的成员:
zrange key start stop [withscores]
127.0.0.1:6379> zrange user:ranking:20230801 0 -1 1) "lucy" 2) "mike" # 查看所有成员,同时查看分数 127.0.0.1:6379> zrange user:ranking:20230801 0 -1 withscores 1) "lucy" 2) "2" 3) "mike" 4) "8"
-
删除指定下标范围的成员:
zremrangebyrank key start stop
# 删除全部 127.0.0.1:6379> zremrangebyrank user:ranking:20230801 0 -1 (integer) 2
-
返回指定分数范围的成员:
zrangebyscore key min max [withscores] [limit offset count]
127.0.0.1:6379> zadd user:ranking:20230801 2 lucy 3 mike 4 abel 5 lily (integer) 4 127.0.0.1:6379> zrangebyscore user:ranking:20230801 (2 +inf withscores limit 0 10 1) "mike" 2) "3" 3) "abel" 4) "4" 5) "lily" 6) "5"
-
返回指定分数范围的成员个数:
zcount key min max
127.0.0.1:6379> zcount user:ranking:20230801 (2 5 (integer) 3
-
删除指定分数范围的成员:
zremrangebyscore key min max
127.0.0.1:6379> zremrangebyscore user:ranking:20230801 (2 (5 (integer) 2