游乐游手机版
首页/数据库/文章详情

KingbaseES的SQL慢查询优化实战:物化视图、QueryMapping与函数缓存详解

时间:2026-07-25 19:28
您肯定遇到过这样的 SQL 慢查询——查一次三十秒,一天跑八百回,而且每次返回的结果一模一样。您盯着执行计划分析半天,索引加了,统计信息更新了,连 VACUUM 都跑了一遍,可速度依然慢得让人抓狂。 为什么?因为问题根本不在执行计划上。真正的症结在于 SQL 本身在做重复劳动:同一份结果被反复计算,
您肯定遇到过这样的 SQL 慢查询——查一次三十秒,一天跑八百回,而且每次返回的结果一模一样。您盯着执行计划分析半天,索引加了,统计信息更新了,连 VACUUM 都跑了一遍,可速度依然慢得让人抓狂。

SQL慢怎么办?KingbaseES的物化视图、QueryMapping和函数缓存优化实战

为什么?因为问题根本不在执行计划上。真正的症结在于 SQL 本身在做重复劳动:同一份结果被反复计算,或者一条写得糟糕的语句您没权限修改,它就只能一遍又一遍低效地执行。这种慢,优化器真的救不了,得换个思路——别让它重复,把算过的结果直接拿来用。用空间换时间,就这么简单。

这篇文章专门介绍三个用于解决这类问题的利器:物化视图、Query Mapping 和函数结果集缓存。它们都是 KingbaseES 数据库性能优化的核心手段,能帮您彻底告别重复计算带来的性能瓶颈。

前言

物化视图、Query Mapping 和函数结果集缓存,这三个功能都是人大金仓 KingbaseES 数据库提供的性能优化利器,各有侧重,但目标一致:消除重复计算,提升查询效率。

为了方便您快速理解,我把它们的基本情况整理成表格:

功能名称核心作用一句话解释
Query MappingSQL 语句的“智能替换”在不修改应用代码的前提下,将低效的 SQL 自动替换为提前配置好的高效 SQL。
物化视图 (Materialized View)查询结果的“预计算存储”把耗时查询的结果像普通表一样存下来,下次直接读取,速度极快。
函数结果缓存 (Function Result Cache)函数执行结果的“短时复用”在一条 SQL 执行期间,相同的函数调用直接返回缓存结果,避免重复计算。

接下来,我们逐一拆解每个功能的具体用法和适用场景。

1. Query Mapping:不改代码也能优化 SQL

这是一个非常实用的功能,它允许 DBA(数据库管理员)在不修改应用程序源码的情况下,对存在性能问题的 SQL 语句进行优化。

  • 它是如何工作的?您可以在数据库中预先创建一条“映射规则”,指定一个“源 SQL”(低效的)和“目标 SQL”(高效的)。当数据库收到匹配的“源 SQL”时,会自动将其替换为“目标 SQL”来执行,整个过程对用户透明。

  • 两种匹配模式

    • TEXT(文本)模式:进行简单的字符串匹配和替换,速度快,适合快速解决一些明确的 SQL 问题。

    • SEMANTICS(语义)模式:会进行语法和语义检查,替换更精准、更安全,适合对准确性要求高的场景。

  • 什么时候用?

    • 性能调优:例如,自动将耗时的 UNION 操作替换为更快的 UNION ALL

    • 数据库迁移:在将其他数据库迁移到 KingbaseES 时,自动将源库的 SQL 语法转换为 KingbaseES 兼容的语法。

2. 物化视图:为复杂查询加速

您可以把物化视图看作一个“快照”。它把一个复杂、耗时的查询结果预先计算好,并像普通表一样存储在数据库中。

  • 跟普通视图有什么区别?普通视图只是一个“虚拟表”,每次查询时都会重新执行 SQL 去获取数据。而物化视图存储的是真实的数据,因此查询它的速度和在普通表中查询一样快。

  • 数据如何刷新?由于物化视图是静态的“快照”,当基表(源表)数据发生变化后,它需要被刷新才能保持最新。KingbaseES 支持手动或自动的刷新机制,但默认需手动执行全量刷新。

