在电商场景中,通过 Redis 缓存热门检索商品关键词可以显著提升搜索性能和用户体验。以下是基于 Redis 的完整实现方案:
简单的直接上代码-一眼就能看懂
private RedisTemplate<String, Object> redisTemplate;
public Boolean saveHotKeyword(String keyword) {
String key = "hot_keywords";
try {
// 检查关键词在有序集合中的分数。
if (redisTemplate.opsForZSet().score(key, keyword) != null) {
// 如果关键词存在,则调用 incrementScore 方法将分数加1
redisTemplate.opsForZSet().incrementScore(key, keyword, 1);
} else {
// 如果关键词不存在于集合中,则调用 add 方法将其添加到集合中,并设置初始分数为 1.0
redisTemplate.opsForZSet().add(key, keyword, 1.0);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
稍微复杂点-本质一样-考虑的较多较详细点
1. 数据结构选择
1.1 关键词热度存储
使用 Sorted Set(有序集合) 存储关键词及其搜索频次:
-
Key:
hot_keywords:{category_id}
(可按商品分类区分) -
Member:关键词(如
手机
、连衣裙
) -
Score:搜索次数(实时更新)
操作示例:
# 每次搜索时增加关键词的分数(搜索次数)
ZINCRBY hot_keywords:electronics 1 "智能手机"
1.2 关键词元数据缓存
使用 Hash 存储关键词的附加信息(如关联商品数、更新时间):
-
Key:
keyword_metadata:{keyword}
-
Field:
product_count
(关联商品数)、update_time
(最后更新时间)
HSET keyword_metadata:智能手机 product_count 150 update_time 1717123200
2. 核心实现步骤
2.1 实时热度统计
在用户搜索时,异步更新关键词的热度:
// 示例:Java + Spring Boot
public void logSearchKeyword(String keyword, String categoryId) {
// 1. 更新 Sorted Set 中的搜索频次
redisTemplate.opsForZSet().incrementScore(
"hot_keywords:" + categoryId,
keyword,
1
);
// 2. 异步更新关键词元数据(如关联商品数)
CompletableFuture.runAsync(() -> {
int productCount = productService.countProductsByKeyword(keyword);
redisTemplate.opsForHash().put(
"keyword_metadata:" + keyword,
"product_count",
String.valueOf(productCount)
);
});
}
2.2 定时生成热门关键词列表
每日凌晨生成当前热门关键词(避免频繁计算):
// 定时任务(Cron:每天0点执行)
@Scheduled(cron = "0 0 0 * * ?")
public void generateDailyHotKeywords() {
// 1. 按分类获取前100热门关键词(按Score倒序)
Set<String> categories = getProductCategories();
for (String categoryId : categories) {
String hotKey = "hot_keywords:" + categoryId;
Set<ZSetOperations.TypedTuple<String>> topKeywords =
redisTemplate.opsForZSet().reverseRangeWithScores(hotKey, 0, 99);
// 2. 将结果缓存到新Key(避免更新影响当前查询)
String dailyHotKey = "daily_hot_keywords:" + categoryId + ":" + LocalDate.now();
redisTemplate.opsForZSet().add(dailyHotKey, topKeywords);
// 3. 设置过期时间(保留7天历史数据)
redisTemplate.expire(dailyHotKey, 7, TimeUnit.DAYS);
// 4. 重置当前热度计数器(可选)
redisTemplate.delete(hotKey);
}
}
2.3 缓存淘汰策略
-
冷门关键词自动淘汰:
对 Sorted Set 中的关键词按以下规则清理:-
每周清理一次过去7天内无搜索的关键词:
# 使用ZREMRANGEBYSCORE删除分数(搜索次数)为0的成员 ZREMRANGEBYSCORE hot_keywords:electronics 0 0
-
限制每个分类下关键词数量(如最多保留500个):
# 保留分数最高的500个关键词,删除其他 ZREMRANGEBYRANK hot_keywords:electronics 0 -501
-
3. 查询热门关键词
3.1 获取实时热门关键词
public List<String> getRealTimeHotKeywords(String categoryId, int topN) {
String key = "hot_keywords:" + categoryId;
return redisTemplate.opsForZSet()
.reverseRange(key, 0, topN - 1);
}
3.2 获取每日缓存的热门关键词
public List<String> getDailyHotKeywords(String categoryId, LocalDate date) {
String key = "daily_hot_keywords:" + categoryId + ":" + date;
return redisTemplate.opsForZSet()
.reverseRange(key, 0, 99);
}
4. 进阶优化
4.1 热度衰减机制
为避免历史热搜长期占据榜单,可引入 时间衰减因子:
-
每天对热度值进行衰减(如乘以系数0.9):
// Lua脚本实现原子衰减操作 String script = "local keywords = redis.call('ZRANGE', KEYS[1], 0, -1); " + "for _, kw in ipairs(keywords) do " + " local score = redis.call('ZSCORE', KEYS[1], kw); " + " redis.call('ZADD', KEYS[1], tonumber(score) * 0.9, kw); " + "end"; redisTemplate.execute(new DefaultRedisScript<>(script), List.of("hot_keywords:electronics"));
4.2 防刷机制
防止恶意刷搜索量:
-
对同一用户(或IP)的重复搜索进行去重:
// 使用HyperLogLog统计独立用户数 String userKey = "kw_users:" + keyword; redisTemplate.opsForHyperLogLog().add(userKey, userId); Long uniqueUsers = redisTemplate.opsForHyperLogLog().size(userKey); // 只有独立用户数达到阈值时才增加热度 if (uniqueUsers >= MIN_UNIQUE_USERS) { redisTemplate.opsForZSet().incrementScore(hotKey, keyword, 1); }
4.3 多级缓存
-
本地缓存(Caffeine):在应用层缓存TOP 10热门关键词,减少Redis访问。
-
布隆过滤器:快速过滤不存在于热门列表中的关键词,避免无效查询。
5. 数据持久化
-
同步到数据库:每日将热门关键词持久化到 MySQL 或 Elasticsearch,用于数据分析。
-
备份机制:启用 Redis RDB/AOF 持久化,确保缓存数据不丢失。