浏览代码

优化慢查询

xw 1 月之前
父节点
当前提交
8c0c7f0f4c

+ 34 - 0
fs-common/src/main/java/com/fs/common/constant/LiveKeysConstant.java

@@ -59,4 +59,38 @@ public class LiveKeysConstant {
     /** 真实订单去重(防止同一订单多次推送),%s=orderId */
     public static final String LIVE_ORDER_TIP_REAL_DEDUP = "live:order:tip:real:dedup:%s";
 
+    // ========== 直播延时任务 ZSet(定时扫描,勿用 KEYS) ==========
+    public static final String LIVE_AUTO_TASK_PREFIX = "live:auto_task:";
+    public static final String LIVE_LOTTERY_TASK_PREFIX = "live:lottery_task:";
+    public static final String LIVE_RED_TASK_PREFIX = "live:red_task:";
+
+    /** 活跃 ZSet key 索引,成员为完整 ZSet key,如 live:auto_task:123 */
+    public static final String LIVE_AUTO_TASK_INDEX = "live:auto_task:index";
+    public static final String LIVE_LOTTERY_TASK_INDEX = "live:lottery_task:index";
+    public static final String LIVE_RED_TASK_INDEX = "live:red_task:index";
+
+    public static String liveAutoTaskKey(Long liveId) {
+        return LIVE_AUTO_TASK_PREFIX + liveId;
+    }
+
+    public static String liveLotteryTaskKey(Long liveId) {
+        return LIVE_LOTTERY_TASK_PREFIX + liveId;
+    }
+
+    public static String liveRedTaskKey(Long liveId) {
+        return LIVE_RED_TASK_PREFIX + liveId;
+    }
+
+    /** 从延时任务 ZSet key 解析直播间 ID */
+    public static Long parseLiveIdFromTaskZSetKey(String zsetKey) {
+        if (zsetKey == null || zsetKey.isEmpty()) {
+            return null;
+        }
+        int idx = zsetKey.lastIndexOf(':');
+        if (idx < 0 || idx >= zsetKey.length() - 1) {
+            return null;
+        }
+        return Long.parseLong(zsetKey.substring(idx + 1));
+    }
+
 }

+ 119 - 0
fs-common/src/main/java/com/fs/common/utils/redis/LiveDelayedTaskRedisUtil.java

