用户及用户包裹的数据结构如下:
市场的数据结构如下:
商品上架
为了将商品放到市场上进行销售,程序除了要使用MULTI命令和EXEC命令之外,还需要配合使用WATCH命令,有时候甚至还会用到UNWATCH或DISCARD命令。
在用户使用WATCH命令对键进行监视之后,直到用户执行EXEC命令的这段时间里面,如果有其他客户端抢先对任何被监视的键进行了替换、更新或删除等操作,那么当用户尝试执行EXEC命令的时候,事务将失败并返回一个错误(之后用户可以选择重试事务或者放弃事务)。
通过用WATCH,MULTI/EXEC UNWATCH/DISCARD等命令,程序可以在执行某些重要操作的时候,通过确保自己正在使用的数据没有发生变化来避免数据出错。
理解Discard
UNWATCH命令可以在WATCH命令执行之后、MULTI命令执行之前对连接进行重置(reset);同样地,DISCARD命令也可以在MULTI命令执行之后、EXEC命令执行之前对连接进行重置。
这也就是说,用户在使用WATCH监视一个或多个键,接着使用MULTI开始一个新的事务,并将多个命令入队到事务队列之后,仍然可以通过发送DISCARD命令来取消WATCH命令并清空所有已入队命令。
小总结:
-
WATCH、MULTI、EXEC的使用流程是:先试用watch监视一两个键,然后使用multi开启管道,然后输入执行命令,然后调用exec进行执行。
-
如果调用了watch之后,在调用multi之前,可以使用unwatch取消监控,如果在multi之后,则可以调用discard进行取消。
我先简单的这么理解。
购买商品
-
程序首先使用WATCH对市场以及买家的个人信息进行监视,然后获取买家拥有的钱数以及商品的售价,并检查买家是否有足够的钱来购买该商品。
-
如果买家没有足够的钱,那么程序会取消事务;相反地,如果买家的钱足够,那么程序首先会将买家支付的钱转移给卖家,然后将售出的商品移动至买家的包裹,并将该商品从市场中移除。
-
当买家的个人信息或者商品买卖市场出现变化而导致WatchError异常出现时,程序将进行重试,其中最大重试时间为10秒。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
stringRedisTemplate.execute((RedisCallback<Object>) connection -> {
String buyer = String.format("users:%s", buyerId);
String seller = String.format("users:%s", sellerId);
String item = String.format("%s.%s", itemId, sellerId);
String inventory = String.format("inventory:%s", buyerId);
LocalDateTime end = LocalDateTime.now().plusSeconds(10);
// 这个地方肯定写法有问题,但是不纠结
while (LocalDateTime.now().isBefore(end)) {
try {
// 对商品买卖市场以及买家的个人信息进行监视
connection.watch(String.format("market:%s", buyer).getBytes(StandardCharsets.UTF_8));
// 检查买家想购买的商品的价格是否出现了变化,以及买家是否有
// 足够的钱来购买这件商品
Double price = connection.zScore(
"market:".getBytes(StandardCharsets.UTF_8),
item.getBytes(StandardCharsets.UTF_8));
Double founds = Double.valueOf(Arrays.toString(connection.hGet(
buyer.getBytes(StandardCharsets.UTF_8),
"funds".getBytes(StandardCharsets.UTF_8))));
if (price != lprice || price > founds) {
connection.unwatch();
return null;
}
// 先将买家支付的钱转义给卖家,然后将被购买的商品移交给买家
connection.multi();
connection.hIncrBy(
seller.getBytes(StandardCharsets.UTF_8),
"founds".getBytes(StandardCharsets.UTF_8),
price);
connection.hIncrBy(
buyer.getBytes(StandardCharsets.UTF_8),
"founds".getBytes(StandardCharsets.UTF_8),
price);
connection.sAdd(
inventory.getBytes(StandardCharsets.UTF_8),
itemId.getBytes(StandardCharsets.UTF_8));
connection.zRem(
"market:".getBytes(StandardCharsets.UTF_8),
item.getBytes(StandardCharsets.UTF_8));
connection.exec();
return true;
} catch (Exception e) {
// 如果买家的个人信息或者商品买卖市场在交易的过程中出现了变化,
// 那么进行重试
e.printStackTrace();
}
}
return false;
});
|