Redis主从复制、哨兵模式、Java客户端使用redis

Redis持久化RDB和AOF、事务管理:https://blog.csdn.net/weixin_45606067/article/details/107904567

七、Redis的发布订阅

1、是什么

进程间的一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。

订阅/发布消息图
在这里插入图片描述

2、命令

在这里插入图片描述

3、案例

先订阅后发布后才能收到消息,
1 可以一次性订阅多个,SUBSCRIBE c1 c2 c3

2 消息发布,PUBLISH c2 hello-redis

3 订阅多个,通配符*, PSUBSCRIBE new*
4 收取消息, PUBLISH new1 redis2015

八、Redis的复制(Master/Slave)

1、是什么

行话:也就是我们所说的主从复制,主机数据更新后根据配置和策略,自动同步到备机的master/slaver机制,Master以写为主,Slave以读为主。

2、能干嘛

  • 读写分离
  • 容灾恢复

3、怎么玩

1)配从(库)不配主(库)

2)从库配置:slaveof 主库IP 主库端口

  • 每次与master断开之后,都需要重新连接,除非你配置进redis.conf文件
  • Info replication:查看主机还是从机信息

3)修改配置文件细节操作(6379,6380,6381)

  • 拷贝多个redis.conf文件
    在这里插入图片描述

  • 开启daemonize yes
    在这里插入图片描述

  • pid文件名字

  • 指定端口

  • Log文件名字
    在这里插入图片描述

  • Dump.rdb名字
    在这里插入图片描述

4)常用3招

① 一主二仆

  • Init
    在这里插入图片描述

  • 一个Master两个Slave
    在这里插入图片描述

  • 日志查看

    • 主机日志
      在这里插入图片描述

    • 备机日志
      在这里插入图片描述

    • info replication
      在这里插入图片描述

    • 主从问题演示

      1 切入点问题?slave1、slave2是从头开始复制还是从切入点开始复制? 比如从k4进来,那之前的123是可以复制。

      2 从机是否可以写?set可否? 不可以写,只有主机可以写。

      3 主机shutdown后情况如何?从机是上位还是原地待命 原地待命

      4 主机又回来了后,主机新增记录,从机还能否顺利复制? 能复制

      5 其中一台从机down后情况如何? 另一台还是继续工作。

      依照原有它能跟上大部队吗?不会 回来之后就是master,你的需要重新配置,除非你配置进redis.conf文件。

② 薪火相传

  • 上一个Slave可以是下一个slave的Master,Slave同样可以接收其他slaves的连接和同步请求,那么该slave作为了链条中下一个的master,可以有效减轻master的写压力。
  • 中途变更转向:会清除之前的数据,重新建立拷贝最新的。
  • Slaveof 新主库IP 新主库端口

③ 反客为主

SLAVEOF no one

使当前数据库停止与其他数据库的同步,转成主数据库。

4、复制原理

  • Slave启动成功连接到master后会发送一个sync命令。
  • Master接到命令启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令,
    在后台进程执行完毕之后,master将传送整个数据文件到slave,以完成一次完全同步。
  • 全量复制:而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中。
  • 增量复制:Master继续将新的所有收集到的修改命令依次传给slave,完成同步。
  • 但是只要是重新连接master,一次完全同步(全量复制)将被自动执行

5、哨兵模式(sentinel)

1)是什么

反客为主的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库。

2)怎么玩(使用步骤)

  • 调整结构,6379带着80、81

  • 自定义的/myredis目录下新建 sentinel.conf 文件,名字绝不能错。

  • 配置哨兵,填写内容

    • sentinel monitor 被监控主机的名字(自己起名字)127.0.0.1 6379 1
      在这里插入图片描述

    • 上面最后一个数字1,表示主机挂掉后salve投票看让谁接替成为主机,得票数多少后成为主机。

  • 启动哨兵

    • Redis-sentinel /myredis/sentinel.conf

    • 上述目录依照各自的实际情况配置,可能目录不同
      在这里插入图片描述

  • 正常主从演示

  • 原有的master挂了
    在这里插入图片描述

  • 投票新选
    在这里插入图片描述

  • 重新主从继续开工,info replication查看
    在这里插入图片描述

  • 问题:如果之前的master重启回来,会不会双master冲突?
    在这里插入图片描述