3. 函数结果缓存:避免重复计算

这个功能专注于优化 SQL 语句中函数的执行效率。

  • 它是如何工作的?当一条 SQL 语句在执行时,如果多次调用同一个确定性函数(即输入相同,输出也相同的函数,如 IMMUTABLESTABLE 类型的函数),并且参数也相同,数据库只会真正执行一次,后续调用直接返回缓存的结果。

  • 适用场景:特别适用于那些被频繁调用、但计算逻辑复杂的函数,尤其是在处理大量数据行时,能显著减少 CPU 开销,提升整体 SQL 执行效率。

一、先别急,有个事你得拎清楚

一个个看之前,先把一件事想明白:这哥仨治的是同一种病——重复计算

优化器再神,也没法帮你跳过“本来该算一次、却被迫算了一百次”这种事。它能替你挑最快的路,可它不会说“这条路我都走过八百遍了,结果我背下来了”。这恰恰是这三个功能补的位。

它们各自盯的层面不一样。物化视图管的是一整个查询的结果,算一次存好,谁来查都给它现成的。Query Mapping 更靠前,SQL 还没进优化器呢,就被它偷偷换掉了。函数结果集缓存范围最小,就管一条 SQL 里被反复调用的那个函数。

所以差别其实就俩字:范围。范围最大的是物化视图,最小的是函数缓存,Query Mapping 夹在中间管语句。下面挨个说。

二、物化视图:把费劲算的结果先存下来

先说物化视图。

这名字听着唬人,原理土得很:把一个查询的结果,实打实地存下来。普通视图你知道吧,那玩意儿是个“别名”,不存数据,每次查照样现算。物化视图不一样,它是真存,小数据放内存,大的落磁盘。

它最拿手的活儿就一件:把那些算起来要命的操作,比如大表连接、复杂分组,提前算好搁那儿。下次再有人查同样的东西,直接拿现成的,省下重算这一大笔开销。

举个真事。订单表 orders,几千万行,业务天天要按用户汇总订单数和金额。写法没毛病,就是每次都得全表扫加分组:

-- 每次现算:扫全表 + 分组聚集,数据一大就磨叽
SELECT user_id, COUNT(*) AS order_cnt, SUM(amount) AS total
FROM   orders
GROUP BY user_id;

慢。那建个物化视图,把这汇总结果预先算出来存着:

-- 建物化视图:把耗时的聚集预先算好、落盘
CREATE MATERIALIZED VIEW mv_order_summary AS
SELECT user_id, COUNT(*) AS order_cnt, SUM(amount) AS total
FROM   orders
GROUP BY user_id;

-- 查询直接打物化视图,秒级返回
SELECT * FROM mv_order_summary WHERE user_id = 1001;

想查得更快,给它建个索引,跟普通表一个套路:

-- 给物化视图建索引,按用户查就快了
CREATE INDEX idx_mv_user ON mv_order_summary(user_id);

但是,重点来了,也是好多人栽跟头的地方:底表数据变了,物化视图不会自己跟着变。 本数据库的物化视图不支持自动更新,也没有增量更新这一说,你只能手动刷新,而且一刷就是全量重算:

-- 底表更新后,手动刷新(全量重算)
REFRESH MATERIALIZED VIEW mv_order_summary;

-- 想刷新时还能让人并发查、不锁表,加 CONCURRENTLY(前提是有唯一索引)
CREATE UNIQUE INDEX uk_mv_user ON mv_order_summary(user_id);
REFRESH MATERIALIZED VIEW CONCURRENTLY mv_order_summary;

所以得说清楚,物化视图是个“挑食”的优化,不是什么时候都能往上怼。它最香的就两种情况。

一种是底表更新少、但查询又重又勤。报表、看板就是它的亲儿子,数据一天才喂一两次,白天被查成千上万遍,预先算好等于白捡。

