📝 摘要
本文基于人大金仓 V9.7.0 生产环境实战经验,整理出一套完整的慢 SQL 根治方法论。从问题定位 (慢查询日志、实时监控、历史统计、锁等待分析)到深度分析 (执行计划解读、异常识别、统计信息排查),再到落地优化(索引、SQL 重构、参数调优、表结构优化),全程提供可直接复制的 SQL 和配置。特别针对金仓与 PostgreSQL 的核心差异点进行了重点说明,解决了政务信创项目中 90% 以上的慢 SQL 问题。
政务选金仓,金融选达梦;MySQL 迁移选金仓,Oracle 迁移选达梦专注 SpringBoot3 + 人大金仓 + 达梦信创实战,关注不迷路
📚 目录
- [🔍 问题定位:3 分钟找到所有慢 SQL](#🔍 问题定位:3 分钟找到所有慢 SQL)
- [📜 1.1 慢查询日志开启(生产必配)](#📜 1.1 慢查询日志开启(生产必配))
- [⚡ 1.2 实时慢 SQL 查询(紧急排查)](#⚡ 1.2 实时慢 SQL 查询(紧急排查))
- [📊 1.3 历史慢 SQL 统计(sys_stat_statements)](#📊 1.3 历史慢 SQL 统计(sys_stat_statements))
- [🔧 1.4 auto_explain 插件(自动记录执行计划)](#🔧 1.4 auto_explain 插件(自动记录执行计划))
- [🔒 1.5 锁等待与阻塞分析](#🔒 1.5 锁等待与阻塞分析)
- [🔬 问题分析:读懂金仓执行计划的核心要点](#🔬 问题分析:读懂金仓执行计划的核心要点)
- [📖 2.1 EXPLAIN 命令详解与最佳实践](#📖 2.1 EXPLAIN 命令详解与最佳实践)
- [🎯 2.2 执行计划关键指标与算子解读](#🎯 2.2 执行计划关键指标与算子解读)
- [⚠️ 2.3 常见异常执行计划识别与诊断](#⚠️ 2.3 常见异常执行计划识别与诊断)
- [🔄 2.4 金仓与 PostgreSQL 执行计划核心差异](#🔄 2.4 金仓与 PostgreSQL 执行计划核心差异)
- [📈 2.5 统计信息异常排查与修复](#📈 2.5 统计信息异常排查与修复)
- [💡 问题优化:从 SQL 到参数的全维度调优](#💡 问题优化:从 SQL 到参数的全维度调优)
- [🗂️ 3.1 索引优化:性价比最高的优化手段](#🗂️ 3.1 索引优化:性价比最高的优化手段)
- [✍️ 3.2 SQL 语句重构:从根本上解决性能问题](#✍️ 3.2 SQL 语句重构:从根本上解决性能问题)
- [💪 3.3 HINT 提示:强制优化器执行正确计划](#💪 3.3 HINT 提示:强制优化器执行正确计划)
- [⚙️ 3.4 关键参数调优:挖掘数据库性能潜力](#⚙️ 3.4 关键参数调优:挖掘数据库性能潜力)
- [🏗️ 3.5 表结构优化:从设计层面避免慢 SQL](#🏗️ 3.5 表结构优化:从设计层面避免慢 SQL)
- [🚫 避坑要点:金仓慢 SQL 优化的 12 个致命陷阱](#🚫 避坑要点:金仓慢 SQL 优化的 12 个致命陷阱)
- [✅ 生产环境验证与监控体系搭建](#✅ 生产环境验证与监控体系搭建)
- [🎁 总结与专栏推荐](#🎁 总结与专栏推荐)
🔍 1. 问题定位:3 分钟找到所有慢 SQL
调优的第一步永远是精准定位,而不是上来就乱改参数。金仓 V9 提供了 5 种定位慢 SQL 的方式,覆盖实时、历史、全量、锁等待等所有场景。
📜 1.1 慢查询日志开启(生产必配)
这是最基础也是最有效的定位手段,建议所有生产环境在上线前就配置好。
💻 可直接复制的完整配置(kingbase.conf):
# 开启日志收集器(必须)
logging_collector = on
# 日志输出格式(CSV便于后续用Excel或脚本分析)
log_destination = 'csvlog'
# 日志目录(相对data目录,建议单独挂载磁盘)
log_directory = 'pg_log'
# 日志文件名格式
log_filename = 'kingbase-slow-%Y-%m-%d_%H.log'
# 日志轮转:每小时一个文件
log_rotation_age = 60
# 日志轮转:单个文件最大1GB
log_rotation_size = 1GB
# 自动截断旧日志(避免磁盘占满)
log_truncate_on_rotation = on
# 保留最近7天的日志
log_retention_period = '7d'
# 慢查询核心配置:记录执行超过100ms的SQL
# 注意:生产环境不要设置为0,会产生海量日志
log_min_duration_statement = 100
# 记录所有DDL语句
log_statement = 'ddl'
# 记录锁等待超过1秒的信息
log_lock_waits = on
log_min_messages = warning
# 记录检查点信息(用于IO性能分析)
log_checkpoints = on
# 记录连接和断开信息
log_connections = on
log_disconnections = on
# 日志前缀:包含时间、进程ID、用户、数据库、客户端IP、应用名称
log_line_prefix = '%m %p %u %d %r %a '
🔧 配置生效与验证:
# 重载配置(不需要重启,推荐)
sys_ctl reload -D /data/kingbase
# 验证配置是否生效
ksql -Usystem -d your_db -c "show log_min_duration_statement;"
# 查看日志文件
tail -f /data/kingbase/pg_log/kingbase-slow-$(date +%Y-%m-%d_%H).log
⚡ 1.2 实时慢 SQL 查询(紧急排查)
当系统突然卡顿,CPU 或 IO 飙升时,需要立即查看当前正在执行的慢 SQL:
sql
-- 查看TOP 10耗时最长的活跃SQL(最常用)
SELECT
pid,
usename,
datname,
application_name,
client_addr,
state,
query_start,
now() - query_start AS duration,
query
FROM sys_stat_activity
WHERE state = 'active'
AND pid <> sys_backend_pid()
ORDER BY duration DESC
LIMIT 10;
-- 查看长事务(idle in transaction状态,最容易导致锁阻塞)
SELECT
pid,
usename,
datname,
client_addr,
query_start,
now() - query_start AS duration,
query
FROM sys_stat_activity
WHERE state = 'idle in transaction'
ORDER BY duration DESC;
-- 查看等待事件(金仓特有,非常重要)
SELECT
pid,
usename,
datname,
wait_event_type,
wait_event,
query
FROM sys_stat_activity
WHERE wait_event IS NOT NULL
AND pid <> sys_backend_pid()
ORDER BY query_start DESC;
-- 终止指定慢SQL进程(安全终止)
SELECT sys_terminate_backend(12345); -- 替换为实际pid
-- 强制终止(慎用,可能导致事务回滚)
SELECT sys_cancel_backend(12345);
📊 1.3 历史慢 SQL 统计(sys_stat_statements)
这是金仓最强大的性能分析插件,能统计所有 SQL 的执行次数、总耗时、平均耗时、IO 消耗等指标。
🔧 插件安装与配置:
# kingbase.conf添加(注意:必须重启数据库)
shared_preload_libraries = 'sys_stat_statements'
# 跟踪所有SQL,包括存储过程内部的SQL
sys_stat_statements.track = all
# 最多记录10000条SQL
sys_stat_statements.max = 10000
# 跟踪执行计划
sys_stat_statements.track_planning = on
💻 重启后创建扩展:
CREATE EXTENSION IF NOT EXISTS sys_stat_statements;
📈 TOP 10 慢 SQL 查询(按总耗时排序):
SELECT
queryid,
query,
calls,
total_time,
mean_time,
max_time,
min_time,
rows,
shared_blks_hit,
shared_blks_read,
shared_blks_dirtied,
temp_files,
temp_bytes
FROM sys_stat_statements
ORDER BY total_time DESC
LIMIT 10;
-- TOP 10按平均耗时排序
SELECT * FROM sys_stat_statements ORDER BY mean_time DESC LIMIT 10;
-- TOP 10按IO消耗排序
SELECT * FROM sys_stat_statements ORDER BY shared_blks_read DESC LIMIT 10;
-- 重置统计信息(优化前后对比用)
SELECT sys_stat_statements_reset();
🔧 1.4 auto_explain 插件(自动记录执行计划)
对于偶尔出现的慢 SQL,手动抓执行计划很困难。auto_explain 可以自动将慢 SQL 的执行计划写入日志。
💻 配置:
shared_preload_libraries = 'sys_stat_statements,auto_explain'
# 记录执行超过100ms的SQL的执行计划
auto_explain.log_min_duration = 100
# 真实执行并显示实际运行时间
auto_explain.log_analyze = on
# 显示缓冲区使用情况
auto_explain.log_buffers = on
# 显示详细信息
auto_explain.log_verbose = on
# 显示触发器执行时间
auto_explain.log_triggers = on
# 记录嵌套语句的执行计划
auto_explain.log_nested_statements = on
🔒 1.5 锁等待与阻塞分析
很多时候 SQL 慢不是因为执行慢,而是因为被其他事务阻塞了。金仓提供了专门的视图来查看锁信息:
-- 查看当前所有锁
SELECT
pid,
usename,
datname,
relation::regclass AS table_name,
locktype,
mode,
granted
FROM sys_locks
WHERE relation IS NOT NULL
ORDER BY pid;
-- 查看阻塞关系(谁阻塞了谁)
SELECT
blocked.pid AS blocked_pid,
blocked.usename AS blocked_user,
blocked.query AS blocked_query,
blocking.pid AS blocking_pid,
blocking.usename AS blocking_user,
blocking.query AS blocking_query,
now() - blocked.query_start AS blocked_duration
FROM sys_stat_activity blocked
JOIN sys_locks bl ON blocked.pid = bl.pid
JOIN sys_locks bk ON bl.relation = bk.relation AND bl.locktype = bk.locktype
JOIN sys_stat_activity blocking ON bk.pid = blocking.pid
WHERE NOT bl.granted AND bk.granted;
🔬 2. 问题分析:读懂金仓执行计划的核心要点
定位到慢 SQL 后,下一步就是分析执行计划。金仓 V9 的执行计划语法与 PostgreSQL 兼容,但在代价模型、算子行为和统计信息处理上有明显差异。
📖 2.1 EXPLAIN 命令详解与最佳实践
💻 最常用的完整命令:
-- 真实执行并显示详细执行计划(推荐,90%的场景用这个)
EXPLAIN (ANALYZE, BUFFERS, VERBOSE, COSTS)
SELECT * FROM t_orders WHERE order_date > '2026-01-01';
-- 对于DML语句,不想影响数据可以用事务包裹
BEGIN;
EXPLAIN (ANALYZE, BUFFERS) UPDATE t_orders SET status = 1 WHERE id = 123;
ROLLBACK;
-- 只生成执行计划,不真实执行(用于快速查看)
EXPLAIN SELECT * FROM t_orders WHERE order_date > '2026-01-01';
📋 EXPLAIN 选项对比表:
| 选项 | 默认值 | 功能说明 | 使用场景 |
|---|---|---|---|
| ANALYZE | false | 真实执行 SQL 并显示实际运行时间和行数 | ✅ 必须开启,否则只能看预估 |
| BUFFERS | false | 显示缓冲区使用情况(必须配合 ANALYZE) | 📊 分析 IO 性能问题时必须开启 |
| VERBOSE | false | 显示详细信息,如字段名、表别名、函数调用 | 🔍 复杂查询时开启 |
| COSTS | true | 显示执行计划的预估成本 | 📈 对比不同执行计划时有用 |
| TIMING | true | 显示每个节点的实际执行时间 | ⏱️ 定位哪个节点最慢 |
| SUMMARY | true | 显示执行计划的总耗时和统计信息 | ✅ 开启 |
| FORMAT | text | 输出格式:text、json、xml、yaml | 🤖 自动化分析时用 json |
🎯 2.2 执行计划关键指标与算子解读
拿到执行计划后,从下往上读,重点关注以下几个指标:
📊 核心指标:
- cost:优化器预估的成本,由启动成本和总成本组成(如 0.00..100.00)
- rows:优化器预估的返回行数
- width:预估的每行平均字节数
- actual time:实际执行时间(单位:毫秒)
- actual rows:实际返回行数
- loops:该节点执行的次数
📋 常见算子解读:
| 算子 | 中文名称 | 性能评价 | 说明 |
|---|---|---|---|
| Seq Scan | 顺序扫描 | ❌ 差 | 全表扫描,数据量大时性能极差 |
| Index Scan | 索引扫描 | ✅ 好 | 通过索引查找,然后回表获取数据 |
| Index Only Scan | 仅索引扫描 | ⭐ 极好 | 不需要回表,直接从索引获取所有数据 |
| Bitmap Index Scan | 位图索引扫描 | ✅ 较好 | 先扫描索引生成位图,再根据位图访问表 |
| Bitmap Heap Scan | 位图堆扫描 | ✅ 较好 | 配合 Bitmap Index Scan 使用 |
| Nested Loop | 嵌套循环连接 | ✅ 小表好 | 适合小表驱动大表,内表有索引 |
| Hash Join | 哈希连接 | ✅ 大表好 | 适合大表之间的等值连接 |
| Merge Join | 归并连接 | ✅ 排序好 | 适合两个已经排序的表连接 |
| Sort | 排序 | ❌ 差 | 内存不足时会溢出到磁盘 |
| HashAggregate | 哈希聚合 | ✅ 好 | 用于 GROUP BY 和聚合函数 |
| GroupAggregate | 分组聚合 | ❌ 差 | 数据已经排序时使用 |
| Limit | 限制 | ✅ 好 | 只返回前 N 行 |
🏷️ 金仓特有算子:
- KSHJoin:金仓优化的哈希连接算子,比 PostgreSQL 的 Hash Join 在大数据量下性能更好
- KSort:金仓优化的排序算子,支持并行排序
- KBitmapHeapScan:金仓优化的位图堆扫描算子
⚠️ 2.3 常见异常执行计划识别与诊断表格
| 异常现象 | 问题原因 | 优化方向 |
|---|---|---|
| 大表 Seq Scan(行数 > 10 万) | 缺失索引或索引失效 | 创建合适的索引 |
| Nested Loop 内表是大表 | 连接顺序错误 | 调整连接顺序或使用 Hash Join |
| Hash Join 出现大量 Disk | work_mem 不足 | 调大 work_mem 参数 |
| 预估行数与实际行数相差 10 倍以上 | 统计信息过期 | 手动收集统计信息 |
| 出现 Materialize 节点 | 子查询未优化 | 重构 SQL 为 JOIN |
| Sort 节点出现 Disk: | work_mem 不足 | 调大 work_mem 或减少排序数据量 |
| 出现 Nested Loop+Seq Scan | 内表没有索引 | 为内表连接字段创建索引 |
| 出现多个 Bitmap Heap Scan | 多个索引组合查询 | 创建联合索引 |
🔄 2.4 金仓与 PostgreSQL 执行计划核心差异
这是很多人踩坑的地方!金仓虽然基于 PostgreSQL 内核,但在代价模型和算子行为上有明显差异:
| 维度 | PostgreSQL 14 | 人大金仓 V9.7 | 影响 |
|---|---|---|---|
| Seq Scan 启动代价 | 0.0 | 100.0 | 金仓更倾向于使用索引扫描 |
| 索引扫描选择率 | 基于直方图插值计算 | 固定经验值 0.05 | 金仓对索引的选择更保守 |
| Hash Join 实现 | 动态批处理 + 内存自适应 | 强制依赖预设哈希桶数 | 金仓 Hash Join 对 work_mem 更敏感 |
| 统计信息更新频率 | 自动触发(默认 20% 数据变更) | 相对保守(默认 50% 数据变更) | 金仓更容易出现统计信息过期 |
| 并行查询支持 | 完善 | 有限支持 | 金仓并行查询性能不如 PostgreSQL |
| 子查询优化 | 较好 | 一般 | 金仓子查询更容易被优化成嵌套循环 |
📈 2.5 统计信息异常排查与修复
执行计划不准确 90% 是因为统计信息过期或不准确。金仓提供了多种方式来查看和修复统计信息:
-- 查看表的统计信息
SELECT
relname,
reltuples,
relpages,
last_analyze,
last_autoanalyze
FROM sys_stat_user_tables
WHERE relname = 't_orders';
-- 查看列的统计信息
SELECT
attname,
n_distinct,
most_common_vals,
most_common_freqs,
histogram_bounds
FROM sys_stats
WHERE tablename = 't_orders' AND attname = 'order_date';
-- 收集单表统计信息
ANALYZE t_orders;
-- 收集单表指定列的统计信息
ANALYZE t_orders(order_date, customer_id);
-- 收集全库统计信息
ANALYZE;
-- 收集详细统计信息(适合数据分布不均匀的表)
ANALYZE VERBOSE t_orders;
-- 调整自动统计信息触发阈值(大表建议调小)
ALTER TABLE t_orders SET (autovacuum_analyze_scale_factor = 0.02);
ALTER TABLE t_orders SET (autovacuum_analyze_threshold = 5000);
💡 3. 问题优化:从 SQL 到参数的全维度调优
🗂️ 3.1 索引优化:性价比最高的优化手段
索引是数据库性能的第一道防线,金仓 V9 支持多种索引类型。
3.1.1 覆盖索引避免回表是索引优化的终极目标:
-- 普通索引(需要回表)
CREATE INDEX idx_orders_date ON t_orders(order_date);
-- 覆盖索引(不需要回表,性能提升3-10倍)
CREATE INDEX idx_orders_date_cover ON t_orders(order_date) INCLUDE (id, customer_id, total_amount, status);
-- 验证是否使用了覆盖索引(执行计划中出现Index Only Scan)
EXPLAIN (ANALYZE) SELECT id, customer_id, total_amount FROM t_orders WHERE order_date > '2026-01-01';
3.1.2 联合索引 遵循最左前缀原则,过滤性高的字段放前面:
-- 好:customer_id过滤性高,order_date次之
CREATE INDEX idx_orders_customer_date ON t_orders(customer_id, order_date);
-- 差:order_date过滤性低,放在前面
CREATE INDEX idx_orders_date_customer ON t_orders(order_date, customer_id);
-- 联合覆盖索引
CREATE INDEX idx_orders_customer_date_cover ON t_orders(customer_id, order_date) INCLUDE (total_amount, status);
3.1.3 函数索引当必须在索引列上使用函数时,创建函数索引:
-- 错误:索引失效
SELECT * FROM t_orders WHERE UPPER(customer_name) = 'ZHANGSAN';
-- 正确:创建函数索引
CREATE INDEX idx_orders_customer_name_upper ON t_orders(UPPER(customer_name));
3.1.4 JSONB 索引金仓 V9 对 JSONB 类型支持很好:
-- GIN索引(适合包含查询)
CREATE INDEX idx_orders_extra_gin ON t_orders USING GIN(extra);
-- 特定JSON路径索引(性能更好)
CREATE INDEX idx_orders_extra_status ON t_orders((extra->>'status'));
CREATE INDEX idx_orders_extra_amount ON t_orders(((extra->>'amount')::NUMERIC));
3.1.5 部分索引只对表中部分数据创建索引,节省空间并提高性能:
-- 只对未完成的订单创建索引
CREATE INDEX idx_orders_status_pending ON t_orders(order_date) WHERE status = 0;
✍️ 3.2 SQL 语句重构:从根本上解决性能问题
3.2.1 分页查询优化金仓 V9 中 OFFSET 越大,性能越差:
-- 慢:OFFSET 100000(需要扫描前100000行)
SELECT * FROM t_orders ORDER BY id LIMIT 10 OFFSET 100000;
-- 快:游标式分页(Keyset Pagination)
SELECT * FROM t_orders WHERE id > 100000 ORDER BY id LIMIT 10;
-- 多字段排序的游标式分页
SELECT * FROM t_orders
WHERE (order_date, id) > ('2026-01-01', 100000)
ORDER BY order_date DESC, id DESC
LIMIT 10;
3.2.2 子查询优化尽量将子查询改为 JOIN:
-- 慢:IN子查询(金仓优化器处理不好)
SELECT * FROM t_orders
WHERE customer_id IN (SELECT id FROM t_customers WHERE region = 'Beijing');
-- 快:JOIN
SELECT o.* FROM t_orders o
JOIN t_customers c ON o.customer_id = c.id
WHERE c.region = 'Beijing';
-- 慢:EXISTS子查询
SELECT * FROM t_orders o
WHERE EXISTS (SELECT 1 FROM t_order_items oi WHERE oi.order_id = o.id AND oi.price > 1000);
-- 快:JOIN+DISTINCT
SELECT DISTINCT o.* FROM t_orders o
JOIN t_order_items oi ON o.id = oi.order_id
WHERE oi.price > 1000;
3.2.3 COUNT (*) 优化金仓中 COUNT (*) 会扫描全表,大数据量下很慢:
-- 慢:精确计数(1000万行需要几秒)
SELECT COUNT(*) FROM t_orders;
-- 快:近似计数(误差<5%,毫秒级)
SELECT reltuples::BIGINT FROM sys_class WHERE relname = 't_orders';
-- 业务优化:单独维护计数表
CREATE TABLE t_counter (
table_name VARCHAR(50) PRIMARY KEY,
count BIGINT NOT NULL DEFAULT 0
);
-- 插入时更新计数
INSERT INTO t_orders (...) VALUES (...);
UPDATE t_counter SET count = count + 1 WHERE table_name = 't_orders';
3.2.4 OR 条件优化OR 连接的条件会导致无法使用索引,改用 UNION ALL:
-- 慢:OR条件
SELECT * FROM t_orders WHERE customer_id = 123 OR order_date > '2026-01-01';
-- 快:UNION ALL
SELECT * FROM t_orders WHERE customer_id = 123
UNION ALL
SELECT * FROM t_orders WHERE order_date > '2026-01-01' AND customer_id <> 123;
💪 3.3 HINT 提示:强制优化器执行正确计划
当优化器选择了错误的执行计划时,可以使用 HINT 强制指定。金仓 V9 支持与 Oracle 兼容的 HINT 语法:
-- 强制使用指定索引
SELECT /*+ IndexScan(t_orders idx_orders_date) */ *
FROM t_orders WHERE order_date > '2026-01-01';
-- 强制全表扫描
SELECT /*+ SeqScan(t_orders) */ *
FROM t_orders WHERE order_date > '2026-01-01';
-- 强制使用哈希连接
SELECT /*+ HashJoin(o c) */ *
FROM t_orders o JOIN t_customers c ON o.customer_id = c.id;
-- 强制使用嵌套循环连接
SELECT /*+ NestLoop(o c) */ *
FROM t_orders o JOIN t_customers c ON o.customer_id = c.id;
-- 指定连接顺序
SELECT /*+ Leading((c o)) */ *
FROM t_orders o JOIN t_customers c ON o.customer_id = c.id;
-- 开启并行查询
SELECT /*+ Parallel(t_orders 4) */ *
FROM t_orders WHERE order_date > '2026-01-01';
⚙️ 3.4 关键参数调优:挖掘数据库性能潜力
💻 内存相关参数(根据服务器配置调整):
# 共享缓冲区(建议设置为物理内存的25%-50%)
# 16G内存:4GB
# 32G内存:8GB
# 64G内存:16GB
shared_buffers = 8GB
# 工作内存(每个连接的排序和哈希操作内存)
# 并发连接数少:64MB-256MB
# 并发连接数多:16MB-32MB
work_mem = 64MB
# 维护工作内存(索引创建、VACUUM等操作)
# 建议设置为物理内存的5%-10%
maintenance_work_mem = 2GB
# 有效缓存大小(告诉优化器系统有多少内存可用)
# 建议设置为物理内存的75%
effective_cache_size = 24GB
# 共享内存段大小
max_prepared_transactions = 100
💻 查询优化器参数:
# 关闭嵌套循环(当大表连接时)
# enable_nestloop = off
# 关闭归并连接(大多数场景下Hash Join更好)
# enable_mergejoin = off
# 开启并行查询
max_parallel_workers_per_gather = 4
max_parallel_workers = 8
parallel_setup_cost = 1000
parallel_tuple_cost = 0.1
# 调整代价模型参数
random_page_cost = 1.1 # SSD磁盘建议设置为1.1
seq_page_cost = 1.0
cpu_tuple_cost = 0.01
cpu_index_tuple_cost = 0.005
cpu_operator_cost = 0.0025
💻 自动清理参数:
# 调整自动清理阈值
autovacuum_vacuum_scale_factor = 0.05
autovacuum_analyze_scale_factor = 0.02
autovacuum_vacuum_threshold = 5000
autovacuum_analyze_threshold = 5000
# 增加自动清理工作进程数
autovacuum_max_workers = 4
# 调整自动清理的IO限制
autovacuum_vacuum_cost_delay = 2ms
autovacuum_vacuum_cost_limit = 2000
🏗️ 3.5 表结构优化:从设计层面避免慢 SQL
3.5.1 表分区对于大表(>1000 万行),建议使用分区表:
-- 创建按时间分区的订单表
CREATE TABLE t_orders (
id BIGINT PRIMARY KEY,
customer_id BIGINT NOT NULL,
order_date DATE NOT NULL,
total_amount NUMERIC(10,2) NOT NULL,
status INT NOT NULL DEFAULT 0
) PARTITION BY RANGE (order_date);
-- 创建分区
CREATE TABLE t_orders_202601 PARTITION OF t_orders
FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');
CREATE TABLE t_orders_202602 PARTITION OF t_orders
FOR VALUES FROM ('2026-02-01') TO ('2026-03-01');
3.5.2 字段类型优化
- ✅ 使用最小的数据类型:能用 INT 就不用 BIGINT,能用 VARCHAR (50) 就不用 VARCHAR (255)
- ✅ 避免使用 TEXT 类型存储短字符串
- ✅ 日期时间使用 DATE 或 TIMESTAMP,不要用 VARCHAR
- ✅ 金额使用 NUMERIC 类型,不要用 FLOAT 或 DOUBLE
3.5.3 避免大字段将大字段(如 TEXT、BLOB)单独拆分到子表中:
-- 主表
CREATE TABLE t_orders (
id BIGINT PRIMARY KEY,
customer_id BIGINT NOT NULL,
order_date DATE NOT NULL,
total_amount NUMERIC(10,2) NOT NULL
);
-- 子表存储大字段
CREATE TABLE t_orders_detail (
order_id BIGINT PRIMARY KEY REFERENCES t_orders(id),
remark TEXT,
attachment BYTEA
);
🚫 4. 避坑要点:金仓慢 SQL 优化的 12 个致命陷阱
- ❌ 不要盲目创建索引:索引会降低写入性能,单表索引数建议不超过 5 个
- ❌ 不要忽略统计信息:金仓的自动统计信息更新比较保守,大表变更后要手动 ANALYZE
- ❌ 不要直接照搬 PostgreSQL 的优化经验:两者在代价模型和算子行为上有明显差异
- ❌ 不要在索引列上使用函数或表达式:会导致索引失效,必须使用时创建函数索引
- ❌ ** 不要使用 SELECT ***:只查询需要的字段,尽量使用覆盖索引
- ❌ 不要使用大 OFFSET 分页:超过 10000 行必须使用游标式分页
- ❌ 不要在事务中执行复杂查询:会导致长事务和锁阻塞
- ❌ 不要忽略 work_mem 参数:太小会导致哈希连接和排序操作溢出到磁盘
- ❌ 不要使用 OR 连接多个条件:会导致无法使用索引,改用 UNION ALL
- ❌ 不要忘记定期 VACUUM:金仓的 MVCC 机制会产生死元组,不清理会导致表膨胀
- ❌ 不要使用子查询:金仓优化器对子查询的处理不如 JOIN 好
- ❌ 不要在生产环境使用 EXPLAIN ANALYZE 执行 DML 语句:会真实修改数据
✅ 5. 生产环境验证与监控体系搭建
📊 优化前后对比验证:
-- 优化前记录统计信息
SELECT sys_stat_statements_reset();
-- 执行优化后的SQL
SELECT ...;
-- 查看优化后的性能指标
SELECT query, calls, total_time, mean_time, rows FROM sys_stat_statements WHERE query LIKE '%your_sql%';
📡 慢 SQL 监控体系搭建:
- 📜 配置慢查询日志,每天自动分析并生成报告
- 📊 使用 Prometheus+Grafana 监控数据库性能指标
- 🚨 设置告警阈值,当慢 SQL 数量超过阈值时自动告警
- 🔄 定期(每周)审查慢 SQL 日志,持续优化
🎁 6. 总结与专栏推荐
人大金仓慢 SQL 优化是一个系统性工程,遵循先定位、再分析、后优化的原则。本文提供的方法已经在多个政务信创项目中验证有效,能够解决 90% 以上的慢 SQL 问题。
📌 核心要点回顾:
- ✅ 生产环境必须开启慢查询日志和 sys_stat_statements 插件
- ✅ 分析执行计划重点关注扫描类型、连接方式和行数预估
- ✅ 索引优化是性价比最高的手段,优先使用覆盖索引
- ✅ 金仓与 PostgreSQL 的差异点是最容易踩坑的地方
- ✅ 优化后一定要进行验证和监控
如果这篇文章对你有帮助,欢迎点赞、收藏、关注三连!