redis深入学习(三)-----事务、主从复制、jedis

reids事务

概念

可以一次执行多个命令,本质是一组命令的集合。一个事务中的所有命令都会序列化,按顺序地串行化执行而不会被其它命令插入,不许加塞

作用

一个队列中,一次性、顺序性、排他性的执行一系列命令

常用命令

正常操作事务:

放弃事务:

 其实redis对于事务是部分支持:

 例如incr k1虽然是错误的(类似于java的运行时异常),但是其他几个结果依然可以成功操作。

watch监控

悲观锁/乐观锁/CAS(Check And Set) 

1、悲观锁

悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁

2、乐观锁

乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。

乐观锁策略:提交版本必须大于记录当前版本才能执行更新

3、CAS

CAS机制当中使用了3个基本操作数:内存地址V,旧的预期值A,要修改的新值B。

更新一个变量的时候,只有当变量的预期值A和内存地址V当中的实际值相同时,才会将内存地址V对应的值修改为B。

4、watch小结

a、Watch指令,类似乐观锁,事务提交时,如果Key的值已被别的客户端改变,比如某个list已被别的客户端push/pop过了,整个事务队列都不会被执行

b、通过WATCH命令在事务执行之前监控了多个Keys,倘若在WATCH之后有任何Key的值发生了变化,EXEC命令执行的事务都将被放弃,同时返回Nullmulti-bulk应答以通知调用者事务执行失败

3阶段

开启:以MULTI开始一个事务

入队:将多个命令入队到事务中,接到这些命令并不会立即执行,而是放到等待执行的事务队列里面

执行:由EXEC命令触发事务

3特性

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

没有隔离级别的概念:队列中的命令没有提交之前都不会实际的被执行,因为事务提交前任何指令都不会被实际执行,也就不存在”事务内的查询要看到事务里的更新,在事务外查询不能看到”这个让人万分头痛的问题

不保证原子性:redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚

Redis的发布订阅 

概念

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

 

命令

主从复制 -----Redis的复制(Master/Slave)

概念

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

1、一主二仆-----将军死了,士兵等待上级派新将军

 2、薪火相传

上一个Slave可以是下一个slave的Master,Slave同样可以接收其他slaves的连接和同步请求,那么该slave作为了链条中下一个的master,可以有效减轻master的写压力。

中途变更转向:会清除之前的数据,重新建立拷贝最新的。

slaveof 新主库IP 新主库端口

 3、反客为主

使当前数据库停止与其他数据库的同步,转成主数据库。-----将军死了,士兵替代他

SLAVEOF no one

作用

1、读写分离

2、容灾恢复

配置

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

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

每次与master断开之后,都需要重新连接,除非你配置进redis.conf文件。info replication可以查看当前redis服务信息,master or slave

代表master为192.168.101.3,端口为6379

复制原理

1、slave启动成功连接到master后会发送一个sync命令

2、Master接到命令启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执行完毕之后,master将传送整个数据文件到slave,以完成一次完全同步

3、全量复制:而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中。

4、增量复制:Master继续将新的所有收集到的修改命令依次传给slave,完成同步

5、但是只要是重新连接master,一次完全同步(全量复制)将被自动执行

哨兵模式

概念

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

配置

在redis目录下新建sentinel.conf文件,名字绝不能错

 sentinel monitor 被监控数据库名字(自己起名字) 127.0.0.1 6379 1

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

启动

总结:将军死了,士兵进行投票,票数合格的当选将军;此时将军又活了,回来变成小弟了。 

缺点

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

jedis

所需jar包

commons-pool-1.6.jar、jedis-2.1.0.jar

测试连通性

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());
    }
}

常用api

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","[email protected]");
         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);
        }     
    }
}

事务提交

主从复制 

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"));
  }

jedispool

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);
    }
 }
}
 
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);
     }
  }
}
 

配置总结:

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

猜你喜欢

转载自www.cnblogs.com/alimayun/p/11575372.html