另一种是查外部库的表。跨库扫描本来就慢得要死,与其每次都跑外边去捞,不如用物化视图把数据搬到本地存着,查的时候走本地:

-- 把访问慢的外部表数据,缓存成本地物化视图(记得定期手动刷新保鲜)
CREATE MATERIALIZED VIEW mv_customer AS
SELECT * FROM fdw_customer;

反过来,如果你的表分分钟都在写,刷新又追不上变化,那就别硬上了。缓存刚刷完就过期,纯属给自己找不痛快。

三、Query Mapping:SQL 还没进优化器,先偷梁换柱

再说 Query Mapping,这个功能我特别喜欢,因为它“阴”。

啥意思呢,它让你提前把“源 SQL → 目标 SQL”的映射关系存进系统表。用户敲进来的语句只要匹配上,数据库就偷偷换成目标语句去跑,用户全程毫无察觉。是不是有点像给 SQL 戴了个面具。

它能干两件事,都特别实用。一是 SQL 调优:碰到条写得很烂的 SQL,偏偏你还没权限改源码(八成是第三方系统发的),这时候建个映射,偷偷把它换成等价但高效的写法,神不知鬼不觉。二是数据库迁移:把别家的方言语法,翻译成本库能跑的语法。

它分两个级别,这俩你得记牢。

TEXT 级别,就是纯字符串匹配,啥都不检查,原样存原始 SQL 和目标 SQL。SEMANTICS 级别高级点,会走一遍词法语法语义检查,存的是查询树,也就是 SQL 解析后的那个内部形态。

这里有个坑得提醒你:想跟 Hint 一起用,只能选 TEXT。因为 Hint 是写在注释里的,SEMANTICS 存的是查询树,注释根本留不住,Hint 也就跟着废了。还有,SEMANTICS 现在搞不定带 rownum 的语句,碰上记得绕。

用法不复杂,三步。先开开关,再建规则,然后该查查:

-- 第一步:配置文件里开启(kingbase.conf)
--   enable_query_rule = on

-- 第二步:建一条映射,把低效的 NOT IN 换成高效的 NOT EXISTS
SELECT create_query_rule(
    'qm_tune1',
    'SELECT * FROM t1 WHERE id NOT IN (SELECT id FROM t2)',          -- 源 SQL(低效)
    'SELECT * FROM t1 WHERE NOT EXISTS (SELECT 1 FROM t2 WHERE t2.id = t1.id)',  -- 目标 SQL(高效)
    true,    -- 这条规则生不生效
    'text'   -- 级别:text 或 semantics
);

规则一立,用户再发那条 NOT IN,底下实际跑的是 NOT EXISTS,干净利落。

还能玩参数化,这点很灵活。源和目标里都能用 $1$2 代表入参,匹配的时候按位置对上:

-- 源 SQL 带 3 个参数,目标 SQL 只关心 val=$3 的计数
SELECT create_query_rule(
    'qm_count',
    'select id,val from t1 where id<$1 and id>$2 and val=$3',
    'select count(0) from t1 where val=$3',
    true, 'text'
);

连参数顺序都能给你打乱交换,灵活得很:

-- 把 id<$1 and val=$2 换成 id<$2 and val=$1,入参顺序对调
SELECT create_query_rule(
    'qm_swap',
    'select * from t2 where id<$1 and val=$2',
    'select * from t2 where id<$2 and val=$1',
    true, 'text'
);

更绝的是,它还能干“条件下推”这种深度改写。比如外层一个连接条件,正常它压不进 UNION 子查询里去;建条映射,把语句改成 LATERAL 形式(就是允许内层引用外层数据那种写法),条件就能推进去,少扫一大堆没用的数据。想看替换之后到底走的啥计划,加个标记就行:

-- 看替换后走的是什么计划
explain (usingquerymapping)
SELECT count(0) FROM t1, (...) AS v WHERE t1.id = v.id;

规则管起来也省心,几个函数来回用:

