• Redis ==> 高级


    一、发布订阅

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

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

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

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

    示例:

    以下实例演示了发布订阅是如何工作的。在我们实例中我们创建了订阅频道名为 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"

    Redis 发布订阅命令:

    序号命令及描述
    1 PSUBSCRIBE pattern [pattern ...]
    订阅一个或多个符合给定模式的频道。
    2 PUBSUB subcommand [argument [argument ...]]
    查看订阅与发布系统状态。
    3 PUBLISH channel message
    将信息发送到指定的频道。
    4 PUNSUBSCRIBE [pattern [pattern ...]]
    退订所有给定模式的频道。
    5 SUBSCRIBE channel [channel ...]
    订阅给定的一个或多个频道的信息。
    6 UNSUBSCRIBE [channel [channel ...]]
    指退订给定的频道。

    二、事务

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

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

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

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

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

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

    比如:

    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 还会继续执行。

    Redis 事务命令:

    序号命令及描述
    1 DISCARD
    取消事务,放弃执行事务块内的所有命令。
    2 EXEC
    执行所有事务块内的命令。
    3 MULTI
    标记一个事务块的开始。
    4 UNWATCH
    取消 WATCH 命令对所有 key 的监视。
    5 WATCH key [key ...]
    监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。

    三、Redis 持久化

    Redis为什么需要持久化

    Redis 中的数据类型都支持 push/pop、add/remove 及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。为了保证效率,数据都是缓存在内存中。当重启系统或者关闭系统后,缓存在内存中的数据都会消失殆尽,再也找不回来了。所以,为了让数据能够长期保存,就要将 Redis 放在缓存中的数据做持久化存储。

    Redis实现持久化的两种方式

    RDB 持久化机制,是对 Redis 中的数据执行周期性的持久化。

    • RDB 会生成多个数据文件,每个数据文件都代表了某一个时刻中 Redis 的数据,这种多个数据文件的方式,非常适合做冷备,可以将这种完整的数据文件发送到一些远程的安全存储上去,比如说阿里云的 ODPS 分布式存储上,以预定好的备份策略来定期备份 Redis 中的数据。
    • RDB 对 Redis 对外提供的读写服务,影响非常小,可以让 Redis 保持高性能,因为 redis 主进程只需要 fork 一个子进程,让子进程执行磁盘 IO 操作来进行 RDB 持久化即可。
    • 相对于 AOF 持久化机制来说,直接基于 RDB 数据文件来重启和恢复 Redis 进程,更加快速。
    • 如果想要在 Redis 故障时,尽可能少的丢失数据,那么 RDB 没有 AOF 好。一般来说,RDB 数据快照文件,都是每隔 5 分钟,或者更长时间生成一次,这个时候就得接受一旦 Redis 进程宕机,那么会丢失最近 5 分钟的数据。
    • RDB 每次在 fork 子进程来执行 RDB 快照数据文件生成的时候,如果数据文件特别大,可能会导致对客户端提供的服务暂停数毫秒,或者甚至数秒。

    AOF 持久化机制,是对每条写入命令作为日志,以 append-only 的模式写入一个日志文件中,在 Redis 重启的时候,可以通过回放 AOF 日志中的写入指令来重新构建整个数据集。

    • AOF 可以更好的保护数据不丢失,一般 AOF 会每隔 1 秒,通过一个后台线程执行一次fsync操作,最多丢失 1 秒钟的数据。
    • AOF 日志文件以 append-only 模式写入,所以没有任何磁盘寻址的开销,写入性能非常高,而且文件不容易破损,即使文件尾部破损,也很容易修复。
    • AOF 日志文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写。因为在 rewrite log 的时候,会对其中的指令进行压缩,创建出一份需要恢复数据的最小日志出来。在创建新日志文件的时候,老的日志文件还是照常写入。当新的 merge 后的日志文件 ready 的时候,再交换新老日志文件即可。
    • AOF 日志文件的命令通过非常可读的方式进行记录,这个特性非常适合做灾难性的误删除的紧急恢复
    • AOF 开启后,支持的写 QPS 会比 RDB 支持的写 QPS 低,因为 AOF 一般会配置成每秒 fsync 一次日志文件,当然,每秒一次 fsync,性能也还是很高的。

    RDB 和 AOF 到底该如何选择

    • 不要仅仅使用 RDB,因为那样会导致你丢失很多数据。
    • 也不要仅仅使用 AOF,因为那样有两个问题:第一,你通过 AOF 做冷备,没有 RDB 做冷备来的恢复速度更快;第二,RDB 每次简单粗暴生成数据快照,更加健壮,可以避免 AOF 这种复杂的备份和恢复机制的 bug.
    • redis 支持同时开启开启两种持久化方式,我们可以综合使用 AOF 和 RDB 两种持久化机制,用 AOF 来保证数据不丢失,作为数据恢复的第一选择; 用 RDB 来做不同程度的冷备,在 AOF 文件都丢失或损坏不可用的时候,还可以使用 RDB 来进行快速的数据恢复。 

    四、Jedis的使用

    1、连接到 redis 服务

    public void test01() {
      //连接本地的 Redis 服务
      Jedis jedis = new Jedis("127.0.0.1",6379);
      System.out.println("连接成功");
      //查看服务是否运行
      System.out.println("服务正在运行: "+jedis.ping());
    }

    2、Redis Java String(字符串) 实例

    public void test02() {
      //连接本地的 Redis 服务
      Jedis jedis = new Jedis("127.0.0.1",6379);
      System.out.println("连接成功");
      //设置 redis 字符串数据
      jedis.set("runoobkey", "www.runoob.com");
      // 获取存储的数据并输出
      System.out.println("redis 存储的字符串为: "+ jedis.get("runoobkey"));
    }

    3、Redis Java List(列表) 实例

    public void test03() {
      //连接本地的 Redis 服务
      Jedis jedis = new Jedis("127.0.0.1",6379);
      System.out.println("连接成功");
      //存储数据到列表中
      jedis.lpush("site-list", "Runoob");
      jedis.lpush("site-list", "Google");
      jedis.lpush("site-list", "Taobao");
      // 获取存储的数据并输出
      List<String> list = jedis.lrange("site-list", 0 ,2);
      for(int i=0; i<list.size(); i++) {
        System.out.println("列表项为: "+list.get(i));
      }
    }

    4、Redis Java Keys 实例

    public void test04() {
      //连接本地的 Redis 服务
      Jedis jedis = new Jedis("127.0.0.1",6379);
      System.out.println("连接成功");
    
      // 获取数据并输出
      Set<String> keys = jedis.keys("*");
      Iterator<String> it=keys.iterator() ;
      while(it.hasNext()){
        String key = it.next();
        System.out.println(key);
      }
    }

    5、连接池

    public class RedisTest {
    
      public static void main(String[] args) {
    
        // 初始化Jedis连接池配置
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
    
        // 初始化连接池
        JedisPool jedisPool = new JedisPool(jedisPoolConfig,"192.168.182.128");
    
        // 获取资源
        Jedis jedis = jedisPool.getResource();
    
        // 验证密码
        jedis.auth("123456");
    
        // 操作
        jedis.set("jack","chen");
        String res = jedis.get("jack");
    
        System.out.println("res = " + res);
    
        // 关闭连接
        jedis.close();
      }
    }

    6、RedisUtils

    public class RedisUtils {
      //服务器IP地址
      private static String ADDR = "127.0.0.1";
      //端口
      private static int PORT = 6379;
      //密码
      private static String AUTH = "123456";
      //连接实例的最大连接数
      private static int MAX_ACTIVE = 1024;
      //控制一个pool最多有多少个状态为idle(空闲的)的jedis实例,默认值也是8。
      private static int MAX_IDLE = 200;
      //等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException
      private static int MAX_WAIT = 10000;
      //连接超时的时间  
      private static int TIMEOUT = 10000;
      // 在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;
      private static boolean TEST_ON_BORROW = true;
    
      private static JedisPool jedisPool = null;
      //数据库模式是16个数据库 0~15
      public static final int DEFAULT_DATABASE = 0;
    
    
      /**
       * 初始化Redis连接池
       */
      static {
        try {
          JedisPoolConfig config = new JedisPoolConfig();
          config.setMaxTotal(MAX_ACTIVE);
          config.setMaxIdle(MAX_IDLE);
          config.setMaxWaitMillis(MAX_WAIT);
          config.setTestOnBorrow(TEST_ON_BORROW);
          jedisPool = new JedisPool(config, ADDR, PORT, TIMEOUT,AUTH,DEFAULT_DATABASE);
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    
    
      /**
       * 获取Jedis实例
       */
      public synchronized static Jedis getJedis() {
    
        try {
    
          if (jedisPool != null) {
            Jedis resource = jedisPool.getResource();
            System.out.println("redis--服务正在运行: "+resource.ping());
            return resource;
          } else {
            return null;
          }
    
        } catch (Exception e) {
          e.printStackTrace();
          return null;
        }
      }
    
    
      /***
       *
       * 释放资源
       */
      public static void returnResource(final Jedis jedis) {
        if(jedis != null) {
          jedisPool.close();
        }
      }
    }
    RedisUtils
  • 相关阅读:
    学习NAnt Build .CS+Solution+MSBuild+SVN+NUnit+NUnitReport
    每日集成CruiseControl.NET + SVN + Msbuild + NAnt
    通过spring.net中的spring.caching CacheResult实现memcached缓存
    c# 简单文件流读写CSV文件
    ajax系列之用jQuery的ajax方法向服务器发出get和post请求
    我的2016书单以及为2017年准备的书单
    微信网页开发之获取用户unionID的两种方法--基于微信的多点登录用户识别
    微信开发之分清公众平台和开放平台、公众号全局凭证和网页授权凭证
    网站实现微信登录之回调函数中登录逻辑的处理--基于yii2开发的描述
    网站实现微信登录之嵌入二维码——基于yii2开发的描述
  • 原文地址:https://www.cnblogs.com/L-Test/p/11625197.html
Copyright © 2020-2023  润新知