有几个场景业务的处理:
一:有一个很大的商品订单表,每天新增数十万条数据。每条数据有个到期时间,需要在到期时间后做一些处理,譬如关闭订单,改变状态之类的。
二:有个付款功能,有到期时间,时间到了需要关闭,或者通知用户等等。
三:抢购时,时间到了,用户不处理不付款的,要把商品回到库存里之类的。 大概类似的一些有到期时间功能的业务场景,但是要么是有较强的实时性,譬如希望到期后立马就改变状态或者做出一些通知之类的,要么是数据量巨大的。 那么可能首先想到的思路就是开个定时任务,隔一段时间去扫一下表,看看到期时间,然后做处理。 很明显,扫表是个很大的工作量,耗时耗资源,甚至会产生死锁什么的。而且大部分时候的扫描是无用的,产生了很多无用的查询。
那么这种问题解决思路: 在添加数据时,将ID和过期时间放到redis里,用那个能排序的结构sortSet,或者类似的能记录时间的中间件,做好排序。然后起个后台任务或者新起个项目,专门是扫描这个redis的第一条数据,也就是最快要过期的,这样只需要查询一条就行了,只要第一条不过期,那后面的就不用看了,也就不需要去操作数据库。倘若第一条过期了,就做相应的处理,然后移除掉,再去扫第二条,依次类推。这样查询就很少,也不需要查表。所以可以把扫描间隔设的很短,来达到强实时性。
这个就是解决的思路
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@Component
public class ExpirationTaskProcessor {
@Resource
private RedisTemplate<String, Object> redisTemplate;
// 添加任务到Redis
public void addTask(String taskId, long expireTime) {
redisTemplate.opsForZSet().add("expiration_tasks", taskId, expireTime);
}
@Scheduled(fixedDelay = 1000)
public void processExpirationTasks() {
long now = System.currentTimeMillis();
while (true) {
// 获取第一条即将过期的任务
Set<Object> tasks = redisTemplate.opsForZSet().rangeByScore("expiration_tasks", 0, now, 0, 1);
// 如果没有过期任务,退出循环
if (tasks == null || tasks.isEmpty()) {
break;
}
Object taskId = tasks.iterator().next();
// 执行过期处理逻辑
processExpiredTask(taskId);
// 从Redis中移除已处理的任务
redisTemplate.opsForZSet().remove("expiration_tasks", taskId);
}
}
private void processExpiredTask(Object taskId) {
// 在这里实现过期任务的处理逻辑
System.out.println("Processing expired task: " + taskId);
// 例如:关闭订单、通知用户等
}
}