SELECT drop_query_rule('qm_tune1');     -- 删掉某条规则
SELECT enable_query_rule('qm_tune1');   -- 让某条规则生效
SELECT disable_query_rule('qm_tune1');  -- 暂时关掉某条规则
SELECT drop_query_rule();               -- 不传参 = 删光所有规则,悠着点

四、函数结果集缓存:同一个函数调一万遍,只算一遍

最后这个,函数结果集缓存,专门对付函数。

你有没有写过这种代码:写了个函数,然后一条 SQL 里反反复复调它。SELECT 里调一次,WHERE 里又调一次,或者对着一堆入参挨个调。函数本身要是就慢,再乘上调那么多次,这条 SQL 直接慢到你想砸键盘。

这功能治的就是这个。原理不绕:函数一旦标成 IMMUTABLE 或者 STABLE,也就是同样的入参永远返回同样的结果,数据库在一条 SQL 跑的期间,就会把“函数 + 入参”算出来的结果存起来。后面再碰到一模一样的入参,直接吐缓存,函数体压根不执行。

得解释下这几个词,不然容易懵。

IMMUTABLE 最严,入参一样结果就一样,跟表、跟时间都无关,绝对的。STABLE 松一点,只要同一条 SQL 里结果不变就行,比如去查一张很少改的配置表。VOLATILE 就没戏了,结果会变的(取当前时间那种),没法缓存。

想命中缓存,三个条件得凑齐:函数得是 IMMUTABLESTABLE;返回值得是单个值,不能是集合;入参别超过 16 个。

来,建个标了 IMMUTABLE 的折扣函数:

-- IMMUTABLE 函数:同样的 (price, rate) 永远算出同样的折扣价
CREATE OR REPLACE FUNCTION cal_discount(price numeric, rate numeric)
RETURNS numeric
LANGUAGE sql
IMMUTABLE
AS $$
    SELECT round(price * rate, 2);
$$;

开关一开,写条会反复调它的 SQL:

-- 开启函数结果集缓存,并设个缓存上限
SET function_result_cache = on;
SET function_cache_number = 1000;

-- 这条 SQL 里,同一组 (amount, 0.9) 入参的调用只算一次,其余命中缓存
SELECT id, cal_discount(amount, 0.9) AS dp
FROM   orders
WHERE  cal_discount(amount, 0.9) > 100;

再补个 STABLE 的,读张几乎不变的汇率表,一个道理:

-- STABLE 函数:按币种查汇率,币种没变结果就不变
CREATE OR REPLACE FUNCTION get_rate(currency text)
RETURNS numeric
LANGUAGE sql
STABLE
AS $$
    SELECT rate FROM exchange_rate WHERE cur = currency;
$$;

有个事得重点强调,是这功能跟物化视图最不一样的地方:缓存只在单条 SQL 执行期间活着,这条 SQL 一跑完就没了。 所以它治不了跨会话的重复,它就管一条 SQL 内部那点事。想跨会话缓存?那是物化视图的活儿,别搞混了。

两个开关也好懂,function_result_cache 管开不开,function_cache_number 管最多存几个。

五、那到底用哪个?看场景下菜碟

道理讲完了,真到用的时候怎么选,才是关键。我给你列个表,对号入座就行:

维度物化视图Query Mapping函数结果集缓存
治什么病整个查询结果反复算语句写法低效、改不动源码单条 SQL 内函数反复调
生效范围跨会话、长期针对特定语句模式单条 SQL 执行期间
怎么生效手动全量刷新建映射规则替换自动命中缓存
数据新鲜度有延迟,需刷新不碰数据,只换写法实时,入参同则结果同
典型场景报表、看板、外部表第三方系统 SQL 调优、迁移复杂计算函数高频调用

再啰嗦几句掏心窝的。

报表、查多写少的数据,闭眼选物化视图。但刷新频率你自己心里得有数,别数据都隔夜了还在用昨天的结果,出了事锅可是你的。

没法改源码、又非得优化某条语句,Query Mapping 顶上。这玩意儿最大的好处是无侵入,应用那边一行都不用动,确实好用。

