今天带着学生学习了Redis的事务功能,Redis的事务与传统的关系型数据库(如MySQL)有所不同,Redis的事务不能回滚。
Redis中使用multi、exec、discard、watch、unwatch等命令来操作事务。
例如:
> MULTI
OK
> INCR aaa
QUEUED
> INCR bbb
QUEUED
> EXEC
1) (integer) 1
2) (integer) 1
multi命令表示启动事务(begin transaction),exec表示执行事务(commit),如果中间的操作有错误,有两种情形:
1.语法错误,表现为在EXEC执行前,命令不能正常添加到Queue中。例如,命令出现语法错误等。
2. 代码在执行的时候错误,表现为在EXEC执行后,命令不能正确执行。例如,将string当做list操作。此种操作比较奇怪,在redis事务中,并不会因为中间失败了导致整个不能执行,而是将正确的部分执行了。有种观点认为 Redis 处理事务的做法会产生 bug ,然而需要注意的是,在通常情况下,回滚并不能解决编程错误带来的问题,并且这类错误通常不会在生产环境中出现,所以 Redis 选择了更简单、更快速的无回滚方式来处理事务。
那么这种事务如何去实现秒杀功能呢。答案是使用watch,watch能监控某一个key的变化,在事务执行时,如果其他的client改变了这个可以所对应的值,将会导致当前client的事务不执行,即类似于乐观锁机制。
示例代码如下:
首先没有watch的代码:
示例为有10张优惠券,有多人来抢,需要提供秒杀功能。
首先在redis中设置一个key为num值为0:
命令为:set num 0
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++){
Jedis jedis = RedisConnection.getJedis();
String num = jedis.get("num");
int n = Integer.parseInt(num);
if (n < 10){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
jedis.incr("num");
String name = UUID.randomUUID().toString().replaceAll("-", "");
System.out.println(name + "抢到一张优惠券");
}
}
}
}
public class TestMain {
public static void main(String[] args) {
for (int i = 0; i < 5; i++){
MyThread thread = new MyThread();
thread.start();
}
}
}
当执行这种代码时,会发现会有超过10个人抢到优惠券。
加入watch时,代码如下,修改Mythred类:
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++){
Jedis jedis = RedisConnection.getJedis();
String watch = jedis.watch("num");
String num = jedis.get("num");
int n = Integer.parseInt(num);
if (n < 10){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
Transaction transaction = jedis.multi();
transaction.incr("num");
List<Object> list = transaction.exec();
String name = UUID.randomUUID().toString().replaceAll("-", "");
if (list == null || list.size() == 0){
System.out.println(watch + "==="+list+"----" + name + "手慢了,抢票失败");
}else {
System.out.println(watch + "==="+list+"----" + name + "抢到一张优惠券");
}
}
}
}
}
测试结果,你会发现,虽然一样会有多人去抢,但是始终只有10个人能正取的抢到。