(六)、Redis监控/乐观锁(watch)
1、悲观锁:顾名思义就是做什么事情都很悲观,无论干什么都要上锁。(影响性能的)
2、乐观锁:做什么事都很乐观,都不会去上锁;只有更新数据的时候去才判断一下version,看一下在此期间是否有人修改过这个数据:
- 获取version
- 更新的时候比较version
使用watch key监控指定数据,相当于乐观锁加锁。
1.正常执行乐观锁
单线程执行成功!
127.0.0.1:6379> set money 100 #设置余额100 OK 127.0.0.1:6379> set out 0 # 发出 0 OK 127.0.0.1:6379> watch money # 开始监视余额 OK 127.0.0.1:6379> multi # 开启事务 OK 127.0.0.1:6379(TX)> decrby money 20 # 画出20 QUEUED 127.0.0.1:6379(TX)> incrby out 20 QUEUED 127.0.0.1:6379(TX)> EXEC #结束 1) (integer) 80 2) (integer) 20
2.多线程执行乐观锁
一个服务器,两个客户端
第一个线程,继续监视money,然后开启事务设置操作**但不提交**
127.0.0.1:6379> watch money OK 127.0.0.1:6379> multi OK 127.0.0.1:6379(TX)> DECRBY money 10 QUEUED 127.0.0.1:6379(TX)> INCRBY out 10 QUEUED 127.0.0.1:6379(TX)>
第二个线程 进行充值1000元人民币
127.0.0.1:6379> get money "80" 127.0.0.1:6379> set money 1000 OK 127.0.0.1:6379> get money "1000"
然后第一个线程提交事务,发现会出错
127.0.0.1:6379> watch money OK 127.0.0.1:6379> multi OK 127.0.0.1:6379(TX)> DECRBY money 10 QUEUED 127.0.0.1:6379(TX)> INCRBY out 10 QUEUED 127.0.0.1:6379(TX)> EXEC # 提交事务,但是出错 (nil)
无论事务是否执行成功,Redis都会取消watch监控
如果发现事务执行失败,我们需要先解锁。
127.0.0.1:6379> unwatch # 解锁 OK 127.0.0.1:6379> watch money # 重新上锁 OK 127.0.0.1:6379> multi OK 127.0.0.1:6379(TX)> DECRBY money 10 QUEUED 127.0.0.1:6379(TX)> INCRBY out 10 QUEUED 127.0.0.1:6379(TX)> EXEC 1) (integer) 990 2) (integer) 30
注意:
- 如果发现事务执行失败就先解锁 unwatch
- 获取最新的值,再次进行监视 watch key
- 比对监视的值是否发生了变化,如果没有变化那么可以执行成功,反之则会失败。
(七)、Jedis
Jedis是Redis官方推荐使用的Java连接redis的客户端。所以我们在使用Java来操作redis时就要学习Jedis。
1.导入相关的依赖
首先设置一个空项目,然后创建一个Maven项目
<dependencies> <!--导入jredis的包--> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.2.0</version> </dependency> <!--fastjson--> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.70</version> </dependency> </dependencies>
2.测试远程Redis
假如说连接Linux系统。
- 注释掉配置文件中的 bind 127.0.0.1
- daemonize 设置为no
- 阿里云开启安全组6379 宝塔也要开启6379
- 要开启服务端的redis
- 一定要设置密码 否则容易被黑。
测试:
package com.jsxs; import redis.clients.jedis.Jedis; public class testPing { public static void main(String[] args) { // 1. new Jedis对象 Jedis jedis = new Jedis("IP", 6379); jedis.auth("密码"); System.out.println(jedis.ping()); } }
输出
package com.jsxs; import redis.clients.jedis.Jedis; public class testPing { public static void main(String[] args) { // 1. new Jedis对象 Jedis jedis = new Jedis("IP", 6379); jedis.auth("密码"); // 2.测试是否链接成功 System.out.println(jedis.ping()); System.out.println("清空数据库->"+ jedis.flushDB()); System.out.println("判断某一个值是否存在->"+ jedis.exists("username")); System.out.println("新增一个键值对->"+jedis.set("username","jsxs")); System.out.println("新增一个键值对->"+ jedis.set("password","15945")); System.out.println("获取全部键值对"+jedis.keys("*")); System.out.println("获取范围内的值"+jedis.getrange("username",0,1)); } }
3.事务相关案列
事务相关举列
成功案列事务
package com.jsxs; import com.alibaba.fastjson.JSONObject; import redis.clients.jedis.Jedis; import redis.clients.jedis.Transaction; public class testTX { public static void main(String[] args) { Jedis jedis = new Jedis("8.130.48.9", 6379); jedis.auth("121788"); System.out.println(jedis.ping()); // 设置JSON数据 JSONObject jsonObject = new JSONObject(); jsonObject.put("hello","world"); jsonObject.put("name","jsxs"); Transaction multi = jedis.multi();//开启事务 String result = jsonObject.toJSONString(); //把JSON数据转换为字符串 try { multi.set("user1",result); multi.set("user2",result); multi.exec(); //假如成功就执行 } catch (Exception e) { multi.discard(); //假如执行失败,就取消事务 e.printStackTrace(); } finally { System.out.println(jedis.get("user1")); System.out.println(jedis.get("user2")); jedis.close(); //最终都关闭客户端 } } }
事务失败
package com.jsxs; import com.alibaba.fastjson.JSONObject; import redis.clients.jedis.Jedis; import redis.clients.jedis.Transaction; public class testTX { public static void main(String[] args) { Jedis jedis = new Jedis("8.130.48.9", 6379); jedis.auth("121788"); System.out.println(jedis.ping()); jedis.flushDB(); // 设置JSON数据 JSONObject jsonObject = new JSONObject(); jsonObject.put("hello","world"); jsonObject.put("name","jsxs"); Transaction multi = jedis.multi();//开启事务 String result = jsonObject.toJSONString(); //把JSON数据转换为字符串 try { multi.set("user1",result); multi.set("user2",result); int i=1/0; multi.exec(); //假如成功就执行 } catch (Exception e) { multi.discard(); //假如执行失败,就取消事务 System.out.println("事务执行失败"); e.printStackTrace(); } finally { System.out.println(jedis.get("user1")); System.out.println(jedis.get("user2")); jedis.close(); //最终都关闭客户端 } } }
(八)、SpringBoot整合Redis
1.导入一个整合包
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
说明: 在SpringBoot2.x之后,原来使用的jedis被替换为了lettuce.
- jedis: 采用的是直连,多个线程操作的话,是不安全的,如果想要避免不安全的,使用jedis oppl连接池,更像BIO模式。
- lerruce: 采用netty,实列可以在多个线程中进行共享,不存在线程不安全的情况,可以减少线程数据了,更像NIO模式