Postgres自定义表缓存模式的更优实现方案咨询
Postgres 通用缓存模式的轻量实现方案
针对你提到的「先查缓存表、无结果则写入慢查询结果再返回」的通用缓存模式,Postgres 原生支持多种无需自定义函数的实现方式,代码更简洁、还能利用原生原子性避免并发问题,可根据场景选择:
1. 全表缓存场景:单CTE语句实现
如果你的quick_table是缓存整个慢查询的全量结果,不需要按键拆分,可以直接用以下单条SQL实现完整逻辑,无需编写函数:
-- 直接执行该语句即可返回等价于你描述逻辑的结果 SELECT * FROM quick_table UNION ALL SELECT * FROM ( INSERT INTO quick_table SELECT * FROM /* 这里替换为你的慢查询语句 */ slow_query WHERE NOT EXISTS (SELECT 1 FROM quick_table) RETURNING * ) cache_data;
逻辑说明:
- 如果
quick_table已有数据,NOT EXISTS判断不成立,不会执行插入,语句直接返回已有缓存结果 - 如果
quick_table为空,插入逻辑触发,返回刚写入的缓存结果 - 整条语句是原子操作,不会出现并发场景下重复写入的问题
2. 按键缓存场景:结合ON CONFLICT实现
如果你的缓存是按业务键拆分(比如按用户ID、区域ID等独立缓存),可以给quick_table的缓存键加唯一约束,配合INSERT ... ON CONFLICT实现:
-- 示例中cache_key为缓存唯一键,替换为实际业务字段即可 WITH inserted_cache AS ( INSERT INTO quick_table (cache_key, col1, col2, col3) SELECT 'target_key', t.col1, t.col2, t.col3 FROM (/* 这里替换为带键过滤的慢查询,比如 WHERE user_id = 'target_key' */) t ON CONFLICT (cache_key) DO NOTHING RETURNING * ) SELECT * FROM inserted_cache UNION ALL SELECT * FROM quick_table WHERE cache_key = 'target_key';
3. 容忍延迟的场景:原生物化视图
如果你的缓存允许一定的滞后性,不需要实时刷新,直接用Postgres原生的物化视图是最省事的方案,连写入逻辑都不需要自己维护:
-- 创建物化视图,本质是把慢查询结果持久化存储,查询速度和普通表一致 CREATE MATERIALIZED VIEW quick_table AS /* 这里替换为你的慢查询语句 */; -- 可以给物化视图加索引进一步优化查询速度 CREATE INDEX idx_quick_table_col1 ON quick_table(col1); -- 需要刷新缓存时执行即可 REFRESH MATERIALIZED VIEW quick_table;
额外优化建议
- 如果需要自动过期缓存,可以在
quick_table中加expire_attimestamp字段,查询时增加WHERE expire_at > NOW()条件,插入时设置expire_at = NOW() + INTERVAL '1 hour'即可,逻辑同样可以集成到上面的单条SQL中,不需要额外写函数 - 注意:如果慢查询包含非确定性结果(比如带
RANDOM()、CURRENT_TIMESTAMP等函数),高并发场景下建议加行锁避免重复写入,性能损耗极低
内容的提问来源于stack exchange,提问作者Xodarap
相关产品推荐
相关产品推荐

