一、什么是执行计划缓存

想象一下你去图书馆借书,每次都要重新查目录、找书架,效率肯定很低。数据库也一样,每次执行SQL语句时,如果都要重新规划查询路径(执行计划),性能就会受影响。openGauss的做法很聪明——把常用的执行计划存起来,下次直接用,这就是执行计划缓存。

举个例子:

-- 技术栈:openGauss
-- 第一次执行查询(需要生成执行计划)
EXPLAIN SELECT * FROM users WHERE age > 20;
-- 第二次执行相同查询时,直接复用缓存计划
SELECT * FROM users WHERE age > 20;  -- 这次更快!

二、缓存机制如何工作

openGauss的缓存像个智能备忘录,它会:

  1. 识别SQL指纹:去掉多余空格、统一大小写,确保SELECT * FROM usersselect * from users被识别为同一个查询
  2. 缓存淘汰策略:当缓存满了,优先淘汰最近最少使用的计划(LRU算法)
  3. 参数控制:通过plan_cache_mode参数可以设置强制使用/禁用缓存

看个实际参数配置示例:

-- 查看当前缓存模式
SHOW plan_cache_mode;  -- 默认是'AUTO'(自动选择)

-- 强制使用缓存(适合稳定查询)
SET plan_cache_mode = 'FORCE';

三、什么情况下缓存会失效

缓存虽好,但遇到这些情况会被清空或重建:

  1. 表结构变更:比如给users表新增字段
  2. 统计信息更新:执行ANALYZE users
  3. 手动清理:执行DISCARD PLANS命令

示例演示失效场景:

-- 先执行普通查询(生成缓存)
SELECT * FROM orders WHERE status = 'shipped';

-- 修改表结构导致缓存失效
ALTER TABLE orders ADD COLUMN discount NUMERIC;

-- 再次查询需要重新生成计划
SELECT * FROM orders WHERE status = 'shipped';  -- 这次变慢了

四、性能优化实战技巧

4.1 让更多查询命中缓存

  • 写法标准化:团队统一SQL书写风格
  • 绑定变量:用?代替直接值,避免SELECT * FROM users WHERE id=1id=2被当作不同查询

绑定变量示例:

-- 不推荐(无法复用缓存)
SELECT * FROM products WHERE category = 'electronics';

-- 推荐写法(使用绑定变量)
PREPARE prod_query (TEXT) AS 
  SELECT * FROM products WHERE category = $1;
EXECUTE prod_query('electronics');

4.2 监控缓存命中率

通过系统视图查看缓存效果:

-- 查看缓存统计(命中率越高越好)
SELECT * FROM pg_plan_cache_status;
-- 理想情况下命中率应超过85%

五、适用场景与注意事项

5.1 最适合的场景

  • OLTP系统(短平快查询)
  • 参数化查询多的应用
  • 查询模式稳定的业务

5.2 需要小心的坑

  1. 复杂查询谨慎使用:20个表关联的查询,缓存可能反而降低性能
  2. 内存消耗:每个缓存计划约占用2-10KB内存
  3. 动态SQL问题:像SELECT * FROM ${tableName}这种无法缓存

六、总结

执行计划缓存像是数据库的"肌肉记忆",合理利用能让查询速度提升30%以上。关键记住三点:

  1. 简单查询多用,复杂查询慎用
  2. 通过绑定变量提高命中率
  3. 定期监控pg_plan_cache_status

最后给个优化检查清单:

-- 1. 检查当前缓存模式
SHOW plan_cache_mode;

-- 2. 分析缓存命中率
SELECT * FROM pg_plan_cache_status;

-- 3. 确认是否有无效缓存
SELECT * FROM pg_plan_cache_invalidations;