Redis 发布与订阅
1.Redis 发布与订阅
1.1 简介
Redis 发布订阅(sub/pub)是一消息通信模式:发送者(pub)发布消息,订阅者(sub)接收消息。
Redis 客户端可以订阅任意数量的频道。
下图展示了频道 channel1 以及订阅这个频道的三个客户端 – client2、client5、client1 之间的关系:
当有新消息通过 pushlish 命令发送给频道 channel1 时,这个消息就会被发送给订阅它的三个客户端:
1.2 命令
订阅频道
-
SUBSCRIBE channel [channel ...]
订阅一个或者多个频道的信息 -
PSUBSCRIBE pattern [pattern ...]
订阅一个或者多个给定模式的频道
发布频道
PUBLISH channel message
将消息发送到指定的频道
退订频道
-
UBSUBSCRIBE channel [channel ...]
退订指定的频道 -
PUNSUBSCRIBE pattern [pattern ...]
推定所有给定模式的频道
39.108.141.193:6379> subscribe cctv5
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "cctv5"
3) (integer) 1
1) "message"
2) "cctv5"
3) "123"
39.108.141.193:6379> publish cctv5 123
(integer) 1
39.108.141.193:6379>
订阅频道的客户端会持续的等待接受。
1.3 应用场景
构建实时消息系统,比如普通的即时聊天,群聊等功能。
- 在一个博客系统中,有 100 个粉丝订阅你,当你发布新文章,就可以推送消息给粉丝们。
- 微信公众号模式
2. Redis 多数据库
Redis 下,数据库是由一个整数索引标识,而不是由一个数据库名称。默认情况下,一个客户端连接到数据库 0
redis 配置文件中下面的参数来控制数据库的总数:database 16
表示存在 0 - 15 编号的数据库
-
SELECT dbnum
数据库切换 -
MOVE key dbnum
将数据移动 -
FLUSHDB
清除当前数据库的所有 key -
FLUSHALL
清空整个 redis 数据库中所有的 key
3. Redis 事务( 称作执行块更贴切)
3.1 简介
Redis 事务可以一次执行多个命令,允许在一次单独的步骤中执行一组命令,并且带有以下重要的特征:
- Redis 会将一个事务中的所有命令序列化,然后按照顺序执行
- 执行中不会被其他的命令插入,不允许出现加塞行为
一个事务从开始执行到执行经历的三个阶段:
- 开始事务
- 命令入队
- 执行事务
3.2 命令
-
MULTI
标记一个事务快的开始 -
EXEC
执行事务块内的命令 -
DISCARD
取消事务,放弃执行事务块内的所有命令 -
WATCH key [key ...]
监视一个或者多个 key 如果在执行事务之前某个 key 被其他命令改动,那么事务将被打断 -
UNWATCH
取消 watch 命令对所有 key 的监视
3.3 示例
示例1:MULTI 和 EXEC
转账功能, A 向 B 转账 50 元
39.108.141.193:6379> set acount:a 100
OK
39.108.141.193:6379> set account:b 120
OK
39.108.141.193:6379> multi
OK
39.108.141.193:6379> get account:a
QUEUED
39.108.141.193:6379> incrby account:b 50
QUEUED
39.108.141.193:6379> decrby account:a 50
QUEUED
39.108.141.193:6379> get account:a
QUEUED
39.108.141.193:6379> get account:b
QUEUED
39.108.141.193:6379> exec
1) "100"
2) (integer) 170
3) (integer) 50
4) "50"
5) "170"
39.108.141.193:6379>
示例2:discard 放弃队列执行
- 输入 multi 命令开始,输入的命令搜会一次进入命令队列中,但是不会执行
- 直到输入 exec 后,redis 回将之前的命令队列中的命令一次执行
- 命令队列的命令执行过程中,可以通过 discard 来放弃队列运行
示例 3:事务的错误处理
如果执行的某个命令报出错误,则只有报错的命令不会被执行,而其他的命令都会执行,不回滚。
比如:
示例 4:事务的错误处理
队列中的某个命令出现了 报告错误,执行时整个的所有队列都会被取消。
示例 3 的错误是程序员的逻辑错误(给 String 类型值执行加操作),这个错误是语法错误(比如,执行并不存在的方法),当语法错误的时候该队列都会被取消。
示例 5:事务的 WATCH
WATCH
用来监控一个或者多个 key ,如果事务在执行之前某个 key 被其他命令所改动,那么事务将被打断。
需求:某个账户在一个事务内进行操作,在提交事务之前,另一个进程对该账户进行操作。
watch 会在事务执行之后自动结束
示例 6:UNWATCH
Redis UNWATCH 命令用于取消 WATCH 命令对所有 key 的监控
如果在执行 WATCH 命令之后,EXEC 命令或者 DISCARD 命令先被执行的话,那就不需要在执行 UNWATCH 了,也就是 EXEC 和 DISCARD 命令也是 WATCH 命令的终结点。
####应用场景
- 一组命令必须同时执行,或者都不执行
- 我们想要保证一组命令在执行过程之中不被其他命令插入
比如:商品秒杀活动
4. Redis 数据淘汰策略 redis.conf
Redis 官方给的警告,当内存不足时,Redis 会根据配置的缓存策略淘汰部分 keys 以保证写入成功,当无淘汰策略时或者没有找到合适的淘汰的 key 时,Redis 会直接返回 out of memory 错误。
最大缓存配置
在 redis 中,允许用户设置最大使用内存大小
maxmemory 512G
redis 提供 6 中数据淘汰策略
- volatile-lru 从已设置过期时间的数据集中挑选最近最少使用的数据淘汰
- volatile-lfu 从已设置过期时间的数据集中,删除一段时间内使用次数最少
- volatile-ttl 从已设置国企时间的数据集中挑选最近将要过期的数据淘汰
- volatile-random 从已设置过期时间的数据集中随机选择数据淘汰
- allkeys-lru 从数据集中挑选最近最少使用的数据淘汰
- allkeys-lfu 从数据集中,删除一段时间内使用次数最少的
- allkeys-randon 从数据集中随机选择数据淘汰
- no-enviction 禁止驱逐数据,不采取任何淘汰策略,为默认配置。针对写操作,返回错误信息
建议:了解了 Redis 的淘汰策略之后,在平时使用时应该尽量主动设置/更新 key 的 expire 时间,主动提出不活跃的旧数据,有助于提升查询性能。
5. Redis 持久化
5.1 简介
数据存放:
内存中:高效,断电内存数据会丢失
硬盘:读写速度慢于内存,断电数据不会丢失
5.2 RDB
RDB:时 Redis 默认的持久化机制,RDB 相当于照快照,保存的是一种状态
在安装文件夹下存在 dump.rdb 文件
这种方式就是将内存中数据以快照的方式写入到 二进制 文件中,默认的文件名为 dump.rdb
优点:
快照保存数据极快、还原数据极快
适用于灾备
缺点:
小内存机器不适合使用。因为只要符合照快照要求,RDB 机制就会消耗内存进行照快照,内存小根本无法运行照快照。
快照条件:
-
服务器正常关闭:./bin/redis-cli shutdown
-
key 满足一定的条件:
在配置文件 redis.conf 中有如下配置
save 900 1 // 每隔 900 秒(15分钟)至少 1 个 key 发生变化,产生快照 save 300 10 // 每隔 300 秒(5分钟)至少 10 个 key 发生变化,产生快照 save 60 10000 // 每隔 60 秒(1分钟)至少 10000 个 key 发生变化,产生快照
5.3 AOF
由于快照方式是在一定间隔时间做一次,所以如果 redis 意外宕机的话,就会丢失最后一次照快照之后的所有数据,如果应用要求不能丢失任何数据,可以采用 AOF 持久化方式。
AOF (Append-only file) 比快照方式有更好的持久化,是由于在使用 aof 持久化方式时,redis 会将每一个收到的写命令都通过 write 函数追加到文件中(默认文件为:appendonly.aof)。当 redis 重启时会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。
#####三种方式,默认:每秒 fsync 一次
appendonly yes //启动 aof 持久化方式
# appendfsync always // 收到写命令就立即写入磁盘,最慢,但是保证完全的持久化
appendfsync everysec // 每秒中写入磁盘一次,在性能和持久化方面做了很好的折中
# appendfsync // 完全依赖于 OS 性能最好,但是持久化没保证
产生的问题
持久化文件会变得越来越大
例如:我们调用 incr test
命令 100 次,文件中必须保存全部的 100 条命令,其实有 99 条都是多余的
6. Redis 缓存与数据库一致性
7.1 解决方案
7.1.1 实时同步
对强一致性要求较高,应采用实时同步方案,即查询缓存查询不到再到 DB 中查询,保存到缓存中;
更新缓存的时候,应该先更新数据库,再将缓存的信息设置为无效。
可以使用 Spring 提供的注解完成实时同步:
@Cacheable:查询时使用,注意 Long 类型需要转成 String 类型
@CachePut :更新时使用,使用此注解,一定会从 DB 上查询数据
@CacheEvict : 删除时使用
@Caching: 组合使用
7.1.2 异步队列
对于并发程度较高的,可采用异步队列的方式同步,可采用 kafka 等消息中间件处理消息生产和消费。
7.1.3 使用阿里的同步工具 canal
canal 实现方式时模拟 mysql slave 和 master 的同步机制,监控 DB bitlog 的日志更新来触发缓存的更新,此种方法可以减少工作量,但是使用时有些局限性。