3)一组 sentinel 能同时监控多个Master

6、复制的缺点

  • 复制延时

    由于所有的写操作都是先在Master上操作,然后同步更新到Slave上,所以从Master同步到Slave机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave机器数量的增加也会使这个问题更加严重。

九、Redis的Java客户端Jedis

1、安装JDK

tar -zxvf jdk-7u67-linux-i586.tar.gz

vi /etc/profile

重启一次Centos

编码验证

具体安装步骤:https://blog.csdn.net/weixin_45606067/article/details/107845918

2、安装eclipse

3、Jedis所需要的jar包

Commons-pool-1.6.jar

Jedis-2.1.0.jar

4、Jedis常用操作

  • 测试连通性

    public class Demo01 {
      public static void main(String[] args) {
        //连接本地的 Redis 服务
        Jedis jedis = new Jedis("127.0.0.1",6379);
        //查看服务是否运行,打出pong表示OK
        System.out.println("connection is OK==========>: "+jedis.ping());
      }
    }
    
  • 5+1

    一个key

    五大数据类型

    package com.atguigu.redis.test;
    import java.util.*;
    import redis.clients.jedis.Jedis;
    
    public class Test02 
    {
      public static void main(String[] args) 
      {
     
         Jedis jedis = new Jedis("127.0.0.1",6379);
         //key
         Set<String> keys = jedis.keys("*");
         for (Iterator iterator = keys.iterator(); iterator.hasNext();) {
           String key = (String) iterator.next();
           System.out.println(key);
         }
         System.out.println("jedis.exists====>"+jedis.exists("k2"));
         System.out.println(jedis.ttl("k1"));
         //String
         //jedis.append("k1","myreids");
         System.out.println(jedis.get("k1"));
         jedis.set("k4","k4_redis");
         System.out.println("----------------------------------------");
         jedis.mset("str1","v1","str2","v2","str3","v3");
         System.out.println(jedis.mget("str1","str2","str3"));
         //list
         System.out.println("----------------------------------------");
         //jedis.lpush("mylist","v1","v2","v3","v4","v5");
         List<String> list = jedis.lrange("mylist",0,-1);
         for (String element : list) {
           System.out.println(element);
         }
         //set
         jedis.sadd("orders","jd001");
         jedis.sadd("orders","jd002");
         jedis.sadd("orders","jd003");
         Set<String> set1 = jedis.smembers("orders");
         for (Iterator iterator = set1.iterator(); iterator.hasNext();) {
           String string = (String) iterator.next();
           System.out.println(string);
         }
         jedis.srem("orders","jd002");
         System.out.println(jedis.smembers("orders").size());
         //hash
         jedis.hset("hash1","userName","lisi");
         System.out.println(jedis.hget("hash1","userName"));
         Map<String,String> map = new HashMap<String,String>();
         map.put("telphone","13811814763");
         map.put("address","atguigu");
         map.put("email","abc@163.com");
         jedis.hmset("hash2",map);
         List<String> result = jedis.hmget("hash2", "telphone","email");
         for (String element : result) {
           System.out.println(element);
         }
         //zset
         jedis.zadd("zset01",60d,"v1");
         jedis.zadd("zset01",70d,"v2");
         jedis.zadd("zset01",80d,"v3");
         jedis.zadd("zset01",90d,"v4");
         
         Set<String> s1 = jedis.zrange("zset01",0,-1);
         for (Iterator iterator = s1.iterator(); iterator.hasNext();) {
           String string = (String) iterator.next();
           System.out.println(string);
         } 
      }
    }
    
  • 事务提交

    • 日常

      package com.atguigu.redis.test;
       
      import redis.clients.jedis.Jedis;
      import redis.clients.jedis.Response;
      import redis.clients.jedis.Transaction;
       
      public class Test03 
      {
        public static void main(String[] args) 
        {
           Jedis jedis = new Jedis("127.0.0.1",6379);
           
           //监控key,如果该动了事务就被放弃
           /*3
           jedis.watch("serialNum");
           jedis.set("serialNum","s#####################");
           jedis.unwatch();*/
           
           Transaction transaction = jedis.multi();//被当作一个命令进行执行
           Response<String> response = transaction.get("serialNum");
           transaction.set("serialNum","s002");
           response = transaction.get("serialNum");
           transaction.lpush("list3","a");
           transaction.lpush("list3","b");
           transaction.lpush("list3","c");
           
           transaction.exec();
           //2 transaction.discard();
           System.out.println("serialNum***********"+response.get());
                
        }
      }
      
    • 加锁

      public class TestTransaction {
       
        public boolean transMethod() {
           Jedis jedis = new Jedis("127.0.0.1", 6379);
           int balance;// 可用余额
           int debt;// 欠额
           int amtToSubtract = 10;// 实刷额度
       
           jedis.watch("balance");
           //jedis.set("balance","5");//此句不该出现,讲课方便。模拟其他程序已经修改了该条目
           balance = Integer.parseInt(jedis.get("balance"));
           if (balance < amtToSubtract) {
             jedis.unwatch();
             System.out.println("modify");
             return false;
           } else {
             System.out.println("***********transaction");
             Transaction transaction = jedis.multi();
             transaction.decrBy("balance", amtToSubtract);
             transaction.incrBy("debt", amtToSubtract);
             transaction.exec();
             balance = Integer.parseInt(jedis.get("balance"));
             debt = Integer.parseInt(jedis.get("debt"));
       
             System.out.println("*******" + balance);
             System.out.println("*******" + debt);
             return true;
           }
        }
       
        /**
         * 通俗点讲,watch命令就是标记一个键,如果标记了一个键, 在提交事务前如果该键被别人修改过,那事务就会失败,这种情况通常可以在程序中
         * 重新再尝试一次。
         * 首先标记了键balance,然后检查余额是否足够,不足就取消标记,并不做扣减; 足够的话,就启动事务进行更新操作,
         * 如果在此期间键balance被其它人修改, 那在提交事务(执行exec)时就会报错, 程序中通常可以捕获这类错误再重新执行一次,直到成功。
         */
        public static void main(String[] args) {
           TestTransaction test = new TestTransaction();
           boolean retValue = test.transMethod();
           System.out.println("main retValue-------: " + retValue);
        }
      }
      
  • 主从复制

    6379,6380启动,先各自先独立

    主写

    从读

    public static void main(String[] args) throws InterruptedException 
      {
         Jedis jedis_M = new Jedis("127.0.0.1",6379);
         Jedis jedis_S = new Jedis("127.0.0.1",6380);
         
         jedis_S.slaveof("127.0.0.1",6379);
         
         jedis_M.set("k6","v6");
         Thread.sleep(500);
         System.out.println(jedis_S.get("k6"));
      }
    

