Redis
:REmote Dictionary Server(远程字典服务器)
官网:
https://redis.io/docs/about/
定义:
Remote Dictionary Server(远程字典服务)是完全开源的,使用 ANSIC 语言编写遵守 BSD 协议,是一个高性能的 Key-Value 数据库提供了丰富的数据结构,例如 String
、Hash
、List
、Set
、SortedSet
等等。数据是存在内存中的,同时支持 事务
、持久化
、LUA脚本
、发布/订阅
、缓存淘汰
、流技术
等多种功能特性。提供了 主从模式
、Redis Sentinel
和 Redis Cluster
集群架构方案。
️1)分布式缓存
,挡在 MySQL
数据库之前的带刀护卫
️ 与传统数据库(MySQL)关系:
内存
,而 MySQL 主要存储在磁盘;共用和配合使用
。️ 2)内存存储和持久化(RDB
+ AOF
)
Redis 支持 异步
将内存中的数据写到硬盘上,同时不影响继续服务。
️ 3)高可用架构搭配
支持 单机
、主从
、哨兵
、集群
模式。
️ 4)缓存穿透、击穿、雪崩
️ 5)分布式锁
️ 6)队列
Reids 提供 list 和 set 操作,这使得 Redis 能作为一个很好的消息队列平台来使用。
可通过 Reids 的 队列
功能做购买限制。比如到节假日或者推广期间,进行一些活动,对用户购买行为进行限制,限制今天只能购买几次商品或者一段时间内只能购买一次。
️ 7)排行版+点赞
在互联网应用中,有各种各样的排行榜,如电商网站的月度销量排行榜、社交APP的礼物排行榜、小程序的投票排行榜等等。Redis 提供的 zset
数据类型能够快速实现这些复杂的排行榜。
比如小说网站对小说进行排名,根据排名,将排名靠前的小说推荐给用户。
1)性能极高
:Redis 能读的速度是 110000次/秒,写的速度是 81000次/秒。
2)数据类型丰富
:不仅仅支持简单的 key-value 类型的数据,同时还提供 list
,set
,zset
,hash
等数据结构的存储。
3)数据的持久化
:可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。
4)数据的备份
:即 master-slave
模式的数据备份。
Redis 的定位:
文档
:https://redis.io
源码
:https://github.com/redis/redis
在线测试:https://try.redis.io/
命令参考:http://doc.redisfans.com/
中文资料:
https://redis.io/download/
官网:https://redis.io/docs/about/
Redis also includes:
️ 1)里程碑式的重要版本
️ 2)命名规则
Redis 从发布至今,已经有十余年的时光了,一直遵循着自己的命名规则:
我们可以通过 redis.io 官网来下载自己感兴趣的版本进行源码阅读:
历史发布版本的源码:https://download.redis.io/releases/
地址:https://github.com/redis/redis/releases
️ 1)部分新特性总览
2022 年 4 月正式发布的 Redis 7.0 是目前 Redis 历史版本中变化最大的版本。
首先,它有超过 50 个以上新增命令;其次,它有大量核心特性的新增和改进。
新增 ZMPOP,BZMPOP,LMPOP,BLMPOP 等新命令,对于 EXPIRE 和 SET 命令,新增了更多的命令参数选项。
例如,ZMPOP 的格式如下:ZMPOP numkeys key [key ...] MIN|MAX [COUNT count]
,而 BZMPOP 是 ZMPOP 的阻塞版本。
listpack 是用来替代 ziplist 的新数据结构,在 7.0 版本已经没有 ziplist 的配置了(6.0 版本仅部分数据类型作为过渡阶段在使用)
大体和之前的 Redis 版本保持一致和稳定,主要是自身底层性能和资源利用率上的 优化和提高
,如果你生产上系统稳定,不用着急升级到最新的 Redis 7 版本,当然,如果你是从零开始的新系统,可以直接上 Redis7.0-GA 版。
️ 1)多AOF文件支持
7.0 版本中一个比较大的变化就是 AOF 文件由一个变成了多个,主要分为两种类型:基本文件(base files)、增量文件(incr files),请注意这些文件名称是复数形式说明每一类文件不仅仅只有一个。在此之外还引入了一个清单文件(manifest)用于跟踪文件以及文件的创建和应用顺序(恢复)。
️ 2)config命令增强
对于 Config Set 和 Get 命令,支持在一次调用过程中传递多个配置参数。例如,现在我们可以在执行一次 Config Set 命令中更改多个参数: config set maxmemory 10000001 maxmemory-clients 50% port 6399
️ 3)限制客户端内存使用 Client-eviction
当 Redis 连接较多,再加上每个连接的内存占用都比较大的时候, Redis 总连接内存占用可能会达到 maxmemory
的上限,可以增加允许限制所有客户端的总内存使用量配置项,redis.config 中对应的配置项,两种配置形式:指定内存大小、基于 maxmemory 的百分比。
️ 4)listpack紧凑列表调整
listpack 是用来替代 ziplist 的新数据结构,在 7.0 版本已经没有 ziplist 的配置了(6.0版本仅部分数据类型作为过渡阶段在使用)listpack 已经替换了 ziplist 类似 hash-max-ziplist-entries 的配置。
️ 5)访问安全性增强 ACLV2
在 redis.conf 配置文件中,protected-mode
默认为 yes
,只有当你希望你的客户端在没有授权的情况下可以连接到 Redis server 的时候可以将 protected-mode 设置为 no。
️ 6)Redis Functions
Redis 函数,一种新的通过服务端脚本扩展 Redis 的方式,函数与数据本身一起存储。简言之,Redis 自己要去抢夺 Lua 脚本的配合使用。
️ 7)RDB保存时间调整
将持久化文件 RDB 的保存规则发生了改变,尤其是时间记录频度变化。
️ 8)命令新增和变动
Zset(有序集合)增加 ZMPOP、BZMPOP、ZINTERCARD 等命令;
Set(集合)增加 SINTERCARD 命令;
List(列表)增加 LMPOP、BLMPOP ,从提供的键名列表中的第一个非空列表键中弹出一个或多个元素。
️ 9)性能资源利用率、安全等改进
自身底层部分优化改动,Redis 核心在许多方面进行了重构和改进。
主动碎片整理V2:增强版主动碎片整理,配合 Jemalloc 版本更新,更快更智能,延时更低。
HyperLogLog 改进:在 Redis5.0 中,HyperLogLog 算法得到改进,优化了计数统计时的内存使用效率,7 更加优秀。
更好的内存统计报告。
如果不为了API向后兼容,我们将不再使用slave一词。
如何查看自己的 Linux 是32位还是64位?准备64位的
getconf LONG_BIT
#返回是多少就是几位
️ 1)Linux 环境安装 Redis 必须先具备 gcc 编译环境
GCC 是 Linux 下的一个编译程序,是C程序的编译工具。
GCC(GNU Compiler Collection)是 GNU(GNU’s Not Unix)计划提供的编译器家族,它能够支持 C,C++,Objective-C,Fortran,Java 和 Ada 等等程序设计语言前端,同时能够运行在 x86,x86-64,IA-64,PowerPC,SPARC 和 Alpha 等等几乎目前所有的硬件平台上。鉴于这些特征,以及 GCC 编译代码的高效性,使得 GCC 成为绝大多数自由软件开发编译的首选工具。虽然对于程序员们来说,编译器只是一个工具,除了开发和维护人员,很少有人关注编译器的发展,但是 GCC 的影响力是如此之大,它的性能提升甚至有望改善所有的自由软件的运行效率,同时它的内部结构的变化也体现出现代编译器发展的新特征。
gcc -v
安装 Redis 之前需要具备 c++ 库环境。
yum -y install gcc- c++
️ 1)查看自己 Redis 版本的命令
redis-server -v
安全 Bug 按照官网提示,升级成为 6.0.8 及以上。
本次使用 Redis7.0。
️ 1)下载获得 redis-7.0.0.tar.gz
后将它放入 Linux 目录 /opt
wget https://download.redis.io/releases/redis-7.0.0.tar.gz
ls -l redis-7.0.0.tar.gz
️ 2)解压
tar -zxvf redis-7.0.0.tar.gz
️ 3)编译安装
#进入目录
cd redis-7.0.0
#在redis-7.0.0目录下执行make命令编译安装
make && make install
....
#安装成功
Hint: It's a good idea to run 'make test' ;)
️ 4)查看默认安装目录:usr/local/bin
Linux 下的 /usr/local
类似 Windows 系统的 C:\Program Files
在安装目录下有以下几个工具:
redis-benchmark
:性能测试工具,服务启动后运行该命令,看看性能如何;redis-check-aof
:修复有问题的 AOF 文件;redis-check-dump
:修复有问题的 dump.rdb 文件;redis-cli
:客户端,操作入口;redis-sentinel
:Redis 集群使用;redis-server
:Redis 服务器启动命令。️ 5)配置文件
将默认的配置文件 redis.conf
拷贝到自己定义好的一个路径下,比如 /myredis
cd redis-7.0.0
mkdir /myredis
cp redis.conf /myredis/redis7.conf
️ 6)修改/myredis目录下redis7.conf配置文件做初始化设置
redis.conf 配置文件,改完后确保生效,记得重启
基础配置:
默认 daemonize no
,修改为 daemonize yes
。
默认 protected-mode yes
,修改为 protected-mode no
。
默认 bind 127.0.0.1
,改为 直接注释掉(默认bind 127.0.0.1只能本机访问)或改成本机IP地址,否则影响远程IP连接。
添加 Redis 连接密码,改为 requirepass 123456
(密码)
️ 7)启动服务
在 /usr/local/bin 目录下运行 redis-server
,启用 /myredis 目录下的 redis.conf 配置文件。
redis-server /myredis/redis7.conf
ps -ef | grep redis | grep -v grep
️ 8)连接服务
redis-cli -a 123456 -p 6379
查看服务端和客服端运行:
ps -ef | grep redis
️ 9)关闭
单实例关闭:
redis-cli -a 123456 shutdown
多实例关闭,指定端口关闭:
redis-cli -p 6379 shutdown
1)停止 redis-server 服务
redis-cli -p 6379 shutdown
ps -ef | grep redis
2)删除 /usr/local/lib 目录下与 redis 相关的文件
#查看
ls -l /usr/local/bin/redis-*
#删除
rm -rf /usr/local/bin/redis-*
ls -l /usr/local/bin/redis-*
官网:https://redis.io/docs/data-types/
这里说的数据类型是value的数据类型,key的类型都是字符串。
️ 1)字符串 String
String 是 Redis 最基本的类型,一个 key 对应一个 value。
String 类型是二进制安全的
,意思是 Redis 的 String 可以包含任何数据,比如图片或者序列化的对象 。
String 类型是 Redis 最基本的数据类型,一个 Redis 中字符串 value 最多可以是 512M
。
️ 2)列表 List
Redis 列表是简单的字符串列表,按照插入顺序排序。可以添加一个元素到列表的 头部
(左边)或 尾部
(右边)。
它的底层实际是个 双端链表
,最多可以包含 2^32 - 1
个元素 (4294967295,每个列表超过40亿个元素)。
️ 3)哈希表 Hash
Redis Hash 是一个 String 类型的 field(字段)和 value(值)的映射表,Hash 特别适合用于存储对象。
Redis 中每个 Hash 可以存储 2^32 - 1
键值对(40多亿)。
️ 4)集合 Set
Redis 的 Set 是 String 类型的 无序集合
。集合成员是唯一的,这就意味着集合中不能出现重复的数据,集合对象的编码可以是 intset 或者 hashtable。
Redis 中 Set 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。
集合中最大的成员数为 2^32 - 1
(4294967295,每个集合可存储40多亿个成员)。
️ 5)有序集合 ZSet
ZSet(sorted set:有序集合)
Redis ZSet 和 Set 一样也是 String 类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个 double 类型的分数
,Redis 正是通过分数来为集合中的成员进行从小到大的排序。
ZSet 的成员是唯一的,但分数(score)却可以重复。
ZSet 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。 集合中最大的成员数为 2^32 - 1
。
️ 6)地理空间 GEO
Redis GEO 主要用于存储 地理位置
信息,并对存储的信息进行操作,包括:
️ 7)基数统计 HyperLogLog
HyperLogLog 是用来做 基数统计
的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定且是很小的。
在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。
但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。
️ 8)位图 Bitmap
Bit arrays(or simply bitmaps,可以称之为 位图
)
上图由许许多多的小格子组成,每一个格子里面只能放 1 或者 0,用它来判断 Y/N 状态说的专业点,每一个个小格子就是一个个 bit。
由 0 和 1 状态表现的二进制位的 bit 数组。
️ 9)位域 Bitfield
通过 Bitfield 命令可以一次性操作多个 比特位域
(指的是连续的多个比特位),它会执行一系列操作并返回一个响应数组,这个数组中的元素对应参数列表中的相应操作的执行结果。
即通过 Bitfield 命令可以一次性对多个比特位域进行操作。
️ 10)流 Stream
Redis Stream 是 Redis 5.0 版本新增加的数据结构。
Redis Stream 主要用于消息队列(MQ,Message Queue)。Redis 本身是有一个 Redis 发布订阅(pub/sub)来实现消息队列的功能,但它有个缺点就是消息无法持久化,如果出现网络断开、Redis 宕机等,消息就会被丢弃。
简单来说发布订阅(pub/sub)可以分发消息,但无法记录历史消息。
而 Redis Stream 提供了消息的持久化和主备复制功能,可以让任何客户端访问任何时刻的数据,并且能记住每一个客户端的访问位置,还能保证消息不丢失。
官网:https://redis.io/commands/
中文参考:http://www.redis.cn/commands.html、http://doc.redisfans.com/
与 Redis 键相关的基本命令:
keys *
:查看当前库所有的 key。KEYS pattern
:查找所有符合给定模式(pattern)的 key 。DEL key
:该命令用于在 key 存在时删除 key。unlink key
:非阻塞删除,仅仅将 key 从 keyspace 元数据中删除,真正的删除会在后续异步中操作。DUMP key
:序列化给定 key ,并返回被序列化的值。TYPE key
:返回 key 所储存的值的类型。EXISTS key
:检查给定 key 是否存在。EXPIRE key seconds
:为给定 key 设置过期时间,以秒计。EXPIREAT key timestamp
:EXPIREAT 的作用和 EXPIRE 类似,都用于为 key 设置过期时间。 不同在于 EXPIREAT 命令接受的时间参数是 UNIX 时间戳。PEXPIRE key milliseconds
:设置 key 的过期时间以毫秒计。PEXPIREAT key milliseconds-timestamp
:设置 key 过期时间的时间戳(unix timestamp)以毫秒计。MOVE key db[0-15]
:将当前数据库的 key 移动到给定的数据库 db 当中。SELECT dbindex
:切换数据库【0-15】,默认为 0。PERSIST key
:移除 key 的过期时间,key 将持久保持。PTTL key
:以毫秒为单位返回 key 的剩余的过期时间。TTL key
:以秒为单位,返回给定 key 的剩余生存时间(TTL,time to live)。-1 表示永不过期,-2 表示已过期。RANDOMKEY
:从当前数据库中随机返回一个 key 。RENAME key newkey
:修改 key 的名称。RENAMENX key newkey
:仅当 newkey 不存在时,将 key 改名为 newkey 。dbsize
:查看当前数据库 key 的数量。flushdb
:清空当前库。flushall
:清空全部库。命令不区分大小写,而 key 是区分大小写的。
帮助命令:
help @类型
:help @string
、help @list
、help @hyperloglog
、…
地址:https://redis.io/docs/data-types/strings/
️ 1)最常用
SET key value [NX | XX] [GET] [EX seconds | PX milliseconds | EXAT unix-time-seconds | PXAT unix-time-milliseconds | KEEPTTL]
SET 命令有 EX、PX、NX、XX 以及 KEEPTTL 五个可选参数,其中 KEEPTTL 为 6.0 版本添加的可选参数,其它为 2.6.12 版本添加的可选参数。
EX seconds
:以秒为单位设置过期时间;PX milliseconds
:以毫秒为单位设置过期时间;EXAT timestamp
:设置以秒为单位的UNIX时间戳所对应的时间为过期时间;PXAT milliseconds-timestamp
:设置以毫秒为单位的UNIX时间戳所对应的时间为过期时间;NX
:键不存在的时候设置键值;XX
:键存在的时候设置键值;KEEPTTL
:保留设置前指定键的生存时间;GET
:返回指定键原本的值,若键不存在时返回 nil。SET 命令使用 EX、PX、NX 参数,其效果等同于 SETEX、PSETEX、SETNX 命令。根据官方文档的描述,未来版本中SETEX、PSETEX、SETNX 命令可能会被淘汰。
EXAT、PXAT 以及 GET 为 Redis 6.2 新增的可选参数。
返回值:
设置成功则返回 OK;返回 nil 为未执行 SET 命令,如不满足 NX、XX 条件等。
若使用 GET 参数,则返回该键原来的值,或在键不存在时返回 nil 。
️ 2)同时设置/获取多个键值
语法:
# 同时设置一个或多个 key-value 对;返回值 OK
MSET key value [key value ...]
# 获取所有(一个或多个)给定 key 的值;返回 list of values
MGET key [key ...]
# 同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在;返回值 1 or 0
MSETNX key value [key value ...]
案例:
redis> MSET key1 "Hello" key2 "World"
"OK"
redis> GET key1
"Hello"
redis> GET key2
"World"
redis> MGET key1 key2 nonexisting
1) "Hello"
2) "World"
3) (nil)
redis> MSETNX key1 "Hello" key2 "World"
(integer) 1
redis> MSETNX key2 "new" key3 "msetnx"
(integer) 0
redis> MGET key1 key2 key3
1) "Hello"
2) "World"
3) (nil)
️ 3)获取指定区间范围内的值
语法:
# 获取指定区间范围内的值,类似between...and的关系,从零到负一表示全部
GETRANGE key start end
# 设置指定区间范围内的值
SETRANGE key offset value
案例:
redis> SET mykey "This is a string"
"OK"
redis> GETRANGE mykey 0 3
"This"
redis> GETRANGE mykey -3 -1
"ing"
redis> GETRANGE mykey 0 -1
"This is a string"
redis> GETRANGE mykey 10 100
"string"
redis> SET key1 "Hello World"
"OK"
redis> SETRANGE key1 6 "Redis"
(integer) 11
redis> GET key1
"Hello Redis"
️ 4)数值增减
语法:
# 递增 1
INCR key
# 递增指定步数
INCRBY key increment
举例:
redis> SET key1 "10"
"OK"
redis> INCR key1
(integer) 11
redis> GET key1
"11"
redis> INCRBY key1 5
(integer) 16
redis> GET key1
"16"
语法:
# 递减 1
DECR key
# 递减指定步数
DECRBY key decrement
举例:
redis> SET mykey "10"
"OK"
redis> DECR mykey
(integer) 9
redis> DECRBY mykey 3
(integer) 6
redis> SET mykey "1234293482390480948029348230948"
"OK"
redis> DECR mykey
(error) value is not an integer or out of range
一定要是数字才能进行加减。
️ 5)获取字符串长度和内容追加
语法:
STRLEN key #获取字符串长度
APPEND key value #内容追加
如果键已经存在并且是字符串,则此命令将在字符串末尾附加值。若键不存在,则创建该键并将其设置为空字符串,所以在这种特殊情况下,APPEND 将类似于 SET。
举例:
redis> SET mykey "Hello world"
"OK"
redis> STRLEN mykey
(integer) 11
redis> STRLEN nonexisting
(integer) 0
redis> EXISTS mykey
(integer) 0
redis> APPEND mykey "Hello"
(integer) 5
redis> APPEND mykey " World"
(integer) 11
redis> GET mykey
"Hello World"
️ 6)GETSET
GETSET key value
原子地将 key 设置为 value,并返回存储在 key 中的旧值。成功 SET 操作后,将放弃与密钥相关的任何先前生存时间。
举例:
redis> SET mykey "Hello"
"OK"
redis> GETSET mykey "World"
"Hello"
redis> GET mykey
"World"
️ 7)应用场景
比如抖音无限点赞某个视频或者商品,点一下加一次:
redis> INCR items:1
(integer) 1
redis> INCR items:1
(integer) 2
redis> INCR items:1
(integer) 3
分布式锁:
SET key value [NX | XX] [EX seconds | PX milliseconds]
redis> SET lock:item:1 1 EX 10 NX
"OK"
redis> SET lock:item:1 1 EX 10 NX
(nil)
地址:https://redis.io/docs/data-types/lists/
Redis 列表是字符串值的双端链表。容量是2的32次方减1个元素,大概40多亿。Redis 列表通常用于:
在 left、right 都可以插入添加,如果键不存在,创建新的链表;如果键已存在,新增内容;如果值全移除,对应的键也就消失了。
底层实际是个双向链表,对两端的操作性能很高,通过索引下标的操作中间的节点性能会较差。
️ 1)lpush
/rpush
/lrange
# 在列表的开头插入所有指定值。如果键不存在,则在执行操作之前将其创建为空列表。
LPUSH key element [element ...]
RPUSH key element [element ...]
LRANGE key start stop
LRANGE 返回存储在键处的列表的指定元素。偏移量 start 和 stop 是从零开始的索引,0 是列表的第一个元素(列表的开头),1 是下一个元素,依此类推。
这些偏移也可以是负数,表示从列表末尾开始的偏移。例如,-1 是列表的最后一个元素,-2 是倒数第二个元素,依此类推。
举例:
redis> LPUSH list1 1 2 3
(integer) 3
redis> LRANGE list1 0 -1
1) "3"
2) "2"
3) "1"
redis> RPUSH list2 10 11 12
(integer) 3
redis> LRANGE list2 0 -1
1) "10"
2) "11"
3) "12"
️ 2) lpop
/rpop
语法:
LPOP key [count]
删除并返回存储在列表的第一个元素。默认情况下,该命令从列表的开头弹出一个元素。当提供可选的 count 参数时,根据列表的长度,取出包含最多 count 个元素。
RPOP key [count]
删除并返回存储在列表的最后一个元素。默认情况下,该命令从列表末尾弹出一个元素。当提供可选的 count 参数时,根据列表的长度,取出包含最多 count 个元素。
举例:
redis> RPUSH list1 1 2 3 4 5 6
(integer) 6
redis> LPOP list1
"1"
redis> RPOP list1
"6"
redis> RPOP list1 2
1) "5"
2) "4"
redis> LRANGE list1 0 -1
1) "2"
2) "3"
️ 3)LINDEX
语法:
LINDEX key index
返回列表中索引下标的元素。索引从 0 开始,因此 0 表示第一个元素,1 表示第二个元素,依此类推。可以使用负索引来指定从列表尾部开始的元素。-1 表示最后一个元素,-2 表示倒数第二个元素,依此类推。
如果键处的值不是列表,则返回错误。
举例:
redis> LPUSH list1 1 2 3 4
(integer) 4
redis> LINDEX list1 0
"4"
redis> LINDEX list1 -1
"1"
️ 4)LLEN
语法:
LLEN key
返回键列表的长度。若键不存在,则将其解释为空列表,并返回 0。当键中存储的值不是列表时,将返回错误。
️ 5)LREM
语法:
LREM key count element
从 left 往 right 删除 count 个值等于 element 的元素首次计数,返回的值为实际删除的数量。
注意,不存在的键被视为空列表,所以当键不存在时,命令将始终返回 0。
举例:
redis> LPUSH list1 1 1 2 2 3 3 3 4
(integer) 8
redis> LREM list1 2 3
(integer) 2
redis> LRANGE list1 0 -1
1) "4"
2) "3"
3) "2"
4) "2"
5) "1"
6) "1"
redis> LREM list1 0 2
(integer) 2
redis> LRANGE list1 0 -1
1) "4"
2) "3"
3) "1"
4) "1"
️ 6)LTRIM
LTRIM key start stop
截取指定范围的值后再赋值给 key。
redis> RPUSH list1 1 2 3 4 5
(integer) 5
redis> LTRIM list1 1 3
"OK"
redis> LRANGE list1 0 -1
1) "2"
2) "3"
3) "4"
️ 7)RPOPLPUSH
RPOPLPUSH source destination
原子的返回并删除存储在源位置的列表的最后一个元素(尾部),并将该元素推送到存储在目标位置的列表中的第一个元素(头部)。
redis> RPUSH list1 1 2 3 4
(integer) 4
redis> RPOPLPUSH list1 list2
"4"
redis> LRANGE list1 0 -1
1) "1"
2) "2"
3) "3"
redis> LRANGE list2 0 -1
1) "4"
️ 8)LSET
LSET key index element
将索引处的列表元素设置为元素。超出范围的索引返回错误。
redis> LPUSH list1 1 2 3
(integer) 3
redis> LSET list1 1 0
"OK"
redis> LRANGE list1 0 -1
1) "3"
2) "0"
3) "1"
️ 9)LINSERT
LINSERT key pivot element
在列表某个已有值的前后再添加具体值。当键不存在时,它被视为空列表,不执行任何操作。当键存在但不包含列表值时,将返回错误。
redis> LPUSH list1 r e d i s r
(integer) 6
redis> LINSERT list1 BEFORE r a
(integer) 7
redis> LRANGE list1 0 -1
1) "a"
2) "r"
3) "s"
4) "i"
5) "d"
6) "e"
7) "r"
️ 10)应用场景
例如微信公众号订阅的消息:
LPUSH subscribe:wuid 10 11
LRANGE subscribe:wuid 0 9
️ 1)hset
/hget
/hmset
/hmget
/hgetall
/hdel
# 存储
HSET key field value [field value ...]
# 获取单个
HGET key field
# 已标记过期
HMSET key field value [field value ...]
# 获取多个
HMGET key field [field ...]
# 获取所有
HGETALL key
# 删除
HDEL key field [field ...]
举例:
redis> HSET hash1 f1 v1
(integer) 1
redis> HGET hash1 f1
"v1"
redis> HMSET hash1 f2 v2 f3 v3
"OK"
redis> HMGET hash1 f2 f3
1) "v2"
2) "v3"
redis> HGETALL hash1
1) "f1"
2) "v1"
3) "f2"
4) "v2"
5) "f3"
6) "v3"
redis> HDEL hash1 f1 f2
(integer) 2
redis> HGETALL hash1
1) "f3"
2) "v3"
️ 2)HLEN
# 返回存储键的哈希中包含的字段数。
HLEN key
️ 3)HEXISTS
# 判断hash是否存在field键
HEXISTS key field
️ 4) HKEYS
/HVALS
# 返回hash所有的 field
HKEYS key
# 返回hash所有的 values
HVALS key
redis> HSET myhash field1 "Hello"
(integer) 1
redis> HSET myhash field2 "World"
(integer) 1
redis> HVALS myhash
1) "Hello"
2) "World"
redis> HKEYS myhash
1) "field1"
2) "field2"
️ 5)HINCRBY
/HINCRBYFLOAT
HINCRBY key field increment
HINCRBYFLOAT key field increment
将存储在key中的哈希的字段的数字递增。如果密钥不存在,则创建一个保存哈希的新密钥。如果字段不存在,则在执行操作之前将值设置为0。
redis> HSET myhash field 5
(integer) 1
redis> HINCRBY myhash field 1
(integer) 6
redis> HINCRBY myhash field -1
(integer) 5
redis> HSET mykey field 10.50
(integer) 1
redis> HINCRBYFLOAT mykey field 0.1
"10.6"
redis> HINCRBYFLOAT mykey field -5
"5.6"
️ 6)HSETNX
HSETNX key field value
仅当字段还不存在时,才将存储在key的哈希中的字段设置为值。如果密钥不存在,则创建一个保存哈希的新密钥。如果字段已存在,则此操作无效。
redis> HSETNX myhash field "Hello"
(integer) 1
redis> HSETNX myhash field "World"
(integer) 0
redis> HGET myhash field
"Hello"
️ 7)应用场景
JD购物车早期设计,目前不再采用,当前小中厂可用。
hset shopcar:uid1024 334488 1
hset shopcar:uid1024 334477 1
hincrby shopcar:uid1024 334477 1
hlen shopcar:uid1024
hgetall shopcar:uid1024
Redis 集合是唯一字符串(成员)的无序集合。
️ 1)SADD
SADD key member [member ...]
将指定的成员添加到集合。已存在此集合成员的将被忽略。如果键不存在,则在添加指定成员之前创建一个新集。
当键中存储的值不是集合时,将返回错误。
举栗子:
redis> SADD set1 "HELLO" "WORLD" "REDIS" "HELLO"
(integer) 3
redis> SMEMBERS set1
1) "REDIS"
2) "WORLD"
3) "HELLO"
️ 2)SMEMBERS
SMEMBERS key
返回键的集合值的所有成员。
redis> SADD set1 "HELLO" "WORLD" "REDIS" "HELLO"
(integer) 3
redis> SMEMBERS set1
1) "REDIS"
2) "WORLD"
3) "HELLO"
️ 3)SISMEMBER
/SMISMEMBER
SISMEMBER key member
SMISMEMBER key member [member ...]
返回成员是否是集合的成员。
返回每个成员是否是集合的成员。对于每个成员,如果值是集合的成员,则返回1;如果元素不是集合的成员或键不存在,则返回0。
举栗子:
redis> SADD myset "one"
(integer) 1
redis> SISMEMBER myset "one"
(integer) 1
redis> SISMEMBER myset "two"
(integer) 0
redis> SMISMEMBER myset "one" "two"
1) (integer) 1
2) (integer) 0
️ 4)SREM
SREM key member [member ...]
从集合中删除指定的成员。将忽略不是此集合成员的指定成员。如果键不存在,则将其视为空集,此命令返回0。
redis> SADD myset 1 2 3 4
(integer) 4
redis> SREM myset 1 3
(integer) 2
redis> SMEMBERS myset
1) "2"
2) "4"
️ 5)SCARD
SCARD key
返回集合的集合基数(元素数)。
redis> SADD set1 1 2 3
(integer) 3
redis> SCARD set1
(integer) 3
️ 6)SRANDMEMBER
SRANDMEMBER key [count]
当只使用key参数时,从key集合中返回一个随机元素。集合内元素不删除
。
如果使用count参数为正数,则返回一个不同元素的数组。数组的长度是count或集合的基数(SCARD),以较低者为准。
如果使用count参数为负数,则允许命令多次返回同一元素。返回的元素数是指定计数的绝对值。
举栗子:
redis> SADD myset one two three
(integer) 3
redis> SRANDMEMBER myset
"two"
redis> SRANDMEMBER myset 2
1) "two"
2) "one"
redis> SRANDMEMBER myset -5
1) "one"
2) "two"
3) "two"
4) "one"
5) "one"
️ 7)SPOP
SPOP key [count]
从集合值中删除并返回一个或多个随机成员。
默认情况下,该命令从集合中弹出一个成员。当提供可选的count参数时,弹出最多count个成员组成或集合的基数个。
redis> SADD set1 1 2 3 4 5
(integer) 5
redis> SPOP set1
"3"
redis> SPOP set1 3
1) "2"
2) "1"
3) "5"
redis> SMEMBERS set1
1) "4"
redis> SPOP set1 3
1) "4"
redis> SMEMBERS set1
(empty array)
️ 8)SMOVE
SMOVE source destination member
将成员从源集合移动到目标集合。此操作是原子操作。
如果源集合不存在或不包含指定的元素,则不执行任何操作,并返回 0。否则,元素将从源集中删除并添加到目标集中。如果指定的元素已存在于目标集中,则仅从源集中删除该元素。
redis> SADD set1 1 2 3
(integer) 3
redis> SADD set2 3 4
(integer) 2
redis> SMOVE set1 set2 1
(integer) 1
redis> SMOVE set1 set2 3
(integer) 1
redis> SMEMBERS set1
1) "2"
redis> SMEMBERS set2
1) "1"
2) "3"
3) "4"
️ 9)集合运算
现存在集合 A(元素 a b c 1 2)、B(元素 1 2 3 a z)
️ 集合的差集运算 A-B,即属于A但不属于B的元素构成的集合
# 返回由第一个集合和所有连续集合之间的差异产生的集合的成员。
SDIFF key [key ...]
举栗:
redis> SADD KEYA a b c 1 2
(integer) 5
redis> SADD KEYB 1 2 3 a z
(integer) 5
redis> SDIFF KEYA KEY B #不存在的键被视为空集。
1) "b"
2) "1"
3) "c"
4) "a"
5) "2"
redis> SDIFF KEYA KEYB
1) "b"
2) "c"
️ 集合的并集运算 A ∪ B,属于A或者属于B的元素合并后的集合
SUNION key [key ...] #返回由所有给定集合的并集产生的集合的成员。
举栗:
redis> SADD KEYA a b c 1 2
(integer) 5
redis> SADD KEYB 1 2 3 a z
(integer) 5
redis> SUNION KEYA KEYB
1) "2"
2) "c"
3) "a"
4) "z"
5) "3"
6) "1"
7) "b"
️ 集合的交集运算 A ∩ B,属于A同时也属于B的共同拥有的元素构成的集合
SINTER key [key ...] #返回由所有给定集合的交集产生的集合的成员
不存在的键被视为空集。如果其中一个键是空集,则生成的集也是空的(因为与空集相交的集总是导致空集)。
redis> SADD KEYA a b c 1 2
(integer) 5
redis> SADD KEYB 1 2 3 a z
(integer) 5
redis> SINTER KEYA KEYB
1) "1"
2) "a"
3) "2"
redis> SINTER KEYA KEYB KEYC
(empty array)
Redis 7 新命令:
该命令类似于 SINTER,但它只返回结果的基数,而不是返回结果集。返回所有给定集合的交集所产生的集合的基数。
SINTERCARD numkeys key [key ...] [LIMIT limit]
举栗:
redis> SADD KEYA a b c 1 2
(integer) 5
redis> SADD KEYB 1 2 3 a z
(integer) 5
redis> SINTERCARD 2 KEYA KEYB
(integer) 3
redis> SINTERCARD 2 KEYA KEYB LIMIT 2
(integer) 2
️ 10)应用场景
️ 微信抽奖小程序
用户参与抽奖
SADD key uid
显示多少人参与抽奖
SCARD key
随机抽奖N人
SRANDMEMBER key 2 # 随机抽奖2个人,元素不删除
SPOP key 3 # 随机抽奖3个人,元素会删除
️ 微信朋友圈点赞查看点赞朋友
新增点赞
SADD pub:msgid uid
取消点赞
SREM pub:msgid uid
显示点赞的朋友
SMEMBERS pub:msgid
点赞数
SCARD pub:msgid
判断是否点赞过
SISMEMBER pub:msgid uid
️ 可能认识的人
用户A的好友,用户B可能认识
SDIFF ua ub
用户B的好友,用户A可能认识
SDIFF ub ua
Redis 有序集合是按关联分数排序的唯一字符串(成员)的集合。当多个字符串具有相同的分数时,这些字符串按字典顺序排列。
️ 1)ZADD
ZADD key [NX | XX] [GT | LT] [CH] [INCR] score member [score member ...]
将具有指定分数的所有指定成员添加到排序集。可以指定多个分数/成员对。如果指定的成员已经是排序集的成员,则会更新分数并将元素重新插入到正确的位置,以确保正确排序。
redis> ZADD zset1 60 a 70 b 80 c 90 d
(integer) 4
redis> ZRANGE zset1 0 -1 WITHSCORES
1) "a"
2) "60"
3) "b"
4) "70"
5) "c"
6) "80"
7) "d"
8) "90"
redis> ZRANGE zset1 0 -1
1) "a"
2) "b"
3) "c"
4) "d"
️ 2)ZRANGE
ZRANGE key start stop [BYSCORE | BYLEX] [REV] [LIMIT offset count] [WITHSCORES]
按照元素分数从小到大的顺序返回索引从start到stop之间的所有元素。
redis> ZADD zset1 60 a 70 b 80 c 90 d
(integer) 4
redis> ZRANGE zset1 0 -1 WITHSCORES
1) "a"
2) "60"
3) "b"
4) "70"
5) "c"
6) "80"
7) "d"
8) "90"
redis> ZRANGE zset1 0 -1
1) "a"
2) "b"
3) "c"
4) "d"
️ 3)ZREVRANGE
从Redis 6.2.0版开始,此命令被视为已弃用。
在迁移或编写新代码时,可以用带REV参数的ZRANGE替换它。
语法:
ZREVRANGE key start stop [WITHSCORES]
返回排序集合中指定的元素范围。元素被认为是从最高到最低的顺序。对于分数相等的元素,使用降序词典顺序。
除了相反的顺序,ZREVRANGE与ZRANGE类似。
举栗:
redis> ZADD zset1 60 a 70 b 80 c 90 d
(integer) 4
redis> ZRANGE zset1 0 -1 WITHSCORES
1) "a"
2) "60"
3) "b"
4) "70"
5) "c"
6) "80"
7) "d"
8) "90"
redis> ZREVRANGE zset1 0 -1 WITHSCORES
1) "d"
2) "90"
3) "c"
4) "80"
5) "b"
6) "70"
7) "a"
8) "60"
️ 4)ZRANGEBYSCORE
从Redis 6.2.0版开始,此命令被视为已弃用。
在迁移或编写新代码时,可以用带BYSCORE参数的ZRANGE替换它。
语法:
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
返回排序集合中SCORE介于min和max之间的所有元素(包括SCORE等于min或max的元素)。元素被认为是从低到高分排序的。
具有相同分数的元素按字典顺序返回(这是从Redis中的排序集实现的属性得出的,不涉及进一步的计算)。
可选的LIMIT参数只能用于获取匹配元素的范围(类似于SQL中的 SELECT LIMIT 偏移量,count)。负计数返回偏移量中的所有元素。请记住,如果偏移量很大,则在返回元素之前,需要遍历已排序集合中的偏移元素,这可能会增加O(N)时间复杂性。
举例:
redis> ZADD zset1 60 a 70 b 80 c 90 d
(integer) 4
redis> ZRANGEBYSCORE zset1 60 70 # 60>= SCORE <=70
1) "a"
2) "b"
redis> ZRANGEBYSCORE zset1 60 70 WITHSCORES
1) "a"
2) "60"
3) "b"
4) "70"
redis> ZRANGEBYSCORE zset1 (60 70 # 60> SCORE <=70
1) "b"
redis> ZRANGEBYSCORE zset1 (60 (80 # 60> SCORE <80
1) "b"
redis> ZRANGEBYSCORE zset1 (60 (90 LIMIT 1 1
1) "c"
redis> ZRANGEBYSCORE zset1 (60 (90 LIMIT 0 -1
1) "b"
2) "c"
️ 5)ZSCORE
ZSCORE key member
返回排序集合中成员的得分。
如果排序集合中不存在成员,或者键不存在,则返回nil。
redis> ZADD myzset 1 "one"
(integer) 1
redis> ZSCORE myzset "one"
"1"
redis> ZSCORE myset "two"
(nil)
️ 6)ZCARD
ZCARD key # 返回排序集合基数(元素数)
举例:
redis> ZADD myzset 1 "one"
(integer) 1
redis> ZADD myzset 2 "two"
(integer) 1
redis> ZCARD myzset
(integer) 2
️ 7)ZCOUNT
ZCOUNT key min max
返回排序集中得分介于最小值和最大值之间的元素数。
redis> ZADD myzset1 1 "one" 2 "two" 3 "three"
(integer) 3
redis> ZCOUNT myzset1 1 3
(integer) 3
redis> ZCOUNT myzset1 (1 3
(integer) 2
️ 8)ZINCRBY
ZINCRBY key increment member
按增量递增排序集合中成员的分数。如果成员在排序集中不存在,则将以增量作为其分数。如果键不存在,将创建一个新的排序集,其中指定成员作为其唯一成员。
分数值应为数值的字符串表示形式,并接受双精度浮点数。可以提供负值来减少分数。
举例:
redis> ZADD myzset 1 "one"
(integer) 1
redis> ZINCRBY myzset 2 "one"
"3"
redis> ZRANGE myzset 0 -1 WITHSCORES
1) "one"
2) "3"
redis> ZINCRBY myzset -1 "one"
"2"
redis> ZRANGE myzset 0 -1 WITHSCORES
1) "one"
2) "2"
️ 9)ZREM
ZREM key member [member ...]
删除集合中指定的元素。
redis> ZADD myzset1 1 "one" 2 "two" 3 "three"
(integer) 3
redis> ZREM myzset1 "one" "two" "redis"
(integer) 2
redis> ZRANGE myzset1 0 -1
1) "three"
️ 10)ZMPOP
ZMPOP numkeys key [key ...] [COUNT count]
从键名列表中的第一个非空排序集合中弹出一个或多个元素(即成员分数对)。
当使用MIN修饰符时,弹出的元素是第一个非空排序集中得分最低的元素。MAX修饰符时会弹出得分最高的元素。可选COUNT参数可用于指定要弹出的元素数,默认设置为1。弹出元素的数量是排序集的基数或COUNT值。
举例:
redis> ZADD myzset 1 "one" 2 "two" 3 "three"
(integer) 3
redis> ZMPOP 1 myzset MIN
1) "myzset"
2) 1) 1) "one"
2) "1"
redis> ZRANGE myzset 0 -1 WITHSCORES
1) "two"
2) "2"
3) "three"
4) "3"
redis> ZMPOP 1 myzset MAX COUNT 10
1) "myzset"
2) 1) 1) "three"
2) "3"
2) 1) "two"
2) "2"
redis> ZADD myzset2 4 "four" 5 "five" 6 "six"
(integer) 3
redis> ZMPOP 2 myzset myzset2 MIN COUNT 10
1) "myzset2"
2) 1) 1) "four"
2) "4"
2) 1) "five"
2) "5"
3) 1) "six"
2) "6"
redis> ZRANGE myzset 0 -1 WITHSCORES
(empty array)
redis> ZMPOP 2 myzset myzset2 MAX COUNT 10
(error) object of type 'NoneType' has no len()
redis> ZRANGE myzset2 0 -1 WITHSCORES
(empty array)
️ 11)ZRANK
/ZREVRANK
ZRANK key member [WITHSCORE]
ZREVRANK key member [WITHSCORE]
ZRANK 返回排序集合中成员的排名,分数从低到高排序。
ZREVRANK 返回排序集合中成员的排名,分数从高到低排序。(逆序)
举例:
redis> ZADD myzset 1 "one" 2 "two" 3 "three"
(integer) 3
redis> ZRANK myzset "one"
(integer) 0
redis> ZRANK myzset "three"
(integer) 2
redis> ZREVRANK myzset "one"
(integer) 2
redis> ZREVRANK myzset "three"
(integer) 0
️ 12)应用场景
根据商品销售对商品进行排序显示。
定义商品销售排行榜(sorted set 集合),key 为 goods:sellsort,分数为商品销售数量,值为商品编号。
销售2件商品1001:
ZINCRBY goods:sellsort 2 1001
销售10件商品1002:
ZINCRBY goods:sellsort 10 1002
查看商品销量前10名:
ZRANGE goods:sellsort 0 9 REV WITHSCORES
Redis 位图是字符串数据类型的扩展,可以将字符串视为位向量。
Bit arrays(or simply bitmaps,可以称之为 位图
),由0和1状态表现的二进制位的 bit 数组。
说明:用 String 类型作为底层数据结构实现的一种统计二值状态的数据类型。
位图本质是数组,它是基于 String 数据类型的按位的操作。该数组由多个二进制位组成,每个二进制位都对应一个偏移量(称之为一个索引)。
Bitmap 支持的最大位数是 2^32 位,它可以极大的节约存储空间,使用 512M 内存就可以存储多达 42.9 亿的字节信息(2^32 = 4294967296)。
Bitmap 的偏移量是从零开始算的。
主要用于状态统计:
️ 1)SETBIT
SETBIT key offset value
redis> SETBIT k2 1 1
(integer) 0
redis> SETBIT k2 2 1
(integer) 0
redis> GET k2
"`"
redis> SETBIT k2 2 0
(integer) 1
redis> GET k2
"@"
️ 2)GETBIT
GETBIT key offset
返回存储键的字符串值中偏移量处的位值。
redis> SETBIT mykey 7 1
(integer) 0
redis> GETBIT mykey 0
(integer) 0
redis> GETBIT mykey 7
(integer) 1
redis> GETBIT mykey 100
(integer) 0
️ 3)STRLEN
统计字节数占用多少:
不是字符串长度而是占据几个字节,超过8位后自己按照8位一组一byte再扩容。
redis> SETBIT K2 0 1
(integer) 0
redis> SETBIT K2 7 1
(integer) 0
redis> STRLEN K2
(integer) 1
redis> SETBIT K2 8 1
(integer) 0
redis> STRLEN K2
(integer) 2
0-7位一组;8-15位一组;
️ 4)BITCOUNT
BITCOUNT key [start end [BYTE | BIT]]
全部键里面含有1的有多少个?
redis> SETBIT login:u1:202303 0 1
(integer) 0
redis> SETBIT login:u1:202303 1 1
(integer) 0
redis> SETBIT login:u1:202303 2 1
(integer) 0
redis> SETBIT login:u1:202303 3 1
(integer) 0
redis> SETBIT login:u1:202303 6 1
(integer) 0
redis> BITCOUNT login:u1:202303
(integer) 5
redis> BITCOUNT login:u1:202303 0 31
(integer) 5
redis> BITCOUNT login:u1:202303 0 31 BYTE
(integer) 5
redis> BITCOUNT login:u1:202303 0 31 BIT
(integer) 5
️ 5)BITOP
BITOP destkey key [key ...]
在多个键(包含字符串值)之间执行逐位操作,并将结果存储在目标键中。
BITOP 命令支持四种按位操作:AND
、OR
、XOR
和 NOT
,因此调用该命令的有效形式为:
BITOP AND destkey srckey1 srckey2 srckey3 ... srckeyN
BITOP OR destkey srckey1 srckey2 srckey3 ... srckeyN
BITOP XOR destkey srckey1 srckey2 srckey3 ... srckeyN
BITOP NOT destkey srckey
NOT 是特殊的,它只接受一个输入键,因为它执行位的反转,所以它只作为一元运算符有意义。
操作结果始终存储在 destkey 中。
举栗子:
一系统的用户有1000W,做个用户id和位置的映射:比如0号位对应用户id:uid-230101-don;比如1号位对应用户id:uid-230101-xxx;…
现在需要计算2023年3月8日/9日两天都签到的用户数。
redis> SETBIT 20230308 0 1
(integer) 0
redis> SETBIT 20230308 1 1
(integer) 0
redis> SETBIT 20230308 2 1
(integer) 0
redis> SETBIT 20230308 3 1
(integer) 0
redis> SETBIT 20230309 0 1
(integer) 0
redis> SETBIT 20230309 2 1
(integer) 0
redis> BITCOUNT 20230308
(integer) 4
redis> BITCOUNT 20230309
(integer) 2
redis> BITOP AND destkey 20230308 20230309
(integer) 1
redis> BITCOUNT destkey
(integer) 2
️ 6)应用场景
redis> SETBIT UID1 0 1
(integer) 0
redis> SETBIT UID1 100 1
(integer) 0
redis> SETBIT UID1 364 1
(integer) 0
redis> BITCOUNT UID1
(integer) 3
redis> STRLEN UID1
(integer) 46
按年去存储一个用户的签到情况,365 天只需要 365 / 8 ≈ 46 Byte,1000W 用户量一年也只需要 44 MB 就足够了。
假如是亿级的系统,每天使用1个1亿位的 Bitmap 约占 12 MB 的内存(10^8 / 8 / 1024 / 1024),10天的 Bitmap 的内存开销约为 120 MB,内存压力不算太高。
此外,在实际使用时,最好对 Bitmap 设置过期时间,让 Redis 自动删除不再需要的签到记录以节省内存开销。
️ 1)场景需求
UV:Unique Visitor,独立访客,一般理解为客户端 IP。需要去重考虑。
️ 2)是什么
Redis 在 2.8.9 版本添加了 HyperLogLog 结构。
Redis HyperLogLog 是用来做 基数统计
的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。
在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元的基数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。
但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。
去重复统计功能的基数估计算法 —— HyperLogLog
基数统计:用于统计一个集合中不重复的元素个数,就是对集合去重复后剩余元素的计算。
️ 3)命令
PFADD key [element [element ...]]
将所有元素参数添加到存储在指定为第一个参数的变量名称处的 HyperLogLog 数据结构中。
PFCOUNT key [key ...]
使用单个键调用时,返回由存储在指定键的 HyperLogLog 数据结构计算的近似基数,如果键不存在,则返回 0。
当使用多个键调用时,通过将存储在键的 HyperLogLogs 内部合并为临时 HyperLogLog,返回传递的 HyperLogLog 并集的近似基数。
PFMERGE destkey [sourcekey [sourcekey ...]]
将多个 HyperLogLog 值合并为一个唯一值,该值将近似于源 HyperLogLog 结构的观察集的并集的基数。
举例:
redis> PFADD hll1 foo bar zap a
(integer) 1
redis> PFADD hll2 a b c foo
(integer) 1
redis> PFMERGE hll3 hll1 hll2
"OK"
redis> PFCOUNT hll3
(integer) 6
Redis 在 3.2 版本以后增加了地理位置 GEO 的处理。
️ 1)简介
移动互联网时代 LBS 应用越来越多,交友软件中附近的人、外卖软件中附近的美食店铺、高德地图附近的核酸检查点等等,那这种附近各种形形色色的XXX地址位置选择是如何实现的?
地球上的地理位置是使用二维的经纬度表示,经度范围 (-180, 180],纬度范围 (-90, 90],只要我们确定一个点的经纬度就可以名取得他在地球的位置。
举例:滴滴打车,最直观的操作就是实时记录更新各个车的位置,然后当我们要找车时,在数据库中查找距离我们(坐标x0,y0)附近 R 公里范围内部的车辆。
使用如下伪SQL:
select taxi from position where x0-r < x < x0 + r and y0-r < y < y0+r;
❌ 但是这样会有什么问题呢?
️ 2)GEOADD
GEOADD key [NX | XX] [CH] longitude latitude member [longitude latitude member ...]
将指定的地理空间项目(经度、纬度、名称)添加到指定的关键字。数据以排序集的形式存储在键中,从而可以使用 GEOSEARCH 命令查询项目。
该命令采用标准格式 x,y 的参数,因此必须在纬度之前指定经度。可以索引的坐标是有限制的:非常靠近极点的区域是不可索引的。
注意:没有 GEODEL 命令,因为可以使用 ZREM 删除元素。地理索引结构只是一个排序集。
举栗子:
redis> GEOADD city 116.403963 39.915119 "天安门" 116.403414 39.924091 "故宫" 116.024067 40.362639 "长城"
(integer) 3
redis> TYPE city
"zset"
redis> ZRANGE city 0 -1
1) "天安门"
2) "故宫"
3) "长城"
redis>
️ 3)GEOPOS
GEOPOS key [member [member ...]]
从键里面返回所有给定位置元素的位置(经度和纬度)。
redis> GEOADD city 116.403963 39.915119 "天安门" 116.403414 39.924091 "故宫" 116.024067 40.362639 "长城"
(integer) 3
redis> GEOPOS city 天安门 故宫 长城
1) 1) "116.40396326780319214"
2) "39.91511970338637383"
2) 1) "116.40341609716415405"
2) "39.92409008156928252"
3) 1) "116.02406591176986694"
2) "40.36263993239462167"
redis>
️ 4)GEOHASH
GEOHASH key [member [member ...]]
返回一个或多个元素在地理空间索引的排序集值中的位置的有效 Geohash 字符串。GEOHASH 算法生成的 base32 编码值。
redis> GEOHASH city 天安门 故宫 长城
1) "wx4g0f6f2v0"
2) "wx4g0gfqsj0"
3) "wx4t85y1kt0"
redis>
️ 5)GEODIST
GEODIST key member1 member2 [M | KM | FT | MI]
返回两个给定位置之间的距离。
redis> GEODIST city 天安门 故宫 km
"0.9988"
redis> GEODIST city 天安门 故宫 m
"998.8332"
redis>
️ 6)GEORADIUS
从Redis 6.2.0版开始,此命令被视为已弃用。
迁移或编写新代码时,可以使用BYRADIUS参数将其替换为GEOSEARCH和GEOSEARCHSTORE。
GEORADIUS key longitude latitude radius
[WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC]
[STORE key] [STOREDIST key]
以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。
GEORADIUS city 116.418017 39.914402 10 km WITHDIST WITHCOORD COUNT 10 WITHHASH DESC
GEORADIUS city 116.418017 39.914402 10 km WITHDIST WITHCOORD WITHHASH COUNT 10 DESC
举例:
redis> GEORADIUS city 116.418017 39.914402 10 km WITHDIST WITHCOORD WITHHASH COUNT 10 DESC
1) 1) "故宫"
2) "1.6470"
3) (integer) 4069885568908290
4) 1) "116.40341609716415405"
2) "39.92409008156928252"
2) 1) "天安门"
2) "1.2016"
3) (integer) 4069885555089531
4) 1) "116.40396326780319214"
2) "39.91511970338637383"
redis>
️ 7)GEORADIUSBYMEMBER
从Redis 6.2.0版开始,此命令被视为已弃用。
在迁移或编写新代码时,可以使用BYRADIUS和FROMMEMBER参数将其替换为GEOSEARCH和GEOSEARCHSTORE。
GEORADIUSBYMEMBER key member radius [WITHCOORD]
[WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC] [STORE key]
[STOREDIST key]
此命令与 GEORADIUS 完全相同,唯一的区别是,它不采用经度和纬度值作为要查询区域的中心,而是采用已存在于由排序集表示的地理空间索引中的成员的名称。
redis> GEORADIUSBYMEMBER city 天安门 10 km WITHDIST WITHCOORD WITHHASH COUNT 10 DESC
1) 1) "故宫"
2) "0.9988"
3) (integer) 4069885568908290
4) 1) "116.40341609716415405"
2) "39.92409008156928252"
2) 1) "天安门"
2) "0.0000"
3) (integer) 4069885555089531
4) 1) "116.40396326780319214"
2) "39.91511970338637383"
redis>
️ 8)GEOSEARCH
GEOSEARCH key
| BYBOX width height > [ASC | DESC] [COUNT count [ANY]] [WITHCOORD] [WITHDIST]
[WITHHASH]
返回地理空间信息的排序集的成员,这些成员位于给定形状指定的区域边界内。此命令扩展了 GEORADIUS 命令,因此除了在圆形区域内搜索外,它还支持在矩形区域内搜索。
使用此命令代替已弃用的 GEORADIUS 和 GEORADIUSBYMEMBER 命令。
FROMMEMBER:使用排序集合中给定的现有 member
的位置。
FROMLONLAT:使用给定的 经度
和 纬度
位置。
BYRADIUS:类似于 GEORADIUS,根据给定的 radius
在圆形区域内搜索。
BYBOX:在轴对齐的矩形内搜索,由 height
和 width
确定。
举栗子:
redis> GEOSEARCH city FROMLONLAT 116.418017 39.914402 BYRADIUS 10 km
1) "天安门"
2) "故宫"
redis> GEOSEARCH city FROMLONLAT 116.418017 39.914402 BYRADIUS 10 km WITHDIST WITHCOORD WITHHASH
1) 1) "天安门"
2) "1.2016"
3) (integer) 4069885555089531
4) 1) "116.40396326780319214"
2) "39.91511970338637383"
2) 1) "故宫"
2) "1.6470"
3) (integer) 4069885568908290
4) 1) "116.40341609716415405"
2) "39.92409008156928252"
redis>
redis> GEOSEARCH city FROMMEMBER 天安门 BYBOX 10 10 km WITHDIST WITHCOORD WITHHASH
1) 1) "天安门"
2) "0.0000"
3) (integer) 4069885555089531
4) 1) "116.40396326780319214"
2) "39.91511970338637383"
2) 1) "故宫"
2) "0.9988"
3) (integer) 4069885568908290
4) 1) "116.40341609716415405"
2) "39.92409008156928252"
redis>
️ 1)是什么
Redis 5.0 之前,Redis 消息队列的2种方案:
按照插入顺序排序,你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
所以常用来做 异步队列使用
,将需要延后处理的任务结构体序列化成字符串塞进 Redis 的列表,另一个线程从这个列表中轮询数据进行处理。
List 实现方式其实就是点对点的模式。
Redis 发布订阅(pub/sub)有个缺点就是消息无法持久化,如果出现网络断开、Redis 宕机等,消息就会被丢弃。而且也没有 ACK 机制来保证数据的可靠性,假设一个消费者都没有,那消息就直接被丢弃了。
简单来说发布订阅(pub/sub)可以分发消息,但无法记录历史消息。
Redis 5.0 版本新增了一个更强大的数据结构 —— Stream
。即 Redis 版的 MQ 消息中间件 + 阻塞队列。
Redis Stream 提供了消息的持久化和主备复制功能,可以让任何客户端访问任何时刻的数据,并且能记住每一个客户端的访问位置,还能保证消息不丢失。
️ 2)能干嘛
实现消息队列,它支持消息的持久化、支持自动生成全局唯一 ID、支持 ACK 确认消息的模式、支持消费组模式等,让消息队列更加的稳定和可靠。
️ 3)底层结构和原理说明
Redis Stream 的结构如下所示,它有一个消息链表,将所有加入的消息都串起来,每个消息都有一个唯一的 ID 和对应的内容:
每个 Stream 都有唯一的名称,它就是 Redis 的 key,在我们首次使用 xadd
指令追加消息时自动创建。
Message Content
消息内容。
Consumer group
消费组,通过 XGROUP CREATE
命令创建,同一个消费组可以有多个消费者。
Last_delivered_id
游标,每个消费组会有个游标 last_delivered_id
,任意一个消费者读取了消息都会使游标 last_delivered_id
往前移动。
Consumer
消费者,消费组中的消费者。
Pending_ids
消费者会有一个状态变量,用于记录被当前消费已读取但未 ACK 的消息 ID,如果客户端没有 ACK,这个变量里面的消息 ID 会越来越多,一旦某个消息被 ACK 它就开始减少。这个 pending_ids
变量在 Redis 官方被称之为 PEL(Pending Entries List),记录了当前已经被客户端读取的消息,但是还没有 ACK(Acknowledge character:确认字符),它用来确保客户端至少消费了消息一次,而不会在网络传输的中途丢失了没处理。
️ 4)基本命令理论简介
️ 队列相关指令
XADD
- 添加消息到队列末尾;XTRIM
- 限制 Stream 的长度,如果已经超长会进行截取;XDEL
- 删除消息;XLEN
- 获取 Stream 中的消息长度;XRANGE
- 获取消息列表(可以指定范围),忽略已经删除的消息;XREVRANGE
- 和 XRANGE 相比区别在于反向获取,ID 从大到小;XREAD
- 获取消息(阻塞/非阻塞),返回大于指定 ID 的消息。️ 消费组相关指令
XGROUP CREATE
- 创建消费者组;XREADGROUP GROUP
- 读取消费者组中的消息;XACK
- 将消息标记为"已处理";XGROUP SETID
- 为消费者组设置新的最后递送消息 ID;XGROUP DELCONSUMER
- 删除消费者;XGROUP DESTROY
- 删除消费者组;XPENDING
- 打印待处理消息的详细信息;XCLAIM
- 转移消息的归属权(长期未被处理/无法处理的消息,转交给其他消费者组进行处理);XINFO
- 打印 Stream \ Consumer \ Group 的详细信息;XINFO GROUPS
- 打印消费者组的详细信息;XINFO STREAM
- 打印 Stream 详细信息。️ 四个特殊符号
- +
:最小和最大可能出现的 ID;$
:表示只消费新的消息,当前流中最大的 ID,可用于将要到来的信息;>
:用于 XREADGROUP 命令,表示迄今还没有发送给组中使用者的信息,会更新消费者组的最后 ID;*
:用于 XADD 命令中,让系统自动生成 ID。️ 5)队列相关指令实操
️ XADD
XADD key [NOMKSTREAM] [ [= | ~] threshold
[LIMIT count]] <* | id> field value [field value ...]
XADD
用于向 Stream 队列中添加消息,如果指定的 Stream 队列不存在,则该命令执行时会新建一个 Stream 队列。*
号表示服务器自动生成 MessageID,后面顺序跟着一堆 业务key / value。
生成的消息 ID,有两部分组成,毫秒时间戳-该毫秒内产生的第1条消息
:
redis> XADD mystream * field1 value1 field2 value2 field3 value3
"1678430091999-1"
信息条目指的是序列号,在相同的毫秒下序列号从 0
开始递增,序列号是 64 位长度,理论上在同一毫秒内生成的数据量无法到达这个级别,因此不用担心序列号会不够用。millisecondsTime 指的是 Redis 节点服务器的本地时间,如果存在当前的毫秒时间戳比以前已经存在的数据的时间戳小的话(本地时间钟后跳),那么系统将会采用以前相同的毫秒创建新的 ID,也即 Redis 在增加信息条目时会检查当前 ID 与上一条目的 ID, 自动纠正错误的情况,一定要保证后面的 ID 比前面大,一个流中信息条目的 ID 必须是单调增的
,这是流的基础。
客户端显示传入规则:
Redis 对于 ID 有强制要求,格式必须是 时间戳-自增ID
这样的方式,且后续 ID 不能小于前一个 ID。
Stream 的消息内容,也就是图中的 Message Content 它的结构类似 Hash 结构,以 key-value 的形式存在。
key
:队列名称,如果不存在就创建。id
:消息 id,使用 *
表示由 Redis 生成,可以自定义,但是要自己保证递增性。field value
: 记录项。️ XRANGE
XRANGE key start end [COUNT count]
用于获取消息列表(可以指定范围),忽略删除的消息。
start
表示开始值,-
代表最小值;end
表示结束值,+
代表最大值;count
表示最多获取多少个值;redis> XADD stream1 * k1 v1 k2 v2
"1678431243920-0"
redis> XADD stream1 * k3 v3 k4 v4
"1678431257954-0"
redis> XRANGE stream1 - +
1) 1) "1678431243920-0"
2) 1) "k1"
2) "v1"
3) "k2"
4) "v2"
2) 1) "1678431257954-0"
2) 1) "k3"
2) "v3"
3) "k4"
4) "v4"
️ XREVRANGE
XREVRANGE key end start [COUNT count]
与 XRANGE 的区别在于,获取消息列表元素的方向是相反的,end 在前,start 在后。
redis> XADD stream1 * k1 v1 k2 v2
"1678431405813-0"
redis> XADD stream1 * k3 v3 k4 v4
"1678431409400-0"
redis> XREVRANGE stream1 + -
1) 1) "1678431409400-0"
2) 1) "k3"
2) "v3"
3) "k4"
4) "v4"
2) 1) "1678431405813-0"
2) 1) "k1"
2) "v1"
3) "k2"
4) "v2"
redis>
️ XDEL
XDEL key id [id ...]
使用 XDEL 删除消息(ID)。
redis> XADD stream1 * k1 v1
"1678431756796-0"
redis> XADD stream1 * k2 v2
"1678431763476-0"
redis> XRANGE stream1 - +
1) 1) "1678431756796-0"
2) 1) "k1"
2) "v1"
2) 1) "1678431763476-0"
2) 1) "k2"
2) "v2"
redis> XDEL stream1 1678431756796-0
(integer) 1
redis> XRANGE stream1 - +
1) 1) "1678431763476-0"
2) 1) "k2"
2) "v2"
redis>
️ XLEN
XLEN key
用于获取 Stream 队列的消息的长度。
redis> XLEN stream1
(integer) 1
️ XTRIM
XTRIM key [= | ~] threshold [LIMIT count]
用于对 Stream 的长度进行截取,如超长会进行截取。
MAXLEN
允许的最大长度,对流进行修剪限制长度。
redis> XADD stream1 * k1 v1
"1678435003936-0"
redis> XADD stream1 * k2 v2
"1678435010172-0"
redis> XADD stream1 * k3 v3
"1678435015141-0"
redis> XRANGE stream1 - +
1) 1) "1678435003936-0"
2) 1) "k1"
2) "v1"
2) 1) "1678435010172-0"
2) 1) "k2"
2) "v2"
3) 1) "1678435015141-0"
2) 1) "k3"
2) "v3"
redis> XTRIM stream1 MAXLEN 2
(integer) 1
redis> XRANGE stream1 - +
1) 1) "1678435010172-0"
2) 1) "k2"
2) "v2"
2) 1) "1678435015141-0"
2) 1) "k3"
2) "v3"
redis>
MINID
允许的最小 ID,从某个 ID 值开始比该 ID 值小的将会被抛弃。
redis> XADD stream1 * k1 v1
"1678436286856-0"
redis> XADD stream1 * k2 v2
"1678436291292-0"
redis> XADD stream1 * k3 v3
"1678436296114-0"
redis> XRANGE stream1 - +
1) 1) "1678436286856-0"
2) 1) "k1"
2) "v1"
2) 1) "1678436291292-0"
2) 1) "k2"
2) "v2"
3) 1) "1678436296114-0"
2) 1) "k3"
2) "v3"
redis> XTRIM stream1 MINID 1678436296114-0
(integer) 2
redis> XRANGE stream1 - +
1) 1) "1678436296114-0"
2) 1) "k3"
2) "v3"
redis>
️ XREAD
XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] id [id ...]
用于获取消息(阻塞/非阻塞),只会返回大于指定 ID 的消息。
COUNT
:最多读取多少条消息。
BLOCK
:是否已阻塞的方式读取消息,默认不阻塞,如果 milliseconds
设置为 0,表示永远阻塞。
> XADD stream1 * k1 v1
1678438374296-0
> XADD stream1 * k2 v2
1678438380663-0
> XRANGE stream1 - +
1678438374296-0
k1
v1
1678438380663-0
k2
v2
> XREAD COUNT 1 STREAMS stream1 $
null
> XREAD COUNT 1 STREAMS stream1 0-0
stream1
1678438374296-0
k1
v1
> XREAD COUNT 3 STREAMS stream1 00
stream1
1678438374296-0
k1
v1
1678438380663-0
k2
v2
1️⃣ $
代表特殊 ID,表示以当前 Stream 已经存储的最大的 ID 作为最后一个 ID,当前 Stream 中不存在大于当前最大 ID 的消息,因此此时返回 nil。
2️⃣ 0-0
代表从最小的 ID 开始获取 Stream 中的消息,当不指定 count,将会返回 Stream 中的所有消息,注意也可以使用 0 / 00 / 000。
客户端A:此时会阻塞
> XREAD COUNT 1 BLOCK 0 STREAMS stream1 $
客户端A:添加消息
> XADD stream1 * k3 v3
1678438810766-0
客户端A添加消息后,客户端B则会取出最新消息:
> XREAD COUNT 1 BLOCK 0 STREAMS stream1 $
stream1
1678438810766-0
k3
v3
Stream 的基础方法,使用 xadd
存入消息和 xread
循环阻塞读取消息的方式可以实现简易版的消息队列,交互流程如下:
️ 6)消费组相关指令实操
️ XGROUP CREATE
XGROUP CREATE key group [MKSTREAM] [ENTRIESREAD entries-read]
创建消费者组的时候必须指定 ID。ID 为 0
表示从头开始消费,为 $
表示只消费新的消息,当前 Stream 内消息会全部忽略。
0
- 表示从 Stream 头部开始消费;$
- 表示从 Stream 尾部开始消费;XGROUP CREATE mystream mygroupA 0
XGROUP CREATE mystream mygroupB $
️ XREADGROUP GROUP
读取消费组中的消息,语法格式:
XREADGROUP GROUP group consumer [COUNT count] [BLOCK milliseconds]
[NOACK] STREAMS key [key ...] id [id ...]
>
:表示从第一条尚未被消费的消息开始读取。举栗子:
消费组 mygroupA 内的消费者 consumer1 从 stream1 消息队列中读取所有消息
> XGROUP CREATE stream1 mygroupA 0
OK
> XGROUP CREATE stream1 mygroupB $
OK
> XREADGROUP GROUP mygroupA consumer1 STREAMS stream1 >
stream1
1678438374296-0
k1
v1
1678438380663-0
k2
v2
1678438810766-0
k3
v3
> XREADGROUP GROUP mygroupA consumer1 STREAMS stream1 >
null
> XREADGROUP GROUP mygroupA consumer2 STREAMS stream1 >
null
Stream 中的消息一旦被消费组里的一个消费者读取了,就不能再被该消费组内的其他消费者读取了,即同一个消费组里的消费者不能消费同一条消息。上述例子的 XREADGROUP
命今再执行一次,此时读到的就是空值。
❓ 但是,不同消费组的消费者可以消费同一条消息吗?
> XGROUP CREATE stream1 mygroupC 0
OK
> XREADGROUP GROUP mygroupC consumer1 STREAMS stream1 >
stream1
1678438374296-0
k1
v1
1678438380663-0
k2
v2
1678438810766-0
k3
v3
> XREADGROUP GROUP mygroupC consumer1 STREAMS stream1 >
null
可见,不同消费组的消费者可以消费同一条消息。
❓ 消费组的目的?
让组内的多个消费者共同分担读取消息,所以,我们通常会让每个消费者读取部分消息,从而实现消息读取负载在多个消费者间是均衡分布的。
举例:
> XGROUP CREATE stream1 mygroupD 0
OK
> XREADGROUP GROUP mygroupD consumer1 COUNT 1 STREAMS stream1 >
stream1
1678438374296-0
k1
v1
> XREADGROUP GROUP mygroupD consumer2 COUNT 1 STREAMS stream1 >
stream1
1678438380663-0
k2
v2
> XREADGROUP GROUP mygroupD consumer3 COUNT 1 STREAMS stream1 >
stream1
1678438810766-0
k3
v3
> XREADGROUP GROUP mygroupD consumer4 COUNT 1 STREAMS stream1 >
null
️❓ 重点问题
基于 Stream 实现的消息队列,如何保证消费者在发生故障或宕机再次重启后,仍然可以读取未处理完的消息?
解答:
Streams 会自动使用内部队列(也称为 PENDING List)留存消费组里每个消费者读取的消息保底措施,直到消费者使用 XACK
命令通知 Streams “消息已经处理完成”。
消费确认增加了消息的可靠性,一般在业务处理完成之后,需要执行 XACK 命令确认消息已经被消费完成。
️ XPENDING
XPENDING key group [[IDLE min-idle-time] start end count [consumer]]
查询每个消费组内所有消费者「已读取、但尚未确认」的消息。
举例:
> XPENDING stream1 mygroupA
3
1678438374296-0 #所有消费者读取的消息最小ID
1678438810766-0 #所有消费者读取的消息最大ID
consumer1 #一个消费者一次性读3条
3
> XPENDING stream1 mygroupC
3
1678438374296-0
1678438810766-0
consumer1
3
> XPENDING stream1 mygroupD
3
1678438374296-0
1678438810766-0
consumer1 #以下3个消费者分别读取1条
1
consumer2
1
consumer3
1
查看某个消费者具体读取了哪些数据。
举例:
> XPENDING stream1 mygroupD - + 10 consumer1
1678438374296-0
consumer1
815248
1
消费者 consumer1 已读取的消息的 ID 是1678438374296-0。一旦消息 1678438374296-0 被 consumer1 处理了 consumer1 就可以使用 XACK
命令通知 Streams,然后这条消息就会被删除。
️ XACK
XACK key group id [id ...]
向消息队列确认消息处理已完成。
举例:
> XPENDING stream1 mygroupC - + 10 consumer1 #没有ack之前3条记录读取过
1678438374296-0
consumer1
1150855
1
1678438380663-0
consumer1
1150855
1
1678438810766-0
consumer1
1150855
1
> XACK stream1 mygroupC 1678438374296-0 #ack成功确认一条,返回1
1
> XPENDING stream1 mygroupC - + 10 consumer1 #ack签收了1条,剩2条
1678438380663-0
consumer1
1176623
1
1678438810766-0
consumer1
1176623
1
️ XINFO
XINFO 用于打印 Stream \ Consumer \ Group 的详细信息。
XINFO STREAM key [FULL [COUNT count]]
XINFO GROUPS key
XINFO CONSUMERS key group
举例:
> XINFO STREAM stream1
length
3
radix-tree-keys
1
radix-tree-nodes
2
groups
4
last-generated-id
1678438810766-0
first-entry
1678438374296-0
k1
v1
last-entry
1678438810766-0
k3
v3
举例:
> XINFO GROUPS stream1
name
mygroupA
consumers
1
pending
3
last-delivered-id
1678438810766-0
name
mygroupB
consumers
0
pending
0
last-delivered-id
1678438810766-0
name
mygroupC
consumers
1
pending
2
last-delivered-id
1678438810766-0
name
mygroupD
consumers
3
pending
2
last-delivered-id
1678438810766-0
举例:
> XINFO CONSUMERS stream1 mygroupA
name
consumer1
pending
3
idle
1770750
Stream 还是不能 100% 等价于 Kafka、RabbitMQ 来使用的,生产案例少,建议大系统慎用。
https://redis.io/docs/data-types/bitfields/
BITFIELD 命令可以将一个 Redis 字符串看作是一个由二进制位组成的数组,并对这个数组中任意偏移进行访问 。 可以使用该命令对一个有符号的 5 位整型数的第 1234 位设置指定值,也可以对一个 31 位无符号整型数的第 4567 位进行取值。类似地,本命令可以对指定的整数进行自增和自减操作,可配置的上溢和下溢处理操作。
BITFIELD 命今可以在一次调用中同时对多个位范围进行操作作:它接受一系列待执行的操作作为参数,并返回一个数组,数组中的每个元素就是对应操作的执行结果。
BITFIELD 命令的作用在于它能够将很多小的整数储存到一个长度较大的位图中,又或者将一个非常庞大的键分割为多个较小的键来进行储存,从而非常高效地使用内存,使得 Redis 能够得到更多不同的应用一特别是在实时分析领域 BITFIELD 能够以指定的方式对计算溢出进行控制的能力,使得它可以被应用于这一领域。
hello 等价于 01101000 01100101 01101100 01101100 01101111
将一个 Redis 字符串看作是一个由二进制位组成的数组。
并能对变长位宽和任意没有字节对齐的指定整型位域进行寻址和修改。
BITFIELD key [GET encoding offset | [OVERFLOW ]
[GET encoding offset | [OVERFLOW ]
...]]
GET encoding offset
:返回指定的位域;SET encoding offset value
:设置指定位域的值并返回它的原值;INCRBY encoding offset increment
:自增或自减(increment为负数)指定位域的值并返回它的新值;OVERFLOW
:设置溢出行为来改变调用 INCRBY 指令的后序操作;当需要一个整型时,有符号整型需在位数前加 i
,无符号在位数前加 u
。例如,u8
是一个 8 位的无符号整型, i16
是一个 16 位的有符号整型。
Ascii 码表:https://ascii.org.cn/
️ GET encoding offset
返回指定的位域
对照表:
hello 等价于 0110100001100101011011000110110001101111
> SET fieldkey hello
OK
> BITFIELD fieldkey GET i8 0
104
> BITFIELD fieldkey GET i8 8
101
> BITFIELD fieldkey GET i8 16
108
> BITFIELD fieldkey GET i8 24
108
> BITFIELD fieldkey GET i8 32
111
BITFIELD 命令可以将一个 Redis 字符串看作是一个由二进制位组成的数组,并对这个数组中任意偏移进行访问 。可以使用该命令对一个有符号的 5 位整型数的第 1234 位设置指定值,也可以对一个 31 位无符号整型数的第 4567 位进行取值。类似地,本命令可以对指定的整数进行自增和自减操作,可配置的上溢和下溢处理操作。
️ SET encoding offset value
设置指定位域的值并返回它的原值
> SET fieldkey hello
OK
> BITFIELD fieldkey GET i8 0
104
> BITFIELD fieldkey GET i8 8
101
> BITFIELD fieldkey GET i8 16
108
> BITFIELD fieldkey GET i8 24
108
> BITFIELD fieldkey GET i8 32
111
> BITFIELD fieldkey SET i8 8 120 #从第9个位开始,将接下来8个位用有符号数120(字母x)替换
101
> GET fieldkey
hxllo
️ INCRBY encoding offset increment
自增或自减(increment为负数)指定位域的值并返回它的新值
> SET fieldkey hello
OK
> BITFIELD fieldkey INCRBY u4 2 1 #从第3个位开始,对接下来的4位无符号数+1
11
> BITFIELD fieldkey INCRBY u4 2 1
12
> BITFIELD fieldkey INCRBY u4 2 1
13
> BITFIELD fieldkey INCRBY u4 2 1
14
> BITFIELD fieldkey INCRBY u4 2 1
15
> BITFIELD fieldkey INCRBY u4 2 1 #默认overflow为wrap,即循环溢出
0
️ 溢出控制 OVERFLOW
️ WRAP :使用回绕(wrap around)方法处理有符号整数和无符号整数的溢出情况。
> SET k1 a
OK
> BITFIELD k1 GET i8 0 #a对应的ascII码值97
97
> BITFIELD k1 SET i8 0 128 #i8表示有符号8位二进制,范围 (-128-127)
97
> BITFIELD k1 GET i8 0 #默认overflow为wrap,即循环溢出
-128
️SAT :使用饱和计算(saturation arithmetic)方法处理溢出,下溢计算的结果为最小的整数值,而上溢计算的结果为最大的整数值。
> BITFIELD k1 GET i8 0
-128
> BITFIELD k1 OVERFLOW SAT SET i8 0 128
-128
> BITFIELD k1 GET i8 0 #上溢 最大的整数值
127
️FAIL :命令将拒绝执行那些会导致上溢或者下溢情况出现的计算,并向用户返回空值表示计算未被执行。
> BITFIELD k1 OVERFLOW FAIL SET i8 0 222
null
官网介绍:https://redis.io/docs/management/persistence/
持久性是指将数据写入持久存储,如固态磁盘(SSD)。Redis 提供了一系列持久性选项。其中包括:
在指定的时间间隔,执行数据集的时间点快照。
把某一时刻的数据和状态以文件的形式写到磁盘上,也就是快照。这样一来即使故障宕机,快照文件也不会丢失,数据的可靠性也就得到了保证。这个快照文件就称为 RDB 文件(dump.rdb),其中,RDB 就是 Redis DataBase 的缩写。
在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的 Snapshot 内存快照,它恢复时再将硬盘快照文件直接读回到内存里。
Redis的数据都在内存中,保存备份时它执行的是全量快照
,也就是说,把内存中的所有数据都记录到磁盘中。
RDB 保存的是 dump.rdb
文件。
默认情况下,Redis 将数据集的快照保存在磁盘上,保存在一个名为 dump.rdb
的二进制文件中。如果数据集中至少有 M 个更改,您可以配置 Redis,使其每隔 N 秒保存一次数据集,也可以手动调用 SAVE
或 BGSAVE
命令。
️ Redis 6.2 之前的版本
自动触发:
在 redis.conf
配置文件中的 SNAPSHOTTING
下配置 save
参数,来触发 Redis 的 RDB 持久化条件,比如 save m n
:表示 m 秒内数据集存在 n 次修改时,自动触发 bgsave
。
️ Redis 6.2 以及 Redis 7.0 之后的版本
️ Redis 7.0 版本,按照 redis.conf
里配置的规则:
save [ ...]
本次案例配置为 5 秒 2 次修改:
save 5 2
️ 修改 dump 文件保存路径:
默认:
# The filename where to dump the DB
dbfilename dump.rdb
# The working directory.
#
# The DB will be written inside this directory, with the filename specified
# above using the 'dbfilename' configuration directive.
#
# The Append Only File will also be created inside this directory.
#
# Note that you must specify a directory here, not a file name.
dir ./
自定义修改的路径且可以进入 Redis 里用 CONFIG GET dir
获取目录:
dir ./dumpfile
> CONFIG GET dir
dir
C:\Dev\Redis\dumpfile
修改 dump 文件名称:
# The filename where to dump the DB
dbfilename dump6379.rdb
️ 触发备份
> SET K1 V1
OK
> SET K2 V2
OK
路径 dumpfile 文件下,生成文件:dump6379.rdb
️ 如何恢复
物理恢复,服务和备份分机隔离
将备份文件 dump.rdb 移动到 Redis 安装目录并启动服务即可。
将 dump6379_bak.rdb 备份文件修改为 dump6379.rdb,启动 Redis 服务:
> KEYS *
K2
K1
不可以把备份文件 dump.rdb 和生产 Redis 服务器放在同一台机器,必须分开各自存储,以防生产机物理损坏后备份文件也挂了。
❌ 备份成功后故意用命令 flushdb
清空 Redis,看看是否可以恢复数据?不能
将 dump6379.rdb 备份成 dump6379_bak.rdb,然后 flushdb 执行后默认自动生成 rdb 文件。
执行
flushall
/flushdb
命令也会产生 dump.rdb 文件,但里面是空的,无数据。
Redis 提供了两个命令来手动⽣成 RDB ⽂件,分别是 SAVE
和 BGSAVE
命令。
️ SAVE
在主程序中执⾏会 阻塞
当前 Redis 服务器,直到持久化工作完成执行 save
命令期间,Redis 不能处理其他命令,线上禁止使用
。
> SAVE
OK
️ BGSAVE
(默认)
Redis 会在后台异步进行快照操作,不阻塞
,快照同时还可以响应客户端请求,该触发方式会 fork 一个子进程由子进程复制持久化过程。
️ 官网介绍:
https://redis.io/commands/bgsave/
执行 BGSAVE,正常情况下,会立即返回 OK。Redis forks ,父级继续为客户端提供服务,子级将 DB 保存在磁盘上,然后退出。
如果已经有后台保存在运行,或者有另一个非后台保存进程在运行,特别是正在进行的AOF重写,则会返回错误。
如果使用 BGSAVE SCHEDULE,则当AOF重写正在进行时,该命令将立即返回 OK,并计划在下一次机会运行后台保存。
客户端可以使用 LASTSAVE 命令来检查操作是否成功。
返回值:
简单字符串回复:如果BGSAVE启动正确,则启动后台保存,或者与SCHEDULE子命令一起使用时安排后台保存。
在Linux程序中,fork()会产生一个和父进程完全相同的子进程,但子进程在此后多会exec系统调用,出于效率考虑,尽量避免膨胀。
Redis 会使用 BGSAVE 对当前内存中的所有数据做快照,这个操作是子进程在后台完成的,这就允许主进程同时可以修改数据。
> BGSAVE
Background saving started
️ LASTSAVE
可以通过 LASTSAVE
命令获取最后一次成功执行快照的时间。
> LASTSAVE
1678960442
Linux 查看:
date -d @1678960442
https://redis.io/docs/management/persistence/#rdb-advantages
总结:
https://redis.io/docs/management/persistence/#rdb-disadvantages
fork()
以便使用子进程在磁盘上持久化。如果数据集很大,fork()
可能会很耗时,并且如果数据集很大并且 CPU 性能不是很好,可能会导致 Redis 停止为客户端服务几毫秒甚至一秒钟。AOF 也需要 fork()
但频率较低,您可以调整要重写日志的频率,而不需要对持久性进行任何权衡。总结:
️ 数据丢失案例
#正常录入数据
> SET K1 V2
OK
> SET K2 V2
OK
> SET K3 V3
OK
#服务出故障(kill -9故意模拟意外down机)
#Redis重启恢复,查看数据是否丢失
> KEYS *
K2
K1
#数据K3丢失了
如何检查修复 dump.rdb 文件?
当服务出故障造成 dump.rdb 文件数据不完整,可以使用 redis-check-rdb 工具进行修复。
>redis-check-rdb dumpfile/dump6379.rdb
[offset 0] Checking RDB file dumpfile/dump6379.rdb
[offset 27] AUX FIELD redis-ver = '7.0.9'
[offset 41] AUX FIELD redis-bits = '64'
[offset 53] AUX FIELD ctime = '1679033301'
[offset 65] AUX FIELD used-mem = '0'
[offset 81] AUX FIELD aof-preamble = '0'
[offset 83] Selecting DB ID 0
[offset 109] Checksum OK
[offset 109] \o/ RDB looks OK! \o/
[info] 2 keys read
[info] 0 expires
[info] 0 already expired
SAVE
或 BGSAVE
命令;FLUSHALL
/ FLUSHDB
命令也会产生 dump.rdb 文件,但里面是空的,无意义;SHUTDOWN
且没有设置开启 AOF 持久化;1、动态所有停止 RDB 保存规则的方法,执行命令:
redis-cli config set save ""
2、配置文件快照禁用:
# Snapshotting can be completely disabled with a single empty string argument
# as in following example:
#
save ""
配置文件 SNAPSHOTTING 模块:
️ 保存数据到磁盘。
# Save the DB to disk.
#
# save [ ...]
#
# Redis will save the DB if the given number of seconds elapsed and it
# surpassed the given number of write operations against the DB.
#
# Snapshotting can be completely disabled with a single empty string argument
# as in following example:
#
# save ""
#
# Unless specified otherwise, by default Redis will save the DB:
# * After 3600 seconds (an hour) if at least 1 change was performed
# * After 300 seconds (5 minutes) if at least 100 changes were performed
# * After 60 seconds if at least 10000 changes were performed
#
# You can set these explicitly by uncommenting the following line.
#
save 3600 1 300 100 60 10000
️ 默认 yes,如果配置成 no,表示不在乎数据不一致或者有其他的手段发现和控制这种不一致,那么在快照写入失败时,也能确保 Redis 继续接受新的写请求。
# By default Redis will stop accepting writes if RDB snapshots are enabled
# (at least one save point) and the latest background save failed.
# This will make the user aware (in a hard way) that data is not persisting
# on disk properly, otherwise chances are that no one will notice and some
# disaster will happen.
#
# If the background saving process will start working again Redis will
# automatically allow writes again.
#
# However if you have setup your proper monitoring of the Redis server
# and persistence, you may want to disable this feature so that Redis will
# continue to work as usual even if there are problems with disk,
# permissions, and so forth.
stop-writes-on-bgsave-error yes
️ 默认 yes,对于存储到磁盘中的快照,可以设置是否进行压缩存储。如果是的话,Redis 会采用 LZF 算法进行压缩。如果你不想消耗 CPU 来进行压缩的话,可以设置为关闭此功能。
# Compress string objects using LZF when dump .rdb databases?
# By default compression is enabled as it's almost always a win.
# If you want to save some CPU in the saving child set it to 'no' but
# the dataset will likely be bigger if you have compressible values or keys.
rdbcompression yes
️ 默认 yes,在存储快照后,还可以让 Redis 使用 CRC64 算法来进行数据校验,但是这样做会增加大约 10% 的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。
# Since version 5 of RDB a CRC64 checksum is placed at the end of the file.
# This makes the format more resistant to corruption but there is a performance
# hit to pay (around 10%) when saving and loading RDB files, so you can disable it
# for maximum performances.
#
# RDB files created with checksum disabled have a checksum of zero that will
# tell the loading code to skip the check.
rdbchecksum yes
️ rdb 文件名
# The filename where to dump the DB
dbfilename dump.rdb
️ 在没有持久性的情况下删除复制中使用的 RDB 文件启用。默认情况下 no,此选项是禁用的。
# Remove RDB files used by replication in instances without persistence
# enabled. By default this option is disabled, however there are environments
# where for regulations or other security concerns, RDB files persisted on
# disk by masters in order to feed replicas, or stored on disk by replicas
# in order to load them for the initial synchronization, should be deleted
# ASAP. Note that this option ONLY WORKS in instances that have both AOF
# and RDB persistence disabled, otherwise is completely ignored.
#
# An alternative (and sometimes better) way to obtain the same effect is
# to use diskless replication on both master and replicas instances. However
# in the case of replicas, diskless is not always an option.
rdb-del-sync-files no
️ 工作目录
# The working directory.
#
# The DB will be written inside this directory, with the filename specified
# above using the 'dbfilename' configuration directive.
#
# The Append Only File will also be created inside this directory.
#
# Note that you must specify a directory here, not a file name.
dir ./
https://redis.io/docs/management/persistence/
以日志的形式来记录每个写操作
,将 Redis 执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,Redis 启动之初会读取该文件重新构建数据,换言之,Redis 重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
默认情况下,Redis 是没有开启 AOF (append only file) 的。开启 AOF 功能需要设置配置:appendonly yes
。
AOF 保存的是 appendonly.aof
文件。
https://redis.io/docs/management/persistence/#append-only-file
RDB 模式不是很耐用。如果运行 Redis 的计算机停止运行,电源线故障,或者不小心 kill -9 实例,那么写入 Redis 的最新数据将丢失。虽然这对某些应用程序来说可以接受,但对于需要完全持久性的应用,这些情况,单独使用 Redis 快照是不可行的。
AOF 是 Redis 的一种替代、完全持久的策略。它在1.1版中提供。
开启 AOF,每当 Redis 收到更改数据集的命令(例如 SET)时,它都会将其附加到 AOF 中。当重新启动 Redis 时,它将重新播放 AOF 以重建状态。
自 Redis 7.0.0,Redis 使用了多部分 AOF 机制。也就是说,原始的单个 AOF 文件被拆分为基本文件(最多一个)和增量文件(可能有多个)。基本文件表示重写 AOF 时存在的数据的初始快照(RDB 或 AOF 格式)。增量文件包含自上一个基本 AOF 文件创建以来的增量更改。所有这些文件都放在一个单独的目录中,并由清单文件跟踪。
同步文件的三种写回策略
,将命令写入磁盘上的 AOF 文件。AOF 重写
),从而起到 AOF 文件压缩的目的。redis.conf
:
# The fsync() call tells the Operating System to actually write data on disk
# instead of waiting for more data in the output buffer. Some OS will really flush
# data on disk, some other OS will just try to do it ASAP.
#
# Redis supports three different modes:
#
# no: don't fsync, just let the OS flush the data when it wants. Faster.
# always: fsync after every write to the append only log. Slow, Safest.
# everysec: fsync only one time every second. Compromise.
#
# The default is "everysec", as that's usually the right compromise between
# speed and data safety. It's up to you to understand if you can relax this to
# "no" that will let the operating system flush the output buffer when
# it wants, for better performances (but if you can live with the idea of
# some data loss consider the default persistence mode that's snapshotting),
# or on the contrary, use "always" that's very slow but a bit safer than
# everysec.
#
# More details please check the following article:
# http://antirez.com/post/redis-persistence-demystified.html
#
# If unsure, use "everysec".
# appendfsync always
appendfsync everysec
# appendfsync no
always
:同步写回,每个写命令执行完立刻同步地将日志写回磁盘;everysec
:每秒写回,每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,每隔 1 秒把缓冲区中的内容写入磁盘;no
:操作系统控制的写回,每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘;写回机制 | 优点 | 缺点 | |
---|---|---|---|
always | 同步写回 | 可靠性高,基本不丢失数据 | 每个写命令都要落盘,性能影响较大 |
everysec | 每秒写回 | 性能中 | 宕机时丢失 1 秒内的数据 |
no | 操作系统控制的写回 | 性能高 | 宕机时丢失数据较多 |
️ 开启 AOF
############################## APPEND ONLY MODE ###############################
# By default Redis asynchronously dumps the dataset on disk. This mode is
# good enough in many applications, but an issue with the Redis process or
# a power outage may result into a few minutes of writes lost (depending on
# the configured save points).
#
# The Append Only File is an alternative persistence mode that provides
# much better durability. For instance using the default data fsync policy
# (see later in the config file) Redis can lose just one second of writes in a
# dramatic event like a server power outage, or a single write if something
# wrong with the Redis process itself happens, but the operating system is
# still running correctly.
#
# AOF and RDB persistence can be enabled at the same time without problems.
# If the AOF is enabled on startup Redis will load the AOF, that is the file
# with the better durability guarantees.
#
# Please check http://redis.io/topics/persistence for more information.
appendonly yes
️ 使用默认写回策略,每秒钟
# The fsync() call tells the Operating System to actually write data on disk
# instead of waiting for more data in the output buffer. Some OS will really flush
# data on disk, some other OS will just try to do it ASAP.
#
# Redis supports three different modes:
#
# no: don't fsync, just let the OS flush the data when it wants. Faster.
# always: fsync after every write to the append only log. Slow, Safest.
# everysec: fsync only one time every second. Compromise.
#
# The default is "everysec", as that's usually the right compromise between
# speed and data safety. It's up to you to understand if you can relax this to
# "no" that will let the operating system flush the output buffer when
# it wants, for better performances (but if you can live with the idea of
# some data loss consider the default persistence mode that's snapshotting),
# or on the contrary, use "always" that's very slow but a bit safer than
# everysec.
#
# More details please check the following article:
# http://antirez.com/post/redis-persistence-demystified.html
#
# If unsure, use "everysec".
# appendfsync always
appendfsync everysec
# appendfsync no
️ AOF 文件保存路径
️ Redis 6 版本
AOF 保存文件的位置和 RDB 保存文件的位置一样,都是通过 redis.conf 配置文件的 dir
配置。
️ Redis 7 版本
# For convenience, Redis stores all persistent append-only files in a dedicated
# directory. The name of the directory is determined by the appenddirname
# configuration parameter.
appenddirname "appendonlydir"
最终路径为:dir + appenddirname
️ AOF 文件保存名称
️ Redis 6 版本
有且仅有一个
# The name of the append only file (default: "appendonly.aof")
appendfilename "appendonly.aof"
️ Redis 7 版本
Redis7.0 Multi Part AOF 的设计,官网说明:https://redis.io/docs/management/persistence/#append-only-file
# The base name of the append only file.
#
# Redis 7 and newer use a set of append-only files to persist the dataset
# and changes applied to it. There are two basic types of files in use:
#
# - Base files, which are a snapshot representing the complete state of the
# dataset at the time the file was created. Base files can be either in
# the form of RDB (binary serialized) or AOF (textual commands).
# - Incremental files, which contain additional commands that were applied
# to the dataset following the previous file.
#
# In addition, manifest files are used to track the files and the order in
# which they were created and should be applied.
#
# Append-only file names are created by Redis following a specific pattern.
# The file name's prefix is based on the 'appendfilename' configuration
# parameter, followed by additional information about the sequence and type.
#
# For example, if appendfilename is set to appendonly.aof, the following file
# names could be derived:
#
# - appendonly.aof.1.base.rdb as a base file.
# - appendonly.aof.1.incr.aof, appendonly.aof.2.incr.aof as incremental files.
# - appendonly.aof.manifest as a manifest file.
appendfilename "appendonly.aof"
base :表示基础 AOF,它一般由子进程通过重写产生,该文件最多只有一个。基本文件
incr :表示增量 AOF,它一般会在 AOFRW 开始执行时被创建,该文件可能存在多个。增量文件
HISTORY :表示历史 AOF,它由 BAS E和 INCR AOF 变化而来,每次 AOFRW 成功完成时,本次 AOFRW 之前对应的 BASE 和 INCR AOF 都将变为 HISTORY,HISTORY 类型的 AOF 会被 Redis 自动删除。
为了管理这些 AOF 文件,引入了一个 manifest (清单) 文件来跟踪、管理这些 AOF。同时,为了便于 AOF 备份和拷贝,将所有的 AOF 文件和 manifest 文件放入一个单独的文件目录中,目录名由 appenddirname 配置。清单文件
编辑中。。。