Redis不支持复杂查询因本质是KV存储,缺乏WHERE、JOIN等能力;ES可作可查缓存层,需配合binlog同步、合理索引拆分、DSL标准化及短TTL缓存防击穿。

复杂查询为什么不能直接塞进Redis
Redis 本质是 KV 存储,不支持 WHERE、JOIN、GROUP BY、全文模糊匹配、多字段组合排序这些操作。你把一个带 WHERE status = 1 AND type IN (2,3,4) ORDER BY created_at DESC LIMIT 20 的 SQL 原样转成 Redis key(比如 search:status1:type234:sort_created),看似缓存了结果,但只要任意一个参数变,key 就失效——前端分页跳到第 2 页、用户改了个筛选条件、运营临时加个新 type,全得重新查库、重建缓存。这不是防击穿,这是制造“缓存雪崩预备态”。
ES 不是缓存,但能当“可查的缓存层”用
ES 天然适合复杂查询:倒排索引支撑多字段过滤、相关性打分支持排序、聚合能力替代 GROUP BY、分页游标(search_after)比 OFFSET 更稳定。关键在于,它不是用来替代数据库的,而是作为“查询加速层”存在——数据仍以 MySQL 为唯一事实源,ES 只同步变更,不承担写一致性压力。
实操建议:
- 用
binlog + canal或Debezium实时同步 MySQL 增量数据到 ES,避免定时任务拉取导致延迟 - ES 索引按业务域拆分(如
product_search_v2),不混用;mapping 字段类型严格对齐,避免text字段被误用于精确匹配 - 禁止在 ES 查询中用
script_score或function_score做实时计算,这类操作会显著拖慢响应,应提前算好存入字段 - 对高频固定组合查询(如“已上架+自营+北京仓有货”),预热生成
filterDSL 并缓存到 Redis,运行时直接拼接调用,省去解析开销
Redis + ES 协同防击穿的关键动作
真正要防的不是“ES 查询慢”,而是“ES 查询失败或超时后,所有流量 fallback 到 MySQL 导致 DB 击穿”。所以必须切断 fallback 链路,而不是堆砌缓存。
具体做法:
- ES 查询设置硬超时(
timeout=800ms),超时直接返回空列表或兜底静态数据,绝不降级查库 - 对每个搜索请求生成唯一
request_id,记录 ES 查询耗时与状态到日志;若连续 5 分钟内某类 query 的timeout_rate > 5%,自动触发告警并暂停该 query 的缓存写入(防止脏数据污染 Redis) - Redis 中只缓存 ES 返回的原始 JSON 结果(
search:result:hash(query_dsl)),TTL 设为 60 秒而非数小时——短 TTL 配合 ES 本身性能,既能扛住突发流量,又不会因数据滞后引发业务问题 - ES 查询前先查 Redis,命中则直接返回;未命中则发 ES 请求,同时用
SETNX search:lock:hash(query_dsl) 1 EX 30加分布式锁,确保同一查询在 30 秒内只允许一个线程穿透到 ES,其余等待或返回缓存旧值(需业务接受最终一致性)
最容易被忽略的点:DSL 本身就是 key
很多人把 ES 查询参数拼成字符串当 key,但没标准化处理——type=[2,3,4] 和 type=[3,2,4] 是两个 key,page=1&size=20 和 size=20&page=1 也是两个 key。这会导致缓存命中率暴跌。必须统一做:query_dsl 序列化前先对数组排序、参数按字典序排列、移除空值和默认值(如 sort=created_at:desc 是默认,不参与 hash)。否则再好的布隆过滤器也救不了你。


