5、JedisPool

  • 获取Jedis实例需要从JedisPool中获取

  • 用完Jedis实例需要返还给JedisPool

  • 如果Jedis在使用过程中出错,则也需要还给JedisPool

  • 案例见代码

    • JedisPoolUtil

      package com.atguigu.redis.test;
       
      import redis.clients.jedis.Jedis;
      import redis.clients.jedis.JedisPool;
      import redis.clients.jedis.JedisPoolConfig;
       
      public class JedisPoolUtil {
        
       private static volatile JedisPool jedisPool = null;//被volatile修饰的变量不会被本地线程缓存,对该变量的读写都是直接操作共享内存。
        
        private JedisPoolUtil() {}
        
        public static JedisPool getJedisPoolInstance()
       {
           if(null == jedisPool)
          {
             synchronized (JedisPoolUtil.class)
            {
                if(null == jedisPool)
               {
                 JedisPoolConfig poolConfig = new JedisPoolConfig();
                 poolConfig.setMaxActive(1000);
                 poolConfig.setMaxIdle(32);
                 poolConfig.setMaxWait(100*1000);
                 poolConfig.setTestOnBorrow(true);
                  
                  jedisPool = new JedisPool(poolConfig,"127.0.0.1");
               }
            }
          }
           return jedisPool;
       }
        
        public static void release(JedisPool jedisPool,Jedis jedis)
       {
           if(null != jedis)
          {
            jedisPool.returnResourceObject(jedis);
          }
       }
      }
      
    • Demo5

      JedisPool.getResource();

      package com.atguigu.redis.test;
       
      import redis.clients.jedis.Jedis;
      import redis.clients.jedis.JedisPool;
       
      public class Test01 {
        public static void main(String[] args) {
           JedisPool jedisPool = JedisPoolUtil.getJedisPoolInstance();
           Jedis jedis = null;
           
           try 
           {
             jedis = jedisPool.getResource();
             jedis.set("k18","v183");
             
           } catch (Exception e) {
             e.printStackTrace();
           }finally{
             JedisPoolUtil.release(jedisPool, jedis);
           }
        }
      }
      
  • 配置总结all

     
    JedisPool的配置参数大部分是由JedisPoolConfig的对应项来赋值的。
     
    maxActive:控制一个pool可分配多少个jedis实例,通过pool.getResource()来获取;如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted。
    maxIdle:控制一个pool最多有多少个状态为idle(空闲)的jedis实例;
    whenExhaustedAction:表示当pool中的jedis实例都被allocated完时,pool要采取的操作;默认有三种。
     WHEN_EXHAUSTED_FAIL --> 表示无jedis实例时,直接抛出NoSuchElementException;
     WHEN_EXHAUSTED_BLOCK --> 则表示阻塞住,或者达到maxWait时抛出JedisConnectionException;
     WHEN_EXHAUSTED_GROW --> 则表示新建一个jedis实例,也就说设置的maxActive无用;
    maxWait:表示当borrow一个jedis实例时,最大的等待时间,如果超过等待时间,则直接抛JedisConnectionException;
    testOnBorrow:获得一个jedis实例的时候是否检查连接可用性(ping());如果为true,则得到的jedis实例均是可用的;
     
    testOnReturn:return 一个jedis实例给pool时,是否检查连接可用性(ping());
     
    testWhileIdle:如果为true,表示有一个idle object evitor线程对idle object进行扫描,如果validate失败,此object会被从pool中drop掉;这一项只有在timeBetweenEvictionRunsMillis大于0时才有意义;
     
    timeBetweenEvictionRunsMillis:表示idle object evitor两次扫描之间要sleep的毫秒数;
     
    numTestsPerEvictionRun:表示idle object evitor每次扫描的最多的对象数;
     
    minEvictableIdleTimeMillis:表示一个对象至少停留在idle状态的最短时间,然后才能被idle object evitor扫描并驱逐;这一项只有在timeBetweenEvictionRunsMillis大于0时才有意义;
     
    softMinEvictableIdleTimeMillis:在minEvictableIdleTimeMillis基础上,加入了至少minIdle个对象已经在pool里面了。如果为-1,evicted不会根据idle time驱逐任何对象。如果minEvictableIdleTimeMillis>0,则此项设置无意义,且只有在timeBetweenEvictionRunsMillis大于0时才有意义;
     
    lifo:borrowObject返回对象时,是采用DEFAULT_LIFO(last in first out,即类似cache的最频繁使用队列),如果为False,则表示FIFO队列;
     
    ==================================================================================================================
    其中JedisPoolConfig对一些参数的默认设置如下:
    testWhileIdle=true
    minEvictableIdleTimeMills=60000
    timeBetweenEvictionRunsMillis=30000
    numTestsPerEvictionRun=-1
    

如果有收获!!! 希望老铁们来个三连,点赞、收藏、转发。
创作不易,别忘点个赞,可以让更多的人看到这篇文章,顺便鼓励我写出更好的博客
  • 14
    点赞
  • 57
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值