一个计算函数被调成千上万次的,开函数结果集缓存。开之前一定确认函数确实是 IMMUTABLESTABLE,别到时候结果错了还查不出哪出的毛病。

还有,这三个不是互斥的,能搭着用。拿 Query Mapping 把低效语句改写成走物化视图,效果直接叠加,爽得很。

六、收个尾

说到底,这三个东西干的是同一件事:跟重复较劲。

物化视图管结果级的重复,提前算好存着;Query Mapping 管语句级的重复,从源头换掉烂写法;函数缓存管调用级的重复,一条 SQL 内别傻算。仨人各占一段赛道,谁也替不了谁。

真想用好它们,功夫其实不在背语法上,而在你能不能先想明白一件事:我这条 SQL 慢,到底慢在哪种重复上。想清楚了,对症下药,性能蹦一个数量级,真不是吹的。

下次再碰到那种优化器都救不了的慢查询,先别急着骂优化器,它也挺冤的。先问自己一句:这活儿,是不是在反复干同一件事?是的话,这仨里头,总有一个能收拾它。

这三个功能共同构成了 KingbaseES 数据库一套强大的性能优化工具集:

  • Query Mapping 让你能低成本、无侵入地解决棘手的 SQL 性能问题。

  • 物化视图高频、复杂的查询提供了极速的访问体验。

  • 函数结果缓存 则通过避免重复计算来提升 SQL 的整体执行效率。

来源:https://www.jb51.net/database/368050gz5.htm
上一篇MySQL添加外键错误Cannot add foreign key constraint解决方法 下一篇MySQL5.7版本数据库sql_mode参数only_full_group_by配置问题的详细解决方法
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
自增主键值从何而来?深入理解原理,告别只会auto_increment
数据库 · 2026-07-25

自增主键值从何而来?深入理解原理,告别只会auto_increment

KingbaseES推荐使用serial、bigserial、显式sequence或identity列实现自增主键。serial创建integer并关联序列,bigserial对应bigint;显式sequence可自定义起始值等参数;identity有generatedbydefault(允许指定值)与always(禁止)两种模式。

Linux下瀚高数据库授权文件过期及替换解决方案
数据库 · 2026-07-25

Linux下瀚高数据库授权文件过期及替换解决方案

在银河麒麟系统下,瀚高数据库hgdb-4 5试用授权20天到期后需替换正式授权文件。正确操作:停止服务,备份旧文件,将授权文件复制到 opt highgo hgdb-4 5 etc lic 并命名为hgdb lic,设置权限600和属主highgo:highgo,再启动服务。禁止直接修改data目录下的license info文件。

Oracle BLOB实时同步的5大技术挑战与难点解析
数据库 · 2026-07-25

Oracle BLOB实时同步的5大技术挑战与难点解析

OracleBLOB实时同步面临分片组装、多列隔离、长事务跨窗口、事务回滚及大对象资源控制等技术挑战,必须在日志中精确还原完整字段值,才能保证源端与目标端数据完全一致,这对同步系统的稳健性提出了高要求。

MySQL禁用redo日志导致全备失败
数据库 · 2026-07-25

MySQL禁用redo日志导致全备失败

MySQL全量备份失败是由于数据定义语言操作触发排序索引构建,禁用重做日志导致XtraBackup无法获取一致性备份。测试验证表明,优化表语句即使无数据也会触发该问题。根本原因在于排序索引构建过程跳过了重做日志记录,破坏了备份的一致性。

Kafka架构图优化与改进的全面详细步骤与实践指南
数据库 · 2026-07-25

Kafka架构图优化与改进的全面详细步骤与实践指南

Kafka作为实时数据流处理的核心中间件,其底层架构虽已相当成熟,但在实际生产环境中,要充分发挥其性能潜力,仍需落实到具体的调优与架构改造上。核心目标可归纳为三点:如何承载更高的吞吐量、如何保障数据不丢失、以及故障发生时如何快速恢复。本文将从这几个关键方向出发,深入探讨如何真正榨干Kafka集群的性