事务
Redis事务的本质:一组命令的集合
一次性、顺序性、排他性
- Redis单条命令是保证原子性的,但是事务不保证原子性
- Redis事务没有隔离级别的概念
- 所有的命令在事务中,并没有直接被执行,只有发起执行命令的时候才会执行
执行事务
Redis事务包括三个命令:
- 开启事务multi
- 命令入队
- 执行事务exec
连接1:0>multi #开启事务
"OK"
连接1:0>set k1 v1 #命令入队
"QUEUED"
连接1:0>set k2 v2
"QUEUED"
连接1:0>get k2
"QUEUED"
连接1:0>exec #执行事务
1) "OK"
2) "OK"
3) "v2"
放弃事务
一旦放弃事务,则之前开启事务后所有的命令入队都失效
连接1:0>multi #开启事务
"OK"
连接1:0>set k1 v1 #命令入队
"QUEUED"
连接1:0>set k2 v2
"QUEUED"
连接1:0>discard #放弃事务
"OK"
连接1:0>get k1 #取值失败
null
小结
- 编译型异常:事务所有的命令都不会被执行
连接1:0>multi
"OK"
连接1:0>set k1 v1
"QUEUED"
连接1:0>getset k2 #事务中故意执行一个错误命令
"ERR wrong number of arguments for 'getset' command"
连接1:0>set k3 v3
"QUEUED"
连接1:0>exec
"EXECABORT Transaction discarded because of previous errors."
连接1:0>get k1 #事务结束时取不到值,所有的命令都不会被执行
null
- 运行时异常:如果事务队列中编译通过但是运行时异常,那么在执行命令时其他命令可以正常执行,错误命令会被抛出
连接1:0>multi #开启事务
"OK"
连接1:0>set k1 v1
"QUEUED"
连接1:0>incr k1 #给字符串自增1,会出错,但是编译时不会报错
"QUEUED"
连接1:0>set k2 v2
"QUEUED"
连接1:0>get k2
"QUEUED"
连接1:0>exec #执行事务,自增会报错,但是其他命令会被正常执行
1) "OK"
2) "ERR value is not an integer or out of range"
3) "OK"
4) "v2"
锁
悲观锁
悲观锁认为什么时候都会出问题,无论做什么都会加锁
由于每次都上锁,导致效率极其低下
乐观锁
乐观锁认为什么时候都不会出问题,所有不会上锁。更新数据时取判断在此期间是否有人修改过这个数据。
正常的情况如下,是可以修改成功的
连接1:0>set money 100
"OK"
连接1:0>set out 0
"OK"
连接1:0>watch money #监视money
"OK"
连接1:0>multi
"OK"
连接1:0>decrby money 20
"QUEUED"
连接1:0>incrby out 20
"QUEUED"
连接1:0>exec
1) "80"
2) "20"
但是在执行exec之前,如果另一个线程修改了这些值,这个时候回导致执行失败。
在Redis中watch就相当于乐观锁
对于上述问题,可以先unwatch解锁再加锁watch,然后执行队列,再执行exec
Jedis
Jedis是Redis官方推荐的Java连接开发工具,是使用Java操作Redis的中间件(当然现在不用这个技术了,SpringBoot上可以直接配置)。
首先导入pom依赖
<dependencies>
<!-- 导入jedis的包-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.3.0</version>
</dependency>
<!--导入fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
</dependency>
</dependencies>
建立测试类TestPing.java测试连接
public class TestPing {
public static void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1",6379);
System.out.println(jedis.ping());
}
}
PONG
Process finished with exit code 0
连接成功!
其他的命令和直接在Redis上没什么区别,此处不述
下面主要写一个事务的测试
public class TestPing {
public static void main(String[] args) {
Jedis jedis = new Jedis("120.26.142.247",6379);
//开启事务
Transaction multi = jedis.multi();
try {
multi.set("user1","one");
multi.set("user2","two");
multi.exec();
}catch (Exception e){
multi.discard();
e.printStackTrace();
}finally {
System.out.println(jedis.get("user1"));
System.out.println(jedis.get("user2"));
jedis.close();
}
}
}
结果如下
one
two
Process finished with exit code 0