Redis消息订阅

SUBSCRIBE, UNSUBSCRIBEPUBLISH 实现了 发布/订阅消息范例,发送者 (publishers) 不直接向特定的接收者发送消息 (subscribers),而是发布消息到频道(channel),不关心有没有订阅者。订阅者订阅关注一个或多个频道(channel),并且只接收他关注的消息,不管发布者是不是存在。发布者和订阅者的解耦提供了更大的伸缩性、更动态的网络拓扑。
例如,为了订阅通道 foo 和 bar ,客户端可以使用通道名字作为参数来调用 SUBSCRIBE 命令:
SUBSCRIBE foo bar
当有客户端发送消息到这些频道时,Redis将会推送传入的消息给所有订阅这些频道的客户端。
正在订阅频道的客户端不应该发送除了订阅或取消订阅以外的命令。订阅和退订操作的执行结果以消息的形式返回,客户端可以读取收到消息的第一个元素来区分收到的是消息类型,还是订阅和退订操作执行结果。订阅客户端能使用的命令是 SUBSCRIBE, PSUBSCRIBE, UNSUBSCRIBE, PUNSUBSCRIBE, PINGQUIT.

推送消息的格式

消息是带有三个元素的Array repl
第一个元素是消息的类型:

  • subscribe: 表示成功订阅到以第二个返回元素命名的频道。第三个元素表示客户端当前订阅频道总数。
  • unsubscribe: 表示成功退订以第二个返回元素命名的频道。第三个元素表示客户端当前订阅频道总数。当第三个参数为零的时候,表示客户端已退出Pub/Sub状态,不再订阅任何频道,可以发送订阅和退订以外的Redis 命令。
  • message: 表示收到其它某一个客户端用 PUBLISH 发布的消息,第二个元素是来源频道的名字,第三个参数是消息实际内容。

    PUBLISH channel message

    Redis PUBLISH 命令用于将信息 message 发送到指定的频道 channel 。
    返回值
    整数: 接收到信息 message 的订阅者数量。
    # 对没有订阅者的频道发送信息
    127.0.0.1:6379> publish ooxx hello
    (integer) 0
    # 向有一个订阅者的频道发送信息 (新启动一台消费端,然后先看订阅频道的数据)
    127.0.0.1:6379> subscribe ooxx
    Reading messages… (press Ctrl-C to quit)
    1) “subscribe”
    2) “ooxx”
    3) (integer) 1
    发现没有接收到ooxx 的信息(原因是消费端需要监听以后,才能接收到订阅消息)
    image.pngimage.png
    image.png

    事务

    介绍

    MULTIEXECDISCARDWATCH 是 Redis 事务相关的命令。事务可以一次执行多个命令, 并且带有以下两个重要的保证:

  • 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。

  • 事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。

EXEC 命令负责触发并执行事务中的所有命令:

  • 如果客户端在使用 MULTI 开启了一个事务之后,却因为断线而没有成功执行 EXEC ,那么事务中的所有命令都不会被执行。
  • 另一方面,如果客户端成功在开启事务之后执行 EXEC ,那么事务中的所有命令都会被执行。

当使用 AOF 方式做持久化的时候, Redis 会使用单个 write(2) 命令将事务写入到磁盘中。
然而,如果 Redis 服务器因为某些原因被管理员杀死,或者遇上某种硬件故障,那么可能只有部分事务命令会被成功写入到磁盘中。
如果 Redis 在重新启动时发现 AOF 文件出了这样的问题,那么它会退出,并汇报一个错误。
使用redis-check-aof程序可以修复这一问题:它会移除 AOF 文件中不完整事务的信息,确保服务器可以顺利启动。
从 2.2 版本开始,Redis 还可以通过乐观锁(optimistic lock)实现 CAS (check-and-set)操作,具体信息请参考文档的后半部分。

为什么 Redis 不支持回滚(roll back)

如果你有使用关系式数据库的经验, 那么 “Redis 在事务失败时不进行回滚,而是继续执行余下的命令”这种做法可能会让你觉得有点奇怪。
以下是这种做法的优点:

  • Redis 命令只会因为错误的语法而失败(并且这些问题不能在入队时发现),或是命令用在了错误类型的键上面:这也就是说,从实用性的角度来说,失败的命令是由编程错误造成的,而这些错误应该在开发的过程中被发现,而不应该出现在生产环境中。
  • 因为不需要对回滚进行支持,所以 Redis 的内部可以保持简单且快速。

有种观点认为 Redis 处理事务的做法会产生 bug , 然而需要注意的是, 在通常情况下, 回滚并不能解决编程错误带来的问题。 举个例子, 如果你本来想通过 INCR 命令将键的值加上 1 , 却不小心加上了 2 , 又或者对错误类型的键执行了 INCR , 回滚是没有办法处理这些情况的。

放弃事务

当执行 DISCARD 命令时, 事务会被放弃, 事务队列会被清空, 并且客户端会从事务状态中退出:

放弃事务

当执行 DISCARD 命令时, 事务会被放弃, 事务队列会被清空, 并且客户端会从事务状态中退出:
文档地址:http://www.redis.cn/topics/transactions.html

用法

MULTI 命令用于开启一个事务,它总是返回 OK 。 MULTI 执行之后, 客户端可以继续向服务器发送任意多条命令, 这些命令不会立即被执行, 而是被放到一个队列中, 当 EXEC命令被调用时, 所有队列中的命令才会被执行。
另一方面, 通过调用 DISCARD , 客户端可以清空事务队列, 并放弃执行事务。
以下是一个事务例子, 它原子地增加了 foo 和 bar 两个键的值:
> MULTI OK
> INCR foo QUEUED
> INCR bar QUEUED
> EXEC 1) (integer) 1 2) (integer) 1
1)(integer) 1
2)(integer) 1
EXEC 命令的回复是一个数组, 数组中的每个元素都是执行事务中的命令所产生的回复。 其中, 回复元素的先后顺序和命令发送的先后顺序一致。
当客户端处于事务状态时, 所有传入的命令都会返回一个内容为 QUEUED 的状态回复(status reply), 这些被入队的命令将在 EXEC 命令被调用时执行。
—————————————————————————分割线—————————————————————————
基础做法,一个客户端进行事务的操作
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set k1 aaa
QUEUED
127.0.0.1:6379(TX)> set k2 bbb
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) OK
两个客户端进行事务的操作
a客户端先启动事务,b客户端后启动事务
image.png
image.png
b客户端先提交事务后,对于k1的取值,a客户端在系统中找不到,返回一个空值
—————————————————————————分割线—————————————————————————设置k1值为 aaa
a客户端使用 WATCH 来监听k1的值
开启事务后查看k1的值,并查询所有的键keys *,这里不结束事务
b客户端开启事务,重新给k1赋值,并结束事务。
然后结束a客户端的事务,会看到a客服端开启事务后的命令没有被执行,因为WATCH 监听k1的值产生了变化。

RedisBloom布隆过滤器

使用docker安装
[root@MyLinux ~]# docker pull redislabs/rebloom:latest
安装后把原来的redis容器给停止掉,然后再开启RedisBloom(或者新开一个端口号进行宿主机的映射关系)
[root@MyLinux ~]# docker run -tid -p 6379:6379 —name redis-bloom —restart=always redislabs/rebloom:latest
启动后就可以进入容器中
docker exec -it 容器Id或容器名称 /bin/bash
例如:docker exec -it redis-bloom /bin/bash
进去后 执行redis-cli 命令进去redis
[root@MyLinux ~]# docker exec -it redis-rebloom /bin/bash
root@38bb8ceb3a96:/data#
127.0.0.1:6379>

redis作为数据库/缓存的区别

缓存 数据库
安全性
持久化 不需要 需要
数据规模 热点数据 全量数据
查询方式 k/v ks/v
回收策略 allkeys-lru/volatile-lru noeviction
  • key 的有效期
    • 1.到期失效
    • 2.到期失效后,不被再次加载的冷数据
  • redis内存尽量控制在1G~10G

缓存数据清除逻辑:
key的有效期:业务逻辑的推动【数据保留一天的有效期】 给key设置时长,读操作不影响时间过期;写操作会剔除时间有效期,因此开发过程中需要通过业务逻辑重新设定;
expire k1 50 //给k1设置50秒过期时间
业务运转:内存有限,随着访问的变化,要淘汰冷数据。
Redis如何清除过期keys
每10s做一次随机keys检测,过期key的百分比25%,高于25则会重复进行随机检测。
回收策略
当maxmemory限制达到的时候Redis会使用的行为由 Redis的maxmemory-policy配置指令来进行配置。
以下的策略是可用的:
noeviction:返回错误当内存限制达到并且客户端尝试执行会让更多内存被使用的命令(大部分的写入指令,但DEL和几个例外)

  • allkeys-lru: 尝试回收最少使用的键(LRU),使得新添加的数据有空间存放。
  • volatile-lru: 尝试回收最少使用的键(LRU),但仅限于在过期集合的键,使得新添加的数据有空间存放。
  • allkeys-random: 回收随机的键使得新添加的数据有空间存放。
  • volatile-random: 回收随机的键使得新添加的数据有空间存放,但仅限于在过期集合的键。
  • volatile-ttl: 回收在过期集合的键,并且优先回收存活时间(TTL)较短的键,使得新添加的数据有空间存放。

缓存击穿
指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力
解决方案:
1.设置热点数据永远不过期
2.加互斥锁
穿透
缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大
解决方案:
布隆过滤
查询结果为空,也保存到缓存中
多层级缓存
雪崩
是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库
解决方案:
缓存数据的过期时间,分散设置,减少同一时刻过期(保存时间设置指定范围内随机)
缓存永久保存