我们知道了Redis的事务,可以在秒杀中避免出现超售现象,如果用在司机抢单上面,也能避免一个订单被多个司机抢单成功。这节课咱们就把Redis事务和Java程序结合在一起,我们完成司机抢单的后端代码。
由于Redis使用内存缓存数据,如果Redis宕机,重启Redis之后,原本内存中缓存的数据就全都消失了。为了在宕机之后能有效恢复之前缓存的数据,我们可以开启Redis的持久化功能。
Redis有RDB和AOF两种持久化方式。RDB会根据指定的规则定时将内存中的数据保存到硬盘中,容易因为持久化不及时,导致恢复的时候丢失一部分缓存数据。AOF会将每次执行的命令及时保存到硬盘中,实时性更好,丢失的数据更少,所以本课程中我们选择AOF模式。
修改Redis配置文件,把原本的save注释掉,然后添加上新的save设置。在结尾还要追加两句话,然后重新启动Redis容器。
bind 0.0.0.0
protected-mode yes
port 6379
tcp-backlog 511
timeout 0
tcp-keepalive 0
loglevel notice
logfile ""
databases 12
save ""
#save 900 1
#save 300 10
#save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir ./
requirepass abc123456
appendonly yes
appendfilename "appendonly.aof"
appendfsync always
在com.example.hxds.odr.service.impl
包OrderServiceImpl.java
类中,实现抽象方法。
@Service
public class OrderServiceImpl implements OrderService {
@Resource
private RedisTemplate redisTemplate;
……
@Override
@Transactional
@LcnTransaction
public String acceptNewOrder(long driverId, long orderId) {
//Redis不存在抢单的新订单就代表抢单失败
if (!redisTemplate.hasKey("order#" + orderId)) {
return "抢单失败";
}
//执行Redis事务
redisTemplate.execute(new SessionCallback() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
//获取新订单记录的Version
operations.watch("order#" + orderId);
//本地缓存Redis操作
operations.multi();
//把新订单缓存的Value设置成抢单司机的ID
operations.opsForValue().set("order#" + orderId, driverId);
//执行Redis事务,如果事务提交失败会自动抛出异常
return operations.exec();
}
});
//抢单成功之后,删除Redis中的新订单,避免让其他司机参与抢单
redisTemplate.delete("order#" + orderId);
//更新订单记录,添加上接单司机ID和接单时间
HashMap param = new HashMap() {{
put("driverId", driverId);
put("orderId", orderId);
}};
int rows = orderDao.acceptNewOrder(param);
if (rows != 1) {
throw new HxdsException("接单失败,无法更新订单记录");
}
return "接单成功";
}
@Override
public Integer searchOrderStatus(Map param) {
Integer status = orderDao.searchOrderStatus(param);
if (status == null) {
throw new HxdsException("没有查询到数据,请核对查询条件");
}
return status;
}
@Override
@Transactional
@LcnTransaction
public String deleteUnAcceptOrder(Map param) {
long orderId = MapUtil.getLong(param, "orderId");
if (!redisTemplate.hasKey("order#" + orderId)) {
return "订单取消失败";
}
redisTemplate.execute(new SessionCallback() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
operations.watch("order#" + orderId);
operations.multi();
operations.opsForValue().set("order#" + orderId, "none");
return operations.exec();
}
});
redisTemplate.delete("order#" + orderId);
int rows = orderDao.deleteUnAcceptOrder(param);
if (rows != 1) {
return "订单取消失败";
}
return "订单取消成功";
}
}
个人总结:先执行Redis事务,避免超售现象。再执行Redis缓存删除 和 SQL操作。