一.Redis中的事务
在Redis中,单条命令依旧保持原子性,但是对于事务来说(命令集)不保证原子性
Redis事务的本质:一组命令的集合,一个事务中所有的命令都会被序列化,在事务的执行过程中,会按照顺序执行,一次性,顺序性,排他性!执行一些命令
如:--- 队列 set1,set2,set3 执行--
Redis事务没有隔离级别的概念,在创建Redis事务中,命令不会被直接执行,而是由exec命令发出后开始队列化执行
Redis执行事务的过程:
- 开启事务(multi)
- 命令入队(......)
- 执行事务(exec)
放弃事务:DISCARD
执行事务的两种错误情况
1.编译型异常(代码有问题,命令有错)
这种错误会导致整个事务都不会被执行
2.运行时异常(代码逻辑异常)
如果事务队列存在语法性的错误,那么执行命令的时候,其它命令都是可以正常执行的,但是语法性错误的指令肯定执行不了,丢失原子性
二.Redis中的乐观锁(监控)
悲观锁(Pessimistic Locking)
- 假设最坏的情况会发生,在读取数据时就加锁,防止其他事务修改该数据。
- 在整个事务过程中保持锁定状态,直到事务提交或回滚。
乐观锁(Optimistic Locking)
- 假设在大多数情况下不会发生冲突,因此在读取数据时不加锁。
- 在更新数据时才检查数据是否被其他事务修改过。如果没有被修改,则更新成功;如果已经被修改,则放弃本次更新或重新尝试。
watch:监视对象
当在被监视的情况下的对象发生变化,那么事务会执行失败,需要放弃原监视后,再次监视;如果事务执行成功,则自动放弃监视
执行失败的情况:
在监视的情况下,另外的一个线程率先更改值,那么被监视的事务会执行失败
另一线程率先更改
返回本线程执行事务:
本线程执行失败
三.Jedis
使用Java操作redis是官方推荐使用的一款API,Jedis是一款操作Redis的中间件,也是操作redis的基础,虽然在springBoot中已经被其它的中间件取代,但是Jedis的操作上手程度很适合新手
导入依赖:
<!-- https://mvnrepository.com/artifact/redis.clients/jedis --> <dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>5.2.0</version> </dependency>
直接新建一个Jedis测试类,创建一个jedis对象,并且传递参数
//创建jedis对象Jedis jedis = new Jedis("127.0.0.1",6379);//测试连接System.out.println(jedis.ping());
String类型测试
//测试String类型jedis.set("name","maming");System.out.println(jedis.get("name"));//批量创建k-vjedis.mset("k1","v1","k2","v2","k3","v3");//批量获得值System.out.println(jedis.mget("k1", "k2", "k3"));
测试list集合
//清空数据库 jedis.flushDB();//向list中添加元素jedis.lpush("list","a","b","c");//获取list中的所有元素System.out.println(jedis.lrange("list",0,-1));//从list中弹出最后一个元素System.out.println(jedis.rpop("list"));//获取list中的所有元素System.out.println(jedis.lrange("list",0,-1));
测试set集合
//清空数据库 jedis.flushDB();//向集合中添加元素jedis.sadd("myset","a","b","c");//打印集合中的元素System.out.println(jedis.smembers("myset"));
其它的命令都是和在redis上一样的,都不用测试了
Jedis事务
在Jedis中的事务和Redis中过程都是一样的,并且性质也是一样,只不过使用Java语言开启和执行事务罢了
//清空数据库 jedis.flushDB();// 创建一个JSONObject对象JSONObject jsonObject = new JSONObject();// 向JSONObject对象中添加键值对jsonObject.put("name","zhangsan");jsonObject.put("age","18");// 将JSONObject对象转换为字符串String re = jsonObject.toString();// 创建一个Transaction对象Transaction multi = jedis.multi();try {// 在Transaction对象中执行set操作multi.set("user1",re);multi.set("user2",re);// 执行Transaction对象中的操作 multi.exec();}catch (Exception e){// 打印异常信息 e.printStackTrace();// 放弃Transaction对象中的操作 multi.discard();}finally {// 打印user1的值System.out.println(jedis.get("user1"));// 打印user2的值System.out.println(jedis.get("user2"));// 关闭jedis连接 jedis.close();}
四.SpringBoot整合Redis
如果SpringBoot需要操作数据类型必须要使用另一个家族成员SpringData
在SpringBoot2.0以后的版本Jedis都被改为lettuce
Jedis:采用的是直连,多个线程操作是不安全的,想要避免的话就是用jedis线程池
lettuce:次啊用netty,实例可以在多个线程中共享,不存在线程不安全的情况
安装依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>
配置Redi数据源:
spring.data.redis.host=127.0.0.1 spring.data.redis.port=6379 spring.data.redis.database=0
测试类:
@Autowiredprivate RedisTemplate redisTemplate;@Test// 测试上下文加载void contextLoads() {// 从Redis中获取键为"name"的值并打印System.out.println(redisTemplate.opsForValue().get("name"));// 将字符串"a"添加到Redis中键为"list"的列表的左边redisTemplate.opsForList().leftPush("list","a");// 从Redis中键为"list"的列表的左边弹出元素并打印System.out.println(redisTemplate.opsForList().leftPop("list"));}
测试结果
springBoot整合后的主要操作数据方法:
opsForValue): 用于处理简单的字符串键值对。
opsForList): 用于处理列表数据结构。
opsForSet): 用于处理无序集合数据结构。
opsForZSet): 用于处理有序集合数据结构。
opsForHash): 用于处理哈希数据结构。
Redis自动配置类:
Redis的自动配置属性(默认写入的地址和端口):