• Redis发布订阅、事务、脚本


    Redis 发布订阅

      Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。

      Redis 客户端可以订阅任意数量的频道。

      下图展示了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系:

    pubsub1

      当有新消息通过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:

    pubsub2

    实例

    以下实例演示了发布订阅是如何工作的。在实例中创建了订阅频道名为 redisChat:

    redis 127.0.0.1:6379> SUBSCRIBE redisChat
    
    Reading messages... (press Ctrl-C to quit)
    1) "subscribe"
    2) "redisChat"
    3) (integer) 1

    现在,重新开启个 redis 客户端,然后在同一个频道 redisChat 发布两次消息,订阅者就能接收到消息。

    redis 127.0.0.1:6379> PUBLISH redisChat "Redis is a great caching technique"
    
    (integer) 1
    
    redis 127.0.0.1:6379> PUBLISH redisChat "Learn redis by runoob.com"
    
    (integer) 1
    
    # 订阅者的客户端会显示如下消息
    1) "message"
    2) "redisChat"
    3) "Redis is a great caching technique"
    1) "message"
    2) "redisChat"
    3) "Learn redis by runoob.com"
    # 1. 订阅一个或多个符合给定模式的频道。
    PSUBSCRIBE pattern [pattern ...] 
    '''
    返回值:接收到的信息。
    redis 127.0.0.1:6379> PSUBSCRIBE mychannel
    Reading messages... (press Ctrl-C to quit)
    1) "psubscribe"
    2) "mychannel"
    3) (integer) 1
    '''
    
    # 2.查看订阅与发布系统状态。
    PUBSUB subcommand [argument [argument ...]] 
    '''
    返回值:由活跃频道组成的列表。
    redis 127.0.0.1:6379> PUBSUB CHANNELS
    (empty list or set)
    '''
    
    # 3.将信息发送到指定的频道。
    PUBLISH channel message 
    '''
    返回值:接收到信息的订阅者数量。
    redis 127.0.0.1:6379> PUBLISH mychannel "hello, i m here"
    (integer) 1
    '''
    
    # 4.退订所有给定模式的频道。
    PUNSUBSCRIBE [pattern [pattern ...]] 
    '''
    返回值:这个命令在不同的客户端中有不同的表现。
    redis 127.0.0.1:6379> PUNSUBSCRIBE mychannel 
    1) "punsubscribe"
    2) "a"
    3) (integer) 1
    '''
    
    # 5.订阅给定的一个或多个频道的信息。
    SUBSCRIBE channel [channel ...] 
    '''
    返回值:接收到的信息
    redis 127.0.0.1:6379> SUBSCRIBE mychannel 
    Reading messages... (press Ctrl-C to quit)
    1) "subscribe"
    2) "mychannel"
    3) (integer) 1
    1) "message"
    2) "mychannel"
    3) "a"
    '''
    
    # 6.指退订给定的频道。
    UNSUBSCRIBE [channel [channel ...]] 
    '''
    返回值:这个命令在不同的客户端中有不同的表现。
    redis 127.0.0.1:6379> UNSUBSCRIBE mychannel 
    1) "unsubscribe"
    2) "a"
    3) (integer) 0
    '''
    redis 发布订阅命令

    Redis 事务

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

    • 批量操作在发送 EXEC 命令前被放入队列缓存。
    • 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
    • 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。

    一个事务从开始到执行会经历以下三个阶段:

    • 开始事务。
    • 命令入队。
    • 执行事务。

    实例

      以下是一个事务的例子, 它先以 MULTI 开始一个事务, 然后将多个命令入队到事务中, 最后由 EXEC 命令触发事务, 一并执行事务中的所有命令:

    redis 127.0.0.1:6379> MULTI
    OK
    
    redis 127.0.0.1:6379> SET book-name "Mastering C++ in 21 days"
    QUEUED
    
    redis 127.0.0.1:6379> GET book-name
    QUEUED
    
    redis 127.0.0.1:6379> SADD tag "C++" "Programming" "Mastering Series"
    QUEUED
    
    redis 127.0.0.1:6379> SMEMBERS tag
    QUEUED
    
    redis 127.0.0.1:6379> EXEC
    1) OK
    2) "Mastering C++ in 21 days"
    3) (integer) 3
    4) 1) "Mastering Series"
       2) "C++"
       3) "Programming"

      单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。

      事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。

    这是官网上的说明 From redis docs on transactions:

    It's important to note that even when a command fails, all the other commands in the queue are processed – Redis will not stop the processing of commands.

    比如:

    redis 127.0.0.1:7000> multi
    OK
    redis 127.0.0.1:7000> set a aaa
    QUEUED
    redis 127.0.0.1:7000> set b bbb
    QUEUED
    redis 127.0.0.1:7000> set c ccc
    QUEUED
    redis 127.0.0.1:7000> exec
    1) OK
    2) OK
    3) OK

    如果在 set b bbb 处失败,set a 已成功不会回滚,set c 还会继续执行。

    # 1.取消事务,放弃执行事务块内的所有命令。
    DISCARD 
    '''
    返回值
    总是返回 OK 。
    redis 127.0.0.1:6379> MULTI
    OK
    redis 127.0.0.1:6379> PING
    QUEUED
    redis 127.0.0.1:6379> SET greeting "hello"
    QUEUED
    redis 127.0.0.1:6379> DISCARD
    OK
    '''
    
    # 2.执行所有事务块内的命令。
    EXEC 
    '''
    返回值:事务块内所有命令的返回值,按命令执行的先后顺序排列。 当操作被打断时,返回空值 nil 。
    # 事务被成功执行
    redis 127.0.0.1:6379> MULTI
    OK
    redis 127.0.0.1:6379> INCR user_id
    QUEUED
    redis 127.0.0.1:6379> INCR user_id
    QUEUED
    redis 127.0.0.1:6379> INCR user_id
    QUEUED
    redis 127.0.0.1:6379> PING
    QUEUED
    redis 127.0.0.1:6379> EXEC
    1) (integer) 1
    2) (integer) 2
    3) (integer) 3
    4) PONG
    
    # 监视 key ,且事务成功执行
    redis 127.0.0.1:6379> WATCH lock lock_times
    OK
    redis 127.0.0.1:6379> MULTI
    OK
    redis 127.0.0.1:6379> SET lock "huangz"
    QUEUED
    redis 127.0.0.1:6379> INCR lock_times
    QUEUED
    redis 127.0.0.1:6379> EXEC
    1) OK
    2) (integer) 1
    
    # 监视 key ,且事务被打断
    redis 127.0.0.1:6379> WATCH lock lock_times
    OK
    redis 127.0.0.1:6379> MULTI
    OK
    redis 127.0.0.1:6379> SET lock "joe"        # 就在这时,另一个客户端修改了 lock_times 的值
    QUEUED
    redis 127.0.0.1:6379> INCR lock_times
    QUEUED
    redis 127.0.0.1:6379> EXEC                  # 因为 lock_times 被修改, joe 的事务执行失败
    (nil)
    '''
    
    # 3.标记一个事务块的开始。
    MULTI 
    '''
    返回值:总是返回 OK 。
    redis 127.0.0.1:6379> MULTI            # 标记事务开始
    OK
    redis 127.0.0.1:6379> INCR user_id     # 多条命令按顺序入队
    QUEUED
    redis 127.0.0.1:6379> INCR user_id
    QUEUED
    redis 127.0.0.1:6379> INCR user_id
    QUEUED
    redis 127.0.0.1:6379> PING
    QUEUED
    redis 127.0.0.1:6379> EXEC             # 执行
    1) (integer) 1
    2) (integer) 2
    3) (integer) 3
    4) PONG
    '''
    
    # 4.取消 WATCH 命令对所有 key 的监视。
    UNWATCH 
    '''
    返回值: 总是返回 OK 。
    redis 127.0.0.1:6379> WATCH lock lock_times
    OK
    redis 127.0.0.1:6379> UNWATCH
    OK
    '''
    
    # 5.监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。
    WATCH key [key ...] 
    '''
    返回值:总是返回 OK 。
    redis> WATCH lock lock_times
    OK
    '''
    redis 事务基本操作

    Redis 脚本

      Redis 脚本使用 Lua 解释器来执行脚本。 Redis 2.6 版本通过内嵌支持 Lua 环境。执行脚本的常用命令为 EVAL

    Eval 命令的基本语法如下:

    redis 127.0.0.1:6379> EVAL script numkeys key [key ...] arg [arg ...]

    实例

    以下实例演示了 redis 脚本工作过程

    redis 127.0.0.1:6379> EVAL "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second
    
    1) "key1"
    2) "key2"
    3) "first"
    4) "second"
    # 1.执行 Lua 脚本。
    EVAL script numkeys key [key ...] arg [arg ...] 
    '''
    参数说明:
    script: 参数是一段 Lua 5.1 脚本程序。脚本不必(也不应该)定义为一个 Lua 函数。
    numkeys: 用于指定键名参数的个数。
    key [key ...]: 从 EVAL 的第三个参数开始算起,表示在脚本中所用到的那些 Redis 键(key),这些键名参数可以在 Lua 中通过全局变量 KEYS 数组,用 1 为基址的形式访问( KEYS[1] , KEYS[2] ,以此类推)。
    arg [arg ...]: 附加参数,在 Lua 中通过全局变量 ARGV 数组访问,访问的形式和 KEYS 变量类似( ARGV[1] 、 ARGV[2] ,诸如此类)。
    redis 127.0.0.1:6379> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second
    1) "key1"
    2) "key2"
    3) "first"
    4) "second"
    '''
    
    # 2.执行 Lua 脚本。
    EVALSHA sha1 numkeys key [key ...] arg [arg ...] 
    '''
    参数说明:
    sha1 : 通过 SCRIPT LOAD 生成的 sha1 校验码。
    numkeys: 用于指定键名参数的个数。
    key [key ...]: 从 EVAL 的第三个参数开始算起,表示在脚本中所用到的那些 Redis 键(key),这些键名参数可以在 Lua 中通过全局变量 KEYS 数组,用 1 为基址的形式访问( KEYS[1] , KEYS[2] ,以此类推)。
    arg [arg ...]: 附加参数,在 Lua 中通过全局变量 ARGV 数组访问,访问的形式和 KEYS 变量类似( ARGV[1] 、 ARGV[2] ,诸如此类)。
    
    redis 127.0.0.1:6379> SCRIPT LOAD "return 'hello moto'"
    "232fd51614574cf0867b83d384a5e898cfd24e5a"
    redis 127.0.0.1:6379> EVALSHA "232fd51614574cf0867b83d384a5e898cfd24e5a" 0
    "hello moto"
    '''
    
    # 3.查看指定的脚本是否已经被保存在缓存当中。
    SCRIPT EXISTS script [script ...] 
    '''
    返回值:一个列表,包含 0 和 1 ,前者表示脚本不存在于缓存,后者表示脚本已经在缓存里面了。
    列表中的元素和给定的 SHA1 校验和保持对应关系,比如列表的第三个元素的值就表示第三个 SHA1 校验和所指定的脚本在缓存中的状态。
    redis 127.0.0.1:6379> SCRIPT LOAD "return 'hello moto'"    # 载入一个脚本
    "232fd51614574cf0867b83d384a5e898cfd24e5a"
    redis 127.0.0.1:6379> SCRIPT EXISTS 232fd51614574cf0867b83d384a5e898cfd24e5a
    1) (integer) 1
    redis 127.0.0.1:6379> SCRIPT FLUSH     # 清空缓存
    OK
    redis 127.0.0.1:6379> SCRIPT EXISTS 232fd51614574cf0867b83d384a5e898cfd24e5a
    1) (integer) 0
    '''
    
    # 4.从脚本缓存中移除所有脚本。
    SCRIPT FLUSH 
    '''
    返回值:OK
    redis 127.0.0.1:6379> SCRIPT FLUSH
    OK
    '''
    
    # 5.杀死当前正在运行的 Lua 脚本。
    SCRIPT KILL 
    '''
    返回值:OK
    redis 127.0.0.1:6379> SCRIPT KILL
    OK
    '''
    
    # 6.将脚本 script 添加到脚本缓存中,但并不立即执行这个脚本。
    SCRIPT LOAD script 
    '''
    返回值:给定脚本的 SHA1 校验和
    redis 127.0.0.1:6379> SCRIPT LOAD "return 1"
    "e0e1f9fabfc9d4800c877a703b823ac0578ff8db"
    '''
    redis 脚本基本操作

     

         

  • 相关阅读:
    Ibatis,Spring整合(注解方式注入)
    Amoeba搞定mysql主从读写分离
    ClickjackFilterDeny X-Frame-Options
    Clickjacking: X-Frame-Options header missing
    升级至Struts2 2.5.2
    js和java MD5加密
    Struts2 2.5.2
    Struts2 2.5.2的套路
    java8 集合流式操作
    JS 文本输入框放大镜效果
  • 原文地址:https://www.cnblogs.com/zhuminghui/p/9645445.html
Copyright © 2020-2023  润新知