@@ -0,0 +1,119 @@
+package com.fs.common.utils.redis;
+
+import com.fs.common.constant.LiveKeysConstant;
+import com.fs.common.core.redis.RedisCache;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * 直播延时任务 ZSet 索引:用 Set 维护活跃 ZSet key,避免生产环境 KEYS 全库扫描。
+ */
+@Component
+public class LiveDelayedTaskRedisUtil {
+
+    @Autowired
+    private RedisCache redisCache;
+
+    public String autoTaskKey(Long liveId) {
+        return LiveKeysConstant.liveAutoTaskKey(liveId);
+    }
+
+    public String lotteryTaskKey(Long liveId) {
+        return LiveKeysConstant.liveLotteryTaskKey(liveId);
+    }
+
+    public String redTaskKey(Long liveId) {
+        return LiveKeysConstant.liveRedTaskKey(liveId);
+    }
+
+    public void trackAutoTask(Long liveId) {
+        track(LiveKeysConstant.LIVE_AUTO_TASK_INDEX, autoTaskKey(liveId));
+    }
+
+    public void trackLotteryTask(Long liveId) {
+        track(LiveKeysConstant.LIVE_LOTTERY_TASK_INDEX, lotteryTaskKey(liveId));
+    }
+
+    public void trackRedTask(Long liveId) {
+        track(LiveKeysConstant.LIVE_RED_TASK_INDEX, redTaskKey(liveId));
+    }
+
+    public void refreshAutoTaskIndex(Long liveId) {
+        refreshIndex(LiveKeysConstant.LIVE_AUTO_TASK_INDEX, autoTaskKey(liveId));
+    }
+
+    public void refreshLotteryTaskIndex(Long liveId) {
+        refreshIndex(LiveKeysConstant.LIVE_LOTTERY_TASK_INDEX, lotteryTaskKey(liveId));
+    }
+
+    public void refreshRedTaskIndex(Long liveId) {
+        refreshIndex(LiveKeysConstant.LIVE_RED_TASK_INDEX, redTaskKey(liveId));
+    }
+
+    public void untrackAutoTask(Long liveId) {
+        untrack(LiveKeysConstant.LIVE_AUTO_TASK_INDEX, autoTaskKey(liveId));
+    }
+
+    public void untrackLotteryTask(Long liveId) {
+        untrack(LiveKeysConstant.LIVE_LOTTERY_TASK_INDEX, lotteryTaskKey(liveId));
+    }
+
+    public void untrackRedTask(Long liveId) {
+        untrack(LiveKeysConstant.LIVE_RED_TASK_INDEX, redTaskKey(liveId));
+    }
+
+    public Set<String> listAutoTaskZSetKeys() {
+        return listZSetKeys(LiveKeysConstant.LIVE_AUTO_TASK_INDEX);
+    }
+
+    public Set<String> listLotteryTaskZSetKeys() {
+        return listZSetKeys(LiveKeysConstant.LIVE_LOTTERY_TASK_INDEX);
+    }
+
+    public Set<String> listRedTaskZSetKeys() {
+        return listZSetKeys(LiveKeysConstant.LIVE_RED_TASK_INDEX);
+    }
+
+    private void track(String indexKey, String zsetKey) {
+        redisCache.redisTemplate.opsForSet().add(indexKey, zsetKey);
+    }
+
+    private void untrack(String indexKey, String zsetKey) {
+        redisCache.redisTemplate.opsForSet().remove(indexKey, zsetKey);
+    }
+
+    /**
+     * ZSet 变更后调用:无成员或 key 已过期则从索引移除。
+     */
+    public void refreshIndex(String indexKey, String zsetKey) {
+        if (!Boolean.TRUE.equals(redisCache.redisTemplate.hasKey(zsetKey))) {
+            untrack(indexKey, zsetKey);
+            return;
+        }
+        Long size = redisCache.redisTemplate.opsForZSet().zCard(zsetKey);
+        if (size == null || size == 0) {
+            untrack(indexKey, zsetKey);
+        }
+    }
+
+    private Set<String> listZSetKeys(String indexKey) {
+        Set<Object> members = redisCache.redisTemplate.opsForSet().members(indexKey);
+        if (members == null || members.isEmpty()) {
+            return Collections.emptySet();
+        }
+        Set<String> result = new HashSet<>(members.size());
+        for (Object member : members) {
+            String zsetKey = member.toString();
+            if (Boolean.TRUE.equals(redisCache.redisTemplate.hasKey(zsetKey))) {
+                result.add(zsetKey);
+            } else {
+                untrack(indexKey, zsetKey);
+            }
+        }
+        return result;
+    }
+}

+ 39 - 42
fs-live-app/src/main/java/com/fs/live/task/Task.java

@@ -8,6 +8,7 @@ import com.fs.common.constant.LiveKeysConstant;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.redis.LiveDelayedTaskRedisUtil;
 import com.fs.framework.aspectj.lock.DistributeLock;
 import com.fs.erp.service.FsJstAftersalePushService;
 import com.fs.his.service.IFsUserService;
@@ -82,6 +83,8 @@ public class Task {
 
     @Autowired
     public FsJstAftersalePushService fsJstAftersalePushService;
+    @Autowired
+    private LiveDelayedTaskRedisUtil liveDelayedTaskRedisUtil;
 
     @Scheduled(cron = "0 0/1 * * * ?")
     @DistributeLock(key = "updateLiveStatusByTime", scene = "task")
@@ -155,7 +158,6 @@ public class Task {
                 liveService.updateLiveEntity(live);
             }
         }
-        String key = "live:auto_task:";
         if (!startLiveList.isEmpty()) {
             for (Live live : startLiveList) {
                 SendMsgVo sendMsgVo = new SendMsgVo();
@@ -164,12 +166,14 @@ public class Task {
                 webSocketServer.broadcastMessage(live.getLiveId(), JSONObject.toJSONString(R.ok().put("data",sendMsgVo)));
                 List<LiveAutoTask> collect = liveAutoTasks.stream().filter(liveAutoTask -> liveAutoTask.getLiveId().equals(live.getLiveId())).collect(Collectors.toList());
                 if (!collect.isEmpty()) {
+                    String autoTaskKey = liveDelayedTaskRedisUtil.autoTaskKey(live.getLiveId());
                     collect.forEach(liveAutoTask -> {
                         liveAutoTask.setCreateTime(null);
                         liveAutoTask.setUpdateTime(null);
-                        redisCache.zSetAdd(key + live.getLiveId(), JSON.toJSONString(liveAutoTask),liveAutoTask.getAbsValue().getTime());
-                        redisCache.expire(key+live.getLiveId(), 1, TimeUnit.DAYS);
+                        redisCache.zSetAdd(autoTaskKey, JSON.toJSONString(liveAutoTask), liveAutoTask.getAbsValue().getTime());
+                        redisCache.expire(autoTaskKey, 1, TimeUnit.DAYS);
                     });
+                    liveDelayedTaskRedisUtil.trackAutoTask(live.getLiveId());
                 }
                 // 清理小程序缓存 和 直播标签缓存
                 String cacheKey = String.format(LiveKeysConstant.LIVE_DATA_CACHE, live.getLiveId());
@@ -214,12 +218,9 @@ public class Task {
                 webSocketServer.broadcastMessage(live.getLiveId(), JSONObject.toJSONString(R.ok().put("data",sendMsgVo)));
                 List<LiveAutoTask> collect = liveAutoTasks.stream().filter(liveAutoTask -> liveAutoTask.getLiveId().equals(live.getLiveId())).collect(Collectors.toList());
                 if (!collect.isEmpty()) {
-                    redisCache.deleteObject(key + live.getLiveId());
-                    collect.forEach(liveAutoTask -> {
-                        liveAutoTask.setCreateTime(null);
-                        liveAutoTask.setUpdateTime(null);
-                        redisCache.redisTemplate.opsForZSet().remove(key + live.getLiveId(), JSON.toJSONString(liveAutoTask),liveAutoTask.getAbsValue().getTime());
-                    });
+                    String autoTaskKey = liveDelayedTaskRedisUtil.autoTaskKey(live.getLiveId());
+                    redisCache.deleteObject(autoTaskKey);
+                    liveDelayedTaskRedisUtil.untrackAutoTask(live.getLiveId());
                 }
                 String cacheKey = String.format(LiveKeysConstant.LIVE_DATA_CACHE, live.getLiveId());
                 redisCache.deleteObject(cacheKey);
@@ -243,42 +244,41 @@ public class Task {
     @DistributeLock(key = "liveLotteryTask", scene = "task")
     public void liveLotteryTask() {
         long currentTime = Instant.now().toEpochMilli(); // 当前时间戳(毫秒)
-        String lotteryKey = "live:lottery_task:*";
-        Set<String> allLiveKeys = redisCache.redisTemplate.keys(lotteryKey);
-        if (allLiveKeys != null && !allLiveKeys.isEmpty()) {
-            for (String liveKey : allLiveKeys) {
-                Set<String> range = redisCache.redisTemplate.opsForZSet().rangeByScore(liveKey, 0, currentTime);
-                if (range == null || range.isEmpty()) {
-                    continue;
-                }
-                processLotteryTask(range);
-                redisCache.redisTemplate.opsForZSet()
-                        .removeRangeByScore(liveKey, 0, currentTime);
+        Set<String> lotteryZSetKeys = liveDelayedTaskRedisUtil.listLotteryTaskZSetKeys();
+        for (String liveKey : lotteryZSetKeys) {
+            Set<String> range = redisCache.redisTemplate.opsForZSet().rangeByScore(liveKey, 0, currentTime);
+            if (range == null || range.isEmpty()) {
+                continue;
+            }
+            processLotteryTask(range);
+            redisCache.redisTemplate.opsForZSet().removeRangeByScore(liveKey, 0, currentTime);
+            Long liveId = LiveKeysConstant.parseLiveIdFromTaskZSetKey(liveKey);
+            if (liveId != null) {
+                liveDelayedTaskRedisUtil.refreshLotteryTaskIndex(liveId);
             }
         }
 
-        String redKey = "live:red_task:*";
-        allLiveKeys = redisCache.redisTemplate.keys(redKey);
-        if (allLiveKeys == null || allLiveKeys.isEmpty()) {
-            return;
-        }
-        for (String liveKey : allLiveKeys) {
+        Set<String> redZSetKeys = liveDelayedTaskRedisUtil.listRedTaskZSetKeys();
+        for (String liveKey : redZSetKeys) {
             Set<String> range = redisCache.redisTemplate.opsForZSet().rangeByScore(liveKey, 0, currentTime);
             if (range == null || range.isEmpty()) {
                 continue;
             }
 
             updateRedStatus(range);
-            redisCache.redisTemplate.opsForZSet()
-                    .removeRangeByScore(liveKey, 0, currentTime);
+            redisCache.redisTemplate.opsForZSet().removeRangeByScore(liveKey, 0, currentTime);
+            Long liveId = LiveKeysConstant.parseLiveIdFromTaskZSetKey(liveKey);
+            if (liveId != null) {
+                liveDelayedTaskRedisUtil.refreshRedTaskIndex(liveId);
+            }
             try {
                 // 广播红包关闭消息
                 SendMsgVo sendMsgVo = new SendMsgVo();
-                sendMsgVo.setLiveId(Long.valueOf(liveKey));
+                sendMsgVo.setLiveId(liveId);
                 sendMsgVo.setCmd("red");
                 sendMsgVo.setStatus(-1);
-                liveService.asyncToCacheLiveConfig(Long.parseLong(liveKey));
-                webSocketServer.broadcastMessage(Long.valueOf(liveKey), JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
+                liveService.asyncToCacheLiveConfig(liveId);
+                webSocketServer.broadcastMessage(liveId, JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
             } catch (Exception e) {
                 log.error("更新红包状态异常", e);
             }
@@ -374,21 +374,18 @@ public class Task {
     public void liveAutoTask() {
         long currentTime = Instant.now().toEpochMilli(); // 当前时间戳(毫秒)
 
-        Set<String> allLiveKeys = redisCache.redisTemplate.keys("live:auto_task:*");
-        if (allLiveKeys == null || allLiveKeys.isEmpty()) {
-            return; // 没有数据,直接返回
-        }
-        // 2. 遍历每个直播间的ZSet键
-        for (String liveKey : allLiveKeys) {
-            // 3. 获取当前直播间ZSet中所有元素(按score排序)
-            // range方法:0表示第一个元素,-1表示最后一个元素,即获取全部
+        Set<String> autoZSetKeys = liveDelayedTaskRedisUtil.listAutoTaskZSetKeys();
+        for (String liveKey : autoZSetKeys) {
             Set<String> range = redisCache.redisTemplate.opsForZSet().rangeByScore(liveKey, 0, currentTime);
             if (range == null || range.isEmpty()) {
-                continue; // 没有数据,直接返回
+                continue;
             }
-            redisCache.redisTemplate.opsForZSet()
-                    .removeRangeByScore(liveKey, 0, currentTime);
+            redisCache.redisTemplate.opsForZSet().removeRangeByScore(liveKey, 0, currentTime);
             processAutoTask(range);
+            Long liveId = LiveKeysConstant.parseLiveIdFromTaskZSetKey(liveKey);
+            if (liveId != null) {
+                liveDelayedTaskRedisUtil.refreshAutoTaskIndex(liveId);
+            }
         }
     }
 

+ 4 - 2
fs-live-app/src/main/java/com/fs/live/websocket/service/WebSocketServer.java

@@ -28,6 +28,7 @@ import com.fs.live.websocket.bean.SendMsgVo;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.redis.LiveDelayedTaskRedisUtil;
 import com.fs.common.utils.spring.SpringUtils;
 import com.fs.live.domain.*;
 import com.fs.live.param.RedPO;
@@ -1332,8 +1333,9 @@ public class WebSocketServer {
 
     }
     private void delAutoTask(long liveId, Long data) {
-        String key = "live:auto_task:";
-        redisCache.redisTemplate.opsForZSet().removeRangeByScore(key + liveId, data, data);
+        String cacheKey = LiveKeysConstant.liveAutoTaskKey(liveId);
+        redisCache.redisTemplate.opsForZSet().removeRangeByScore(cacheKey, data, data);
+        SpringUtils.getBean(LiveDelayedTaskRedisUtil.class).refreshAutoTaskIndex(liveId);
     }
 
     /**

+ 21 - 8
fs-service/src/main/java/com/fs/live/service/impl/LiveAutoTaskServiceImpl.java

@@ -8,9 +8,11 @@ import java.util.*;
 import cn.hutool.core.util.ObjectUtil;
 import com.alibaba.fastjson.JSON;
 
+import com.fs.common.constant.LiveKeysConstant;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.DateUtils;
+import com.fs.common.utils.redis.LiveDelayedTaskRedisUtil;
 import com.fs.live.domain.*;
 import com.fs.live.mapper.*;
 import com.fs.live.param.LiveLotteryProduct;
@@ -41,6 +43,8 @@ public class LiveAutoTaskServiceImpl implements ILiveAutoTaskService {
     @Autowired
     private RedisCache redisCache;
     @Autowired
+    private LiveDelayedTaskRedisUtil liveDelayedTaskRedisUtil;
+    @Autowired
     private LiveAutoTaskMapper baseMapper;
 
     @Autowired
@@ -186,8 +190,10 @@ public class LiveAutoTaskServiceImpl implements ILiveAutoTaskService {
         if (live.getStatus() == 2 && liveAutoTask.getStatus() == 1L) {
             liveAutoTask.setUpdateTime(null);
             liveAutoTask.setCreateTime(null);
-            redisCache.redisTemplate.opsForZSet().add("live:auto_task:" + live.getLiveId(), JSON.toJSONString(liveAutoTask),liveAutoTask.getAbsValue().getTime());
-            redisCache.redisTemplate.expire("live:auto_task:" + live.getLiveId(), 30, java.util.concurrent.TimeUnit.MINUTES);
+            String cacheKey = LiveKeysConstant.liveAutoTaskKey(live.getLiveId());
+            redisCache.redisTemplate.opsForZSet().add(cacheKey, JSON.toJSONString(liveAutoTask), liveAutoTask.getAbsValue().getTime());
+            redisCache.redisTemplate.expire(cacheKey, 30, java.util.concurrent.TimeUnit.MINUTES);
+            liveDelayedTaskRedisUtil.trackAutoTask(live.getLiveId());
         }
 
 
@@ -223,8 +229,10 @@ public class LiveAutoTaskServiceImpl implements ILiveAutoTaskService {
         if (live.getStatus() == 2 && liveAutoTask.getStatus() == 1L) {
             liveAutoTask.setUpdateTime(null);
             liveAutoTask.setCreateTime(null);
-            redisCache.redisTemplate.opsForZSet().add("live:auto_task:" + live.getLiveId(), JSON.toJSONString(liveAutoTask),liveAutoTask.getAbsValue().getTime());
-            redisCache.redisTemplate.expire("live:auto_task:" + live.getLiveId(), 30, java.util.concurrent.TimeUnit.MINUTES);
+            String cacheKey = LiveKeysConstant.liveAutoTaskKey(live.getLiveId());
+            redisCache.redisTemplate.opsForZSet().add(cacheKey, JSON.toJSONString(liveAutoTask), liveAutoTask.getAbsValue().getTime());
+            redisCache.redisTemplate.expire(cacheKey, 30, java.util.concurrent.TimeUnit.MINUTES);
+            liveDelayedTaskRedisUtil.trackAutoTask(live.getLiveId());
         }
         return R.ok();
     }
@@ -252,7 +260,9 @@ public class LiveAutoTaskServiceImpl implements ILiveAutoTaskService {
     public R updateLiveAutoTask(LiveAutoTask liveAutoTask)
     {
         LiveAutoTask existTask = baseMapper.selectLiveAutoTaskById(liveAutoTask.getId());
-        redisCache.redisTemplate.opsForZSet().removeRangeByScore("live:auto_task:" + existTask.getLiveId(), existTask.getAbsValue().getTime(), existTask.getAbsValue().getTime());
+        String autoTaskCacheKey = LiveKeysConstant.liveAutoTaskKey(existTask.getLiveId());
+        redisCache.redisTemplate.opsForZSet().removeRangeByScore(autoTaskCacheKey, existTask.getAbsValue().getTime(), existTask.getAbsValue().getTime());
+        liveDelayedTaskRedisUtil.refreshAutoTaskIndex(existTask.getLiveId());
         if (liveAutoTask.getTaskType() == 1L) {
             // 商品
             LiveGoodsVo liveGoodsVo = goodsService.selectLiveGoodsVoByGoodsId(Long.valueOf(liveAutoTask.getContent()));
@@ -594,7 +604,7 @@ public class LiveAutoTaskServiceImpl implements ILiveAutoTaskService {
     private void removeTaskFromCache(LiveAutoTask task) {
         try {
             if (task.getLiveId() != null && task.getId() != null) {
-                String cacheKey = "live:auto_task:" + task.getLiveId();
+                String cacheKey = LiveKeysConstant.liveAutoTaskKey(task.getLiveId());
                 // 获取ZSet中的所有任务
                 Set<String> allTasks = redisCache.redisTemplate.opsForZSet().range(cacheKey, 0, -1);
                 if (allTasks != null && !allTasks.isEmpty()) {
@@ -612,6 +622,7 @@ public class LiveAutoTaskServiceImpl implements ILiveAutoTaskService {
                             log.warn("解析缓存中的任务失败,taskJson: {}, error: {}", taskJson, e.getMessage());
                         }
                     }
+                    liveDelayedTaskRedisUtil.refreshAutoTaskIndex(task.getLiveId());
                 }
             }
         } catch (Exception e) {
@@ -626,9 +637,10 @@ public class LiveAutoTaskServiceImpl implements ILiveAutoTaskService {
      */
     public void deleteLotteryCache(Long lotteryId, Long liveId) {
         try {
-            String cacheKey = "live:lottery_task:" + liveId;
+            String cacheKey = LiveKeysConstant.liveLotteryTaskKey(liveId);
             // 从ZSet中删除指定的抽奖ID
             redisCache.redisTemplate.opsForZSet().remove(cacheKey, String.valueOf(lotteryId));
+            liveDelayedTaskRedisUtil.refreshLotteryTaskIndex(liveId);
             log.info("删除抽奖缓存,lotteryId: {}, liveId: {}", lotteryId, liveId);
         } catch (Exception e) {
             log.error("删除抽奖缓存失败,lotteryId: {}, liveId: {}", lotteryId, liveId, e);
@@ -642,9 +654,10 @@ public class LiveAutoTaskServiceImpl implements ILiveAutoTaskService {
      */
     public void deleteRedCache(Long redId, Long liveId) {
         try {
-            String cacheKey = "live:red_task:" + liveId;
+            String cacheKey = LiveKeysConstant.liveRedTaskKey(liveId);
             // 从ZSet中删除指定的红包ID
             redisCache.redisTemplate.opsForZSet().remove(cacheKey, String.valueOf(redId));
+            liveDelayedTaskRedisUtil.refreshRedTaskIndex(liveId);
             // 同时删除剩余红包数的缓存
             redisCache.deleteObject("live:red:remainingLots:" + redId);
             log.info("删除红包缓存,redId: {}, liveId: {}", redId, liveId);

+ 12 - 8
fs-service/src/main/java/com/fs/live/service/impl/LiveLotteryConfServiceImpl.java

@@ -6,6 +6,7 @@ import com.fs.common.constant.LiveKeysConstant;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.DateUtils;
+import com.fs.common.utils.redis.LiveDelayedTaskRedisUtil;
 import com.fs.live.domain.Live;
 import com.fs.live.domain.LiveAutoTask;
 import com.fs.live.domain.LiveLotteryConf;
@@ -54,6 +55,8 @@ public class LiveLotteryConfServiceImpl implements ILiveLotteryConfService {
     @Autowired
     private RedisCache redisCache;
     @Autowired
+    private LiveDelayedTaskRedisUtil liveDelayedTaskRedisUtil;
+    @Autowired
     private LiveLotteryRegistrationMapper lotteryRegistrationMapper;
     @Autowired
     private LiveUserLotteryRecordMapper liveUserLotteryRecordMapper;
@@ -111,7 +114,8 @@ public class LiveLotteryConfServiceImpl implements ILiveLotteryConfService {
     @Override
     public R updateLiveLotteryConf(LiveLotteryConf liveLotteryConf)
     {
-        String cacheKey = "live:lottery_task:" + liveLotteryConf.getLiveId();
+        Long liveId = liveLotteryConf.getLiveId();
+        String cacheKey = LiveKeysConstant.liveLotteryTaskKey(liveId);
         liveLotteryConf.setUpdateTime(DateUtils.getNowDate());
         if ("1".equals(liveLotteryConf.getLotteryStatus())) {
             List<LiveLotteryProduct> prizes = productMapper.selectLiveLotteryProductConfByLotteryId(liveLotteryConf.getLotteryId());
@@ -122,8 +126,10 @@ public class LiveLotteryConfServiceImpl implements ILiveLotteryConfService {
             double score = localDateTime.atZone(java.time.ZoneId.systemDefault()).toInstant().toEpochMilli();
             redisCache.redisTemplate.opsForZSet().add(cacheKey, String.valueOf(liveLotteryConf.getLotteryId()), score);
             redisCache.redisTemplate.expire(cacheKey, 30, java.util.concurrent.TimeUnit.MINUTES);
+            liveDelayedTaskRedisUtil.trackLotteryTask(liveId);
         } else {
             redisCache.deleteObject(cacheKey);
+            liveDelayedTaskRedisUtil.untrackLotteryTask(liveId);
         }
         return R.ok().put("data",baseMapper.updateLiveLotteryConf(liveLotteryConf));
     }
@@ -701,21 +707,19 @@ public class LiveLotteryConfServiceImpl implements ILiveLotteryConfService {
             liveAutoTaskService.directInsertLiveAutoTask(settlementTask);
 
             if (live.getStatus() == 2) {
+                String autoTaskKey = LiveKeysConstant.liveAutoTaskKey(live.getLiveId());
                 redisCache.redisTemplate.opsForZSet().add(
-                    "live:auto_task:" + live.getLiveId(),
+                    autoTaskKey,
                     JSON.toJSONString(startTask),
                     startTime.getTime()
                 );
                 redisCache.redisTemplate.opsForZSet().add(
-                    "live:auto_task:" + live.getLiveId(),
+                    autoTaskKey,
                     JSON.toJSONString(settlementTask),
                     settlementTime.getTime()
                 );
-                redisCache.redisTemplate.expire(
-                    "live:auto_task:" + live.getLiveId(),
-                    30,
-                    TimeUnit.MINUTES
-                );
+                redisCache.redisTemplate.expire(autoTaskKey, 30, TimeUnit.MINUTES);
+                liveDelayedTaskRedisUtil.trackAutoTask(live.getLiveId());
             }
 
             return R.ok("自动化抽奖设置成功");

+ 12 - 8
fs-service/src/main/java/com/fs/live/service/impl/LiveRedConfServiceImpl.java

@@ -8,6 +8,7 @@ import com.fs.common.constant.LiveKeysConstant;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.DateUtils;
+import com.fs.common.utils.redis.LiveDelayedTaskRedisUtil;
 import com.fs.his.domain.FsUserIntegralLogs;
 import com.fs.his.enums.FsUserIntegralLogTypeEnum;
 import com.fs.his.mapper.FsUserIntegralLogsMapper;
@@ -53,6 +54,8 @@ public class LiveRedConfServiceImpl implements ILiveRedConfService {
 
     @Autowired
     private RedisCache redisCache;
+    @Autowired
+    private LiveDelayedTaskRedisUtil liveDelayedTaskRedisUtil;
 
     @Autowired
     private FsUserMapper userService;
@@ -122,7 +125,8 @@ public class LiveRedConfServiceImpl implements ILiveRedConfService {
     @Override
     public int updateLiveRedConf(LiveRedConf liveRedConf)
     {
-        String cacheKey = "live:red_task:" + liveRedConf.getLiveId();
+        Long liveId = liveRedConf.getLiveId();
+        String cacheKey = LiveKeysConstant.liveRedTaskKey(liveId);
         liveRedConf.setUpdateTime(DateUtils.getNowDate());
         // 开始
         if (liveRedConf.getRedStatus() == 1) {
@@ -131,6 +135,7 @@ public class LiveRedConfServiceImpl implements ILiveRedConfService {
             double score = localDateTime.atZone(java.time.ZoneId.systemDefault()).toInstant().toEpochMilli();
             redisCache.redisTemplate.opsForZSet().add(cacheKey, String.valueOf(liveRedConf.getRedId()), score);
             redisCache.redisTemplate.expire(cacheKey, 30, TimeUnit.MINUTES);
+            liveDelayedTaskRedisUtil.trackRedTask(liveId);
 
             // 将红包配置缓存到 Redis(用于高并发查询)
             String redConfCacheKey = REDPACKET_CONF_CACHE_KEY + liveRedConf.getRedId();
@@ -140,6 +145,7 @@ public class LiveRedConfServiceImpl implements ILiveRedConfService {
             // 其他
             redisCache.deleteObject(REDPACKET_REMAININGLOTS_KEY + liveRedConf.getRedId());
             redisCache.deleteObject(cacheKey);
+            liveDelayedTaskRedisUtil.untrackRedTask(liveId);
             // 删除红包配置缓存
             redisCache.deleteObject(REDPACKET_CONF_CACHE_KEY + liveRedConf.getRedId());
             redStatusUpdate(CollUtil.newHashSet(liveRedConf.getRedId()));
@@ -822,21 +828,19 @@ public class LiveRedConfServiceImpl implements ILiveRedConfService {
 
             // 如果直播中,加入Redis
             if (live.getStatus() == 2) {
+                String autoTaskKey = LiveKeysConstant.liveAutoTaskKey(live.getLiveId());
                 redisCache.redisTemplate.opsForZSet().add(
-                    "live:auto_task:" + live.getLiveId(),
+                    autoTaskKey,
                     com.alibaba.fastjson.JSON.toJSONString(startTask),
                     startTime.getTime()
                 );
                 redisCache.redisTemplate.opsForZSet().add(
-                    "live:auto_task:" + live.getLiveId(),
+                    autoTaskKey,
                     com.alibaba.fastjson.JSON.toJSONString(settlementTask),
                     settlementTime.getTime()
                 );
-                redisCache.redisTemplate.expire(
-                    "live:auto_task:" + live.getLiveId(),
-                    30,
-                    TimeUnit.MINUTES
-                );
+                redisCache.redisTemplate.expire(autoTaskKey, 30, TimeUnit.MINUTES);
+                liveDelayedTaskRedisUtil.trackAutoTask(live.getLiveId());
             }
 
             return R.ok("自动化红包设置成功");

+ 9 - 2
fs-service/src/main/java/com/fs/live/service/impl/LiveServiceImpl.java

@@ -33,6 +33,7 @@ import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.bean.BeanUtils;
+import com.fs.common.utils.redis.LiveDelayedTaskRedisUtil;
 import com.fs.live.domain.*;
 import com.fs.live.mapper.*;
 import com.fs.live.param.LiveReplayParam;
@@ -116,6 +117,8 @@ public class LiveServiceImpl implements ILiveService
     @Autowired
     private RedisCache redisCache;
     @Autowired
+    private LiveDelayedTaskRedisUtil liveDelayedTaskRedisUtil;
+    @Autowired
     private LiveMapper baseMapper;
     @Autowired
     private LiveMiniprogramSubNotifyTaskMapper liveMiniprogramSubNotifyTaskMapper;
@@ -997,12 +1000,16 @@ public class LiveServiceImpl implements ILiveService
         // 清除缓存
         clearLiveCache(live.getLiveId());
         List<LiveAutoTask> liveAutoTasks = liveAutoTaskService.selectNoActivedByLiveId(exist.getLiveId(), new Date());
+        String autoTaskKey = LiveKeysConstant.liveAutoTaskKey(live.getLiveId());
         liveAutoTasks.forEach(liveAutoTask -> {
             liveAutoTask.setCreateTime(null);
             liveAutoTask.setUpdateTime(null);
-            redisCache.redisTemplate.opsForZSet().add("live:auto_task:" + live.getLiveId(), JSON.toJSONString(liveAutoTask),liveAutoTask.getAbsValue().getTime());
-            redisCache.redisTemplate.expire("live:auto_task:"+live.getLiveId(), 1, TimeUnit.DAYS);
+            redisCache.redisTemplate.opsForZSet().add(autoTaskKey, JSON.toJSONString(liveAutoTask), liveAutoTask.getAbsValue().getTime());
+            redisCache.redisTemplate.expire(autoTaskKey, 1, TimeUnit.DAYS);
         });
+        if (!liveAutoTasks.isEmpty()) {
+            liveDelayedTaskRedisUtil.trackAutoTask(live.getLiveId());
+        }
         String cacheKey = String.format(LiveKeysConstant.LIVE_DATA_CACHE, live.getLiveId());
         redisCache.deleteObject(cacheKey);
         String cacheKey2 = String.format(LiveKeysConstant.LIVE_FLAG_CACHE, live.getLiveId());