文章摘要(AI生成)
REDIS使用场景消息模式队列模式我们可以使用list类型的lpush和rpop实现消息队列。但使用rpop命令会不知队列中是否有消息,需要轮询,消息会有延迟另外损失系统性能。我们可以使用brpop命令,如果从队列中取不出来数据,会一直阻塞直到超过指定时间后才返回null缺点:做消费者确认ACK麻烦
REDIS使用场景
消息模式
队列模式
我们可以使用list类型的lpush
和rpop
实现消息队列。但使用rpop
命令会不知队列中是否有消息,需要轮询,消息会有延迟另外损失系统性能。我们可以使用brpop
命令,如果从队列中取不出来数据,会一直阻塞直到超过指定时间后才返回null
缺点:
做消费者确认ACK麻烦,不能保证消费者消费消息后是否成功处理的问题(宕机或处理异常等),通常需要维护一个Pending列表,保证消息处理确认;不能做广播模式,如pub/sub,消息发布/订阅模型;不能重复消费,一旦消费就会被删除;不支持分组消费
发布订阅模式
相关命令
SUBSCRIBE,用于订阅信道
PUBLISH,向信道发送消息
UNSUBSCRIBE,取消订阅
此模式允许生产者只生产一次消息,由中间件负责将消息复制到多个消息队列,每个消息队列由对应的 消费组消费。
优点
典型的广播模式,一个消息可以发布到多个消费者 多信道订阅,消费者可以同时订阅多个信道,从而接收多类消息 消息即时发送,消息不用等待消费者读取,消费者会自动接收到信道发布的消息
缺点
- 消息一旦发布,不能接收。换句话就是发布时若客户端不在线,则消息丢失,不能寻回
- 不能保证每个消费者接收的时间是一致的
- 若消费者客户端出现消息积压,到一定程度,会被强制断开,导致消息意外丢失。通常发生在消息的生产远大于消费速度时
可见,Pub/Sub 模式不适合做消息存储,消息积压类的业务,而是擅长处理广播,即时通讯,即时反馈的业务。
使用SortedSet
发布订阅
我们也可以通过SortedSet
实现发布订阅模式。有序集合的方案是在自己确定消息顺序时比较常用,使用集合成员的Score来作为消息ID,保证顺序,还可以保证消息ID的单调递增。通常可以使用时间戳+序号的方案。确保了消息ID的单调递增,利用 SortedSet
的依据Score排序的特征,就可以制作一个有序的消息队列了
Redis Stream
Redis 5.0 全新的数据类型:streams,官方把它定义为:以更抽象的方式建模日志的数据结构。Redis 的streams主要是一个append only(AOF)的数据结构,至少在概念上它是一种在内存中表示的抽象数据类型,只不过它们实现了更强大的操作,以克服日志文件本身的限制。
如果你了解MQ,那么可以把streams当做基于内存的MQ。如果你还了解kafka,那么甚至可以把 streams当做基于内存的kafka
。listpack
存储信息,Rax组织listpack
消息链表
listpack
是对ziplist
的改进,它比ziplist
少了一个定位最后一个元素的属性 另外,这个功能有点类似于redis
以前的Pub/Sub,但是也有基本的不同:
-
streams支持多个客户端(消费者)等待数据(Linux环境开多个窗口执行XREAD即可模拟),并且每个客户端得到的是完全相同的数据。
-
Pub/Sub是发送忘记的方式,并且不存储任何数据;而streams模式下,所有消息被无限期追加在 streams中,除非用于显式执行删除(XDEL)。XDEL 只做一个标记位,其实信息和长度还在
-
streams的消费组也是Pub/Sub无法实现的控制方式。
streams数据结构本身非常简单,但是streams依然是Redis到目前为止最复杂的类型,其原因是实现的一些额外的功能:一系列的阻塞操作允许消费者等待生产者加入到streams的新数据。另外还有一个称 为消费组的概念,Consumer Group概念最先由kafka
提出,Redis有一个类似实现,和 kafka
的消费组的目的是一样的:允许一组客户端协调消费相同的信息流
消息队列相关命令:
- XADD - 添加消息到末尾 XTRIM - 对流进行修剪,限制长度
- XDEL - 删除消息 XLEN - 获取流包含的元素数量,即消息长度
- XRANGE - 获取消息列表,会自动过滤已经删除的消息
- XREVRANGE - 反向获取消息列表,ID 从大到小
- XREAD - 以阻塞或非阻塞方式获取消息列表
消费者组相关命令:
- XGROUP CREATE - 创建消费者组
- XREADGROUP GROUP - 读取消费者组中的消息
- XACK - 将消息标记为"已处理"
- XGROUP SETID - 为消费者组设置新的最后递送消息ID
- XGROUP DELCONSUMER - 删除消费者
- XGROUP DESTROY - 删除消费者组
- XPENDING - 显示待处理消息的相关信息
- XCLAIM - 转移消息的归属权
- XINFO - 查看流和消费者组的相关信息;
- XINFO GROUPS - 打印消费者组的信息;
- XINFO STREAM - 打印流信息
redis
事务
Redis
事务允许在单个步骤中执行一组命令,它们以命令 MULTI
、EXEC
和为中心。Redis 事务有两个重要的保证:DISCARD
WATCH
- 事务中的所有命令都被序列化并按顺序执行。另一个客户端发送的请求永远不会在Redis 事务执行过程**中得到处理。**这保证了命令作为单个独立操作执行。
- 该
EXEC
命令触发事务中所有命令的执行,因此如果客户端在调用命令之前在事务上下文中失去与服务器的连接,则不会EXEC
执行任何操作,相反,如果EXEC
调用命令,则所有操作执行。使用aof
时,Redis 确保使用syscall
命令将事务写入磁盘。但是,如果 Redis 服务器崩溃或被系统管理员以某种硬方式杀死,则可能只注册了部分操作。Redis 会在重启时检测到这种情况,并会报错退出。使用redis-check-aof
工具可以修复将删除部分事务的aof
文件,以便服务器可以重新启动。
使用命令输入 Redis 事务MULTI
。该命令始终以 回复OK
。此时用户可以发出多个命令。Redis 不会执行这些命令,而是将它们排队。所有的命令都执行一次EXEC
被调用。
相反,调用DISCARD
将刷新事务队列并退出事务。
注意:严格意义上说
redis
事务只是个批处理。有隔离性但是没有原子性。在事务中正确的命令可以执行,错误的命令则会被忽略,不会进行事务回滚。
redis
可编程性
可以使用Lua脚本和Redis函数拓展redis
. Redis 7.0以上支持函数编程,7.0以下只能使用lua脚本进行拓展。
调用EVAL命令执行lua脚本
在redis客户端中,执行以下命令:
EVAL script numkeys key [key ...] arg [arg ...]
命令说明:
script
参数:是一段Lua脚本程序,它会被运行在Redis服务器上下文中,这段脚本不必(也不 应该)定义为一个Lua函数。numkeys
参数:用于指定键名参数的个数。 key [key …]参数: 从EVAL的第三个参数开始算起,使用了numkeys
个键(key),表示在脚本中所用到的那些Redis键(key),这些键名参数可以在Lua中通过全局变量KEYS数组,用1为 基址的形式访问( KEYS[1] , KEYS[2] ,以此类推)。arg [arg ...]
参数:可以在Lua中通过全局变量ARGV数组访问,访问的形式和KEYS变量类似( ARGV[1] 、 ARGV[2] ,诸如此类)。
示例:
EVAL "return redis.call('SET', KEYS[1], ARGV[1])" 1 foo bar
通过redis-cli调试Lua
执行lua脚本
时,可以通过以下命令执行
./redis-cli --eval /tmp/script.lua mykey somekey , arg1 arg2
当然也可以加上ldb命令调用调试器,对数据的更改会在调试完回滚。命令如下:
./redis-cli --ldb --eval /tmp/script.lua mykey somekey , arg1 arg2
输入后,我们将进入调试模式,可以通过quit
退出调试,restart
重启调试,也可以使用help
查看断点和其他操作命令
REDIS使用优化
设置键值的过期时间
我们应该根据实际的业务情况,对键值设置合理的过期时间,这样 Redis 会帮你自动清除过期的键值 对,以节约对内存的占用,以避免键值过多的堆积,频繁的触发内存淘汰策略。
Redis 有四个不同的命令可以用于设置键的生存时间(键可以存在多久)或过期时间(键什么时候会被删除) :
- EXPlRE 命令用于将键key 的生存时间设置为ttl 秒。
- PEXPIRE 命令用于将键key 的生存时间设置为ttl 毫秒。
- EXPIREAT < timestamp> 命令用于将键key 的过期时间设置为timestamp所指定的秒数时间戳。
- PEXPIREAT < timestamp > 命令用于将键key 的过期时间设置为timestamp所指定的毫秒数时间 戳。
限制 Redis 内存大小,设置内存淘汰策略
没有指定最大缓存,如果有新的数据添加,超过最大内存,则会使redis崩溃,所以一定要设置。最佳设置是物理内存的75% ,写操作比较多 60%
在redis config文件中,可以通过maxmemory-policy
配置内存的淘汰策略。避免内存雪崩。可配置的内存淘汰策略如下:
- noeviction:不淘汰任何数据,当内存不足时,新增操作会报错,Redis 默认内存淘汰策略;
- allkeys-lru:淘汰整个键值中最久未使用的键值;
- allkeys-random:随机淘汰任意键值;
- volatile-lru:淘汰所有设置了过期时间的键值中最久未使用的键值;
- volatile-random:随机淘汰设置了过期时间的任意键值;
- volatile-ttl:优先淘汰更早过期的键值。
在 Redis 4.0 版本中又新增了 2 种淘汰策略:
- volatile-lfu:淘汰所有设置了过期时间的键值中,最少使用的键值;
- allkeys-lfu:淘汰整个键值中最少使用的键值。
禁用耗时长的查询命令
一方面可以禁止使用 keys命令,避免一次查询所有的成员,要使用 scan 命令进行分批的,游标式的遍历;也可以通过机制严格控制 Hash、Set、Sorted Set 等结构的数据大小。
使用slowlog
优化耗时命令
我们可以使用 slowlog
功能找出最耗时的 Redis 命令进行相关的优化,以提升 Redis
的运行速度, 慢查询有两个重要的配置项:
slowlog-log-slower-than
:用于设置慢查询的评定时间,也就是说超过此配置项的命令,将会 被当成慢操作记录在慢查询日志中,它执行单位是微秒 (1 秒等于 1000000 微秒);slowlog-max-len
:用来配置慢查询日志的最大记录数。
避免大量数据同时失效
Redis 过期键值删除使用的是贪心策略,它每秒会进行 10 次过期扫描,此配置可在 redis.conf
进行配 置,默认值是 hz 10
,Redis 会随机抽取 20 个值,删除这 20 个键中过期的键,如果过期 key 的比例 超过 25% ,重复执行此流程
如果有大量缓存在同一时间同时过期,那么会导致Redis 循环多次持续扫描删除过期字典,直到过期字典中过期键值被删除的比较稀疏为止,而在整个执行过程会导致 Redis 的读写出现明显的卡顿,卡顿的另一种原因是内存管理器需要频繁回收内存页,因此也会消耗一定的CPU。
为了避免这种卡顿现象的产生,我们需要预防大量的缓存在同一时刻一起过期,就简单的解决方案就是在过期时间的基础上添加一个指定范围的随机数。
使用Pipeline批量操作数据
参考redis 官方流水线使用说明,通过批处理 Redis 命令来优化往返时间
客户端使用连接池
在客户端的使用上我们除了要尽量使用Pipeline
的技术外,还需要注意要尽量使用 Redis 连接池,而不 是频繁创建销毁 Redis 连接,这样就可以减少网络传输次数和减少了非必要调用指令。
使用redis集群增加读写速度
Redis 分布式架构有重要的手段: 主从同步、哨兵模式、Redis Cluster 集群
使用主从同步功能我们可以把写入放到主库上执行,把读功能转移到从服务上,因此就可以在单位时间 内处理更多的请求,从而提升的 Redis 整体的运行速度。
而哨兵模式是对于主从功能的升级,但当主节点奔溃之后,无需人工干预就能自动恢复 Redis 的正常使用。
Redis Cluster 是 Redis 3.0 正式推出的,Redis 集群是通过将数据库分散存储到多个节点上来平衡各个 节点的负载压力。 Redis Cluster 采用虚拟哈希槽分区,所有的键根据哈希函数映射到 0 ~ 16383 整数槽内,计算公式: slot = CRC16(key) & 16383,每一个节点负责维护一部分槽以及槽所映射的键值数据。这样 Redis 就可 以把读写压力从一台服务器,分散给多台服务器了,因此性能会有很大的提升。
评论区