需求:
分页求出进三天的发布视频的权重
热度 = 权重 / 衰减时间
衰减时间 = 当前时间 - 视频发布时间 小根堆来实现
这个公式可以很好的利用半衰期来进行解决
难点:
如果一次性加载太多到springBoot服务器里面会造成堆内存占用过多,
分页又有可能造成深分页问题,因此选择使用主键(雪花id)作为游标的快速分页算法
流程:
1:找出当前时间-三天的最大视频id
2:利用视频id作为游标每次选择1000个视频计算热度
3:插入到小根堆当中去
@Scheduled(cron = "30 * * * * ?") // 每分钟的第30秒执行public void findTopK() { //更新增量表//应该优化一下,选择三天之内最小发布的String tag = "0";Double lambda = 0.001;int K = 15;PriorityQueue<VideoInfo> minHeap = new PriorityQueue<>(Comparator.comparingDouble(v -> calculateWeight(v, lambda)));List<VideoInfo> videoList = videoInfoMapper.selectByGreaterThanVideoIdLimit1000(tag);while(videoList != null && videoList.size() != 0){for (VideoInfo video : videoList) {minHeap.offer(video);if (minHeap.size() > K) {minHeap.poll(); // 移除权重最小的视频}}tag = videoList.get(videoList.size() - 1).getVideoId();videoList = videoInfoMapper.selectByGreaterThanVideoIdLimit1000(tag);}List<VideoInfo> topK = new ArrayList<>(minHeap);topK.sort((a, b) -> Double.compare(calculateWeight(b, lambda), calculateWeight(a, lambda)));cacheVideo.setHotVideos(topK);}/*** 计算半衰期权重* 权重 = (播放量 + 点赞量) * e^(-λ * 时间差)*/private static double calculateWeight(VideoInfo video, double lambda) {long currentTime = System.currentTimeMillis();long createTime = video.getCreateTime().getTime();long timeDiffSeconds = (currentTime - createTime) / 1000; // 转为秒double decayFactor = Math.exp(-lambda * timeDiffSeconds);return (video.getPlayCount() + video.getLikeCount()) * decayFactor;}
雪花id介绍:
Mysql使用索引和order by
注意:using index表示 使用到了索引 , 并且所取的数据完全在索引中就能拿到
返回Using where 说明用户要的字段不完全覆盖,server层要进行过滤,或者进行了回表
"Using where" 表示 MySQL 服务器层需要对存储引擎返回的行进行额外的过滤检查
这种检查可能发生在两种情况下:
a) 存储引擎返回的行不完全符合 WHERE 条件(需要二次过滤)
b) 需要从存储引擎获取完整行数据(即回表)
没有索引的动用都是using where
-- 假设有索引 (a, b)
EXPLAIN SELECT a, b FROM table ORDER BY a, b;
排序和索引使用的一样,因此会使用索引,不会再进行排序
-- 假设有索引 (a, b)
EXPLAIN SELECT a, b FROM table ORDER BY b, a;
会显示"Using index; Using filesort",因为排序顺序与索引不完全匹配
-- 假设有索引 (a, b)
EXPLAIN SELECT a, b FROM table ORDER BY b, a;
会显示"Using index; Using filesort",因为排序顺序与索引不完全匹配
深分页问题:
MySQL必须读取并丢弃大量不需要的数据才能到达目标分页位置。
SELECT * FROM table INNER JOIN (SELECT id FROM table ORDER BY id LIMIT 10000, 20
) AS tmp USING(id);
优化1:
SELECT * FROM table INNER JOIN (SELECT id FROM table ORDER BY id LIMIT 10000, 20
) AS tmp USING(id);
优化2:
-- 记住上一页最后一条记录的ID
SELECT * FROM table
WHERE id > 上一页最后ID
ORDER BY id
LIMIT 20;
优化3:
索引覆盖