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
原文地址:https://www.cnblogs.com/alimayun/p/11575372.html