慢SQL优化最佳实践
应用场景
慢SQL是数据库性能问题的常见原因,执行时间过长的SQL语句会占用大量系统资源,导致业务响应缓慢甚至超时。华为云GaussDB提供了慢SQL优化功能,帮助用户识别和优化慢SQL语句,从而提升数据库性能。
本文通过以下四个案例,介绍如何使用GaussDB慢SQL功能定位并解决常见的性能问题。
前提条件
已购买GaussDB实例,且实例状态为“正常”。
构造数据
- 登录云数据库GaussDB控制台。
- 在“实例管理”页面,选择指定的实例,单击实例名称,进入实例基本信息页面。
- 在左侧导航栏单击“参数管理”,进入“参数”页面。
- 单击“高风险参数”,在搜索框中搜索参数“log_min_duration_statement”,修改参数值为0。
log_min_duration_statement参数用于控制慢SQL的阈值,默认将查询时间超过3s的SQL视为慢SQL。如果业务需要将查询时间低于3s的SQL视为慢SQL,则需要修改该参数。为便于演示SQL性能优化效果,本实践将该参数设置为0。
图1 修改参数值
- 登录数据库,预置表和数据。本文以通过DAS登录数据库的方式为例。
- 在实例基本信息页面右上角,单击“登录”,进入数据管理服务数据库登录界面。
- 在自定义登录页面正确输入对应用户名的密码,单击“测试连接”。测试连接通过后,单击“登录”,进入您的数据库。
- 在顶部菜单栏选择“SQL操作”>“SQL窗口”,打开一个SQL窗口。
- 输入如下SQL,创建用户。
CREATE USER 用户名 WITH CREATEDB PASSWORD '用户密码';
- 使用5.d创建的用户名按照5.a~5.b重新登录数据库。
- 在首页单击“新建数据库”,输入数据库名称,选择DBCOMPATIBILITY,单击“确定”。本实践中选择使用PostgreSQL作为兼容的数据库类型。 图2 新建数据库
- 数据库创建完成后,在顶部菜单栏选择“库管理”,并在库管理界面切换为5.f创建的数据库,单击“新建Schema”,输入Schema名称,单击“确定”。 图3 新建Schema
- 在顶部菜单栏选择“SQL操作”>“SQL窗口”,打开一个SQL窗口,在左边切换库名为5.f创建的数据库,切换Schema为5.g创建的Schema。
- 执行如下SQL,预置表和数据以模拟业务场景。
- 创建用户表和模拟数据。
-- 1. 用户表 (users) CREATE TABLE IF NOT EXISTS users ( id BIGSERIAL PRIMARY KEY, username VARCHAR(50) NOT NULL, email VARCHAR(100) NOT NULL, phone VARCHAR(20), status VARCHAR(20) DEFAULT 'active', create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); COMMENT ON TABLE users IS '用户表'; COMMENT ON COLUMN users.id IS '用户ID,主键'; COMMENT ON COLUMN users.username IS '用户名'; COMMENT ON COLUMN users.email IS '邮箱地址'; COMMENT ON COLUMN users.phone IS '手机号码'; COMMENT ON COLUMN users.status IS '用户状态:active/inactive/banned'; COMMENT ON COLUMN users.create_time IS '创建时间'; COMMENT ON COLUMN users.updated_time IS '更新时间'; -- 生成 users 表的模拟数据 INSERT INTO users (username, email, phone, status, create_time) SELECT 'user_' || i AS username, 'user_' || i || '@example.com' AS email, '138' || LPAD(i::TEXT, 8, '0') AS phone, CASE WHEN i % 100 = 0 THEN 'banned' WHEN i % 10 = 0 THEN 'inactive' ELSE 'active' END AS status, NOW() - (FLOOR(RANDOM() * 365) || ' days')::INTERVAL FROM generate_series(1, 3000000) AS t(i); - 创建订单表和模拟数据。
-- 2. 订单表 (orders) CREATE TABLE IF NOT EXISTS orders ( id BIGSERIAL PRIMARY KEY, order_no VARCHAR(50) NOT NULL UNIQUE, user_id BIGINT NOT NULL, customer_name VARCHAR(100), total_amount DECIMAL(12, 2) DEFAULT 0.00, status VARCHAR(20) DEFAULT 'pending', payment_status VARCHAR(20) DEFAULT 'unpaid', create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); COMMENT ON TABLE orders IS '订单表'; COMMENT ON COLUMN orders.id IS '订单ID,主键'; COMMENT ON COLUMN orders.order_no IS '订单编号'; COMMENT ON COLUMN orders.user_id IS '用户ID'; COMMENT ON COLUMN orders.customer_name IS '客户姓名'; COMMENT ON COLUMN orders.total_amount IS '订单总金额'; COMMENT ON COLUMN orders.status IS '订单状态:pending/completed/cancelled/refunded'; COMMENT ON COLUMN orders.payment_status IS '支付状态:paid/unpaid'; COMMENT ON COLUMN orders.create_time IS '创建时间'; COMMENT ON COLUMN orders.updated_time IS '更新时间'; -- 生成 orders 表的模拟数据 INSERT INTO orders (order_no, user_id, customer_name, total_amount, status, payment_status, create_time) SELECT 'ORD' || LPAD(i::TEXT, 10, '0') AS order_no, (RANDOM() * 3000000)::BIGINT + 1 AS user_id, 'customer_' || ((RANDOM() * 3000000)::BIGINT + 1) AS customer_name, ROUND((RANDOM() * 10000 + 100)::DECIMAL(12, 2), 2) AS total_amount, CASE WHEN i % 100 = 0 THEN 'refunded' WHEN i % 7 = 0 THEN 'cancelled' WHEN i % 5 = 0 THEN 'completed' ELSE 'pending' END AS status, CASE WHEN (CASE WHEN i % 100 = 0 THEN 'refunded' WHEN i % 7 = 0 THEN 'cancelled' WHEN i % 5 = 0 THEN 'completed' ELSE 'pending' END) = 'completed' THEN 'paid' ELSE 'unpaid' END AS payment_status, NOW() - (FLOOR(RANDOM() * 365) || ' days')::INTERVAL AS create_time FROM generate_series(1, 5000000) AS t(i); - 创建订单明细表和模拟数据。
-- 3. 订单明细表 (order_items) CREATE TABLE IF NOT EXISTS order_items ( id BIGSERIAL PRIMARY KEY, order_id BIGINT NOT NULL, product_id BIGINT, product_name VARCHAR(200), quantity INT DEFAULT 1, unit_price DECIMAL(10, 2), total_price DECIMAL(12, 2), create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); COMMENT ON TABLE order_items IS '订单明细表'; COMMENT ON COLUMN order_items.id IS '订单明细ID,主键'; COMMENT ON COLUMN order_items.order_id IS '订单ID'; COMMENT ON COLUMN order_items.product_id IS '商品ID'; COMMENT ON COLUMN order_items.product_name IS '商品名称'; COMMENT ON COLUMN order_items.quantity IS '购买数量'; COMMENT ON COLUMN order_items.unit_price IS '单价'; COMMENT ON COLUMN order_items.total_price IS '总价'; COMMENT ON COLUMN order_items.create_time IS '创建时间'; COMMENT ON COLUMN order_items.updated_time IS '更新时间'; -- 生成 order_items 表的模拟数据 INSERT INTO order_items (order_id, product_id, product_name, quantity, unit_price, total_price) SELECT order_id, product_id, product_name, quantity, unit_price, ROUND(unit_price * quantity, 2) AS total_price FROM ( SELECT (RANDOM() * 5000000)::BIGINT + 1 AS order_id, (RANDOM() * 1000000)::INTEGER + 1 AS product_id, 'product_' || ((RANDOM() * 100)::INTEGER + 1) AS product_name, (RANDOM() * 5)::INTEGER + 1 AS quantity, ROUND((RANDOM() * 500 + 10)::DECIMAL(10, 2), 2) AS unit_price FROM generate_series(1, 2000000) ) AS t; - 创建商品表和模拟数据。
-- 4. 商品表 (products) CREATE TABLE IF NOT EXISTS products ( id BIGSERIAL PRIMARY KEY, product_name VARCHAR(200) NOT NULL, category VARCHAR(50), price DECIMAL(10, 2) DEFAULT 0.00, sales INT DEFAULT 0, stock INT DEFAULT 0, status VARCHAR(20) DEFAULT 'available', create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); COMMENT ON TABLE products IS '商品表'; COMMENT ON COLUMN products.id IS '商品ID,主键'; COMMENT ON COLUMN products.product_name IS '商品名称'; COMMENT ON COLUMN products.category IS '商品分类'; COMMENT ON COLUMN products.price IS '商品价格'; COMMENT ON COLUMN products.sales IS '销售数量'; COMMENT ON COLUMN products.stock IS '库存数量'; COMMENT ON COLUMN products.status IS '商品状态'; COMMENT ON COLUMN products.create_time IS '创建时间'; COMMENT ON COLUMN products.updated_time IS '更新时间'; -- 生成 products 表的模拟数据 INSERT INTO products (product_name, category, price, sales, stock, status) SELECT CASE WHEN category_val = 'Electronics' THEN CASE WHEN i % 10 = 0 THEN '手机品牌A' || i WHEN i % 10 = 1 THEN '手机品牌B' || i WHEN i % 10 = 2 THEN '手机品牌C' || i ELSE '电子产品_' || i END ELSE category_val || '_product_' || i END AS product_name, category_val, ROUND((RANDOM() * 5000 + 50)::DECIMAL(10, 2), 2) AS price, (RANDOM() * 10000)::INTEGER AS sales, (RANDOM() * 5000)::INTEGER AS stock, 'available' AS status FROM ( SELECT i, CASE WHEN i % 4 = 0 THEN 'Electronics' WHEN i % 4 = 1 THEN 'Clothing' WHEN i % 4 = 2 THEN 'Food' ELSE 'Home' END AS category_val FROM generate_series(1, 5000000) AS t(i) ) temp;
- 创建用户表和模拟数据。
典型案例
问题场景
在电商系统中,用户频繁查询近期待处理的订单,导致订单表数据量达到数百万行,查询响应缓慢,进而影响订单处理效率。
问题SQL
SELECT order_no, total_amount, customer_name FROM orders WHERE create_time >= '2025-05-01' AND status = 'pending' ORDER BY create_time DESC LIMIT 100;
问题分析
- 登录云数据库GaussDB控制台。
- 在“实例管理”页面,选择指定的实例,单击实例的名称,进入实例详情页面。
- 单击“诊断优化 > SQL诊断”。
- 在“慢SQL”页签,选择指定节点、时间范围、SQL文本,配置SQL阈值,单击“查询”按钮,获取慢SQL列表。
- 用户可自行选择时间区间,最大时间跨度为一天,可按照节点维度展示数据。
- 过滤系统用户:开启后,将会跳过系统用户所执行的SQL记录。默认开启。
- 用户可以选择"OR"或者"AND"查询,筛选框中可以填入需要组合过滤的多个SQL文本(不超过5个)。本示例中选择"OR"查询,SQL文本为 orders。
- 支持自定义慢SQL阈值筛选。本示例中配置为1000ms。
图4 查询慢SQL
- 在慢SQL列表中,查看SQL计划(执行计划)和平均执行时间。平均时间为2.0286s,执行计划如下。
[Parameterized] Datanode Name: dn_6001_6002_6003 Limit (cost=257646.18..257646.30 rows=50 width=44) -> Sort (cost=257646.18..266236.99 rows=50 width=44) Sort Key: create_time DESC -> Seq Scan on orders (cost=0.00..143494.00 rows=3436323 width=44) Filter: (((status)::text = '***'::text) AND (create_time >= '***'::timestamp without time zone)), (Expression Flatten Optimized)执行计划中,Seq Scan on orders表示对整张orders表进行全表顺序扫描,遍历3436323行数据,每一行数据读取出来后,通过Filter过滤status和create_time两个条件,以获取符合条件的数据行。由于是扫描全部数据行后才进行过滤和排序,因此效率极低。
优化方案
通过对执行计划的分析,该SQL扫描了整张orders表,并过滤出符合status和create_time两个条件的数据行,因此需要对这两个条件字段添加联合索引。注意联合索引字段的顺序,顺序错误可能会引起索引失效。
CREATE INDEX idx_orders_status_create_time ON orders(status, create_time DESC);
验证效果
- 重新执行SQL。
SELECT order_no, total_amount, customer_name FROM orders WHERE create_time >= '2025-05-01' AND status = 'pending' ORDER BY create_time DESC LIMIT 100;
- 查看执行计划和时间。执行时间为0.001s,执行计划如下。 图5 查看SQL计划和执行时间
执行计划:[Parameterized] Datanode Name: dn_6001_6002_6003 Limit (cost=0.00..2.32 rows=50 width=44) -> Index Scan using idx_orders_status_create_time on orders (cost=0.00..159532.64 rows=50 width=44) Index Cond: (((status)::text = '***'::text) AND (create_time >= '***'::timestamp without time zone))从执行计划中可以看到,SQL命令采用了索引扫描:Index Scan,且与优化前执行计划中使用Filter不同,优化后的执行计划采用Index Cond,过滤逻辑直接在索引层面完成,无需将整行数据取出来后再Filter,因此执行效率显著提升。
- 对比优化前后指标。可以看到执行速度得到了显著提升。
指标
优化前
优化后
提升
扫描方式
Seq Scan
Index Scan
-
平均执行时间
2.0286s
0.001s
2028.6 倍
问题场景
在电商平台中,用户查看自己的历史订单列表时,需要展示订单基本信息以及最后一条订单明细的商品名称。数据库已为orders表创建了id主键索引,但未创建user_id索引。查询过程中,优化器选择按主键索引进行反向扫描(期望利用ORDER BY id DESC的有序性),导致需要大量Filter过滤user_id条件,页面加载缓慢,影响用户体验。
问题SQL
SELECT o.order_no, o.total_amount, o.customer_name, o.status, o.create_time,
(SELECT oi.product_name FROM order_items oi WHERE oi.order_id = o.id AND ROWNUM = 1 ORDER BY oi.id DESC) as last_product
FROM orders o
WHERE o.user_id = 12345
ORDER BY o.id DESC
LIMIT 20; 问题分析
- 登录云数据库GaussDB控制台。
- 在“实例管理”页面,选择指定的实例,单击实例的名称,进入实例详情页面。
- 单击“诊断优化 > SQL诊断”。
- 在“慢SQL”页签,选择指定节点、时间范围、SQL文本,配置SQL阈值,单击“查询”按钮,获取慢SQL列表。
- 用户可自行选择时间区间,最大时间跨度为一天,可按照节点维度展示数据。
- 过滤系统用户:开启后,将会跳过系统用户所执行的SQL记录。默认开启。
- 用户可以选择"OR"或者"AND"查询,筛选框中可以填入需要组合过滤的多个SQL文本(不超过5个)。本示例中选择"OR"查询,SQL文本为 orders。
- 支持自定义慢SQL阈值筛选。本示例中配置为1000ms。
图6 查询慢SQL
- 在慢SQL列表中,查看SQL计划(执行计划)和平均执行时间。平均时间为2.1985s,执行计划如下。
[Parameterized] Datanode Name: dn_6001_6002_6003 Limit (cost=176666.67..176666.67 rows=1 width=61) -> Index Scan Backward using orders_pkey on orders o (cost=0.00..176666.67 rows=1 width=61) Filter: (user_id = $2) SubPlan 1 -> Sort (cost=0.01..0.02 rows=1 width=18) Sort Key: oi.id DESC -> Rownum (cost=0.00..0.00 rows=1 width=18) StopKey: (ROWNUM = $1) -> Seq Scan on order_items oi (cost=0.00..47983.00 rows=1 width=18) Filter: (order_id = o.id), (Expression Flatten Optimized)从执行计划可以看出,优化器选择按主键索引反向扫描(Index Scan Backward using orders_pkey),期望利用id DESC有序性避免排序。但WHERE条件是user_id,需要回表+Filter过滤,实际扫描500万行才找到几条匹配记录,效率极低。子查询中Seq Scan on order_items表明对order_items进行了全表扫描,过滤条件 Filter: order_id = o.id,加剧了SQL执行开销。
优化方案
方案一:SQL Patch强制全表扫描
优化器错误选择主键索引反向扫描,导致大量Filter过滤。通过SQL Patch强制使用全表扫描做临时规避,顺序扫描效率反而会更高。
- 登录云数据库GaussDB控制台。
- 在“实例管理”页面,选择指定的实例,单击实例的名称,进入实例详情页面。
- 单击“诊断优化 > SQL诊断”。
- 在“慢SQL”页签,选择指定节点,时间范围,组合过滤SQL文本、归一化SQL ID以及其他扩展字段,单击“查询”按钮,获取慢SQL列表。
- 在慢SQL列表,选择对应慢SQL,在“操作”列单击“更多 > SQL Patch”,显示“SQL PATCH详情”页面。 图7 创建SQL PATCH
- 选择PATCH类型为HINT,输入PATCH名称和PATCH内容,单击“创建”。 图8 创建SQL PATCH
- 验证效果。
- 重新执行如下SQL。
SELECT o.order_no, o.total_amount, o.customer_name, o.status, o.create_time, (SELECT oi.product_name FROM order_items oi WHERE oi.order_id = o.id AND ROWNUM = 1 ORDER BY oi.id DESC) as last_product FROM orders o WHERE o.user_id = 12345 ORDER BY o.id DESC LIMIT 20; - 查看执行计划和时间。执行时间为1.0434s,执行计划如下。 图9 查看SQL计划和执行时间
执行计划:
[Parameterized] Datanode Name: dn_6001_6002_6003 Limit (cost=178977.03..178977.03 rows=1 width=61) -> Sort (cost=178977.03..178977.03 rows=1 width=61) Sort Key: o.id DESC -> Seq Scan on orders o (cost=0.00..178977.02 rows=1 width=61) Filter: (user_id = '***'), (Expression Flatten Optimized) SubPlan 1 -> Sort (cost=47983.01..47983.01 rows=1 width=18) Sort Key: oi.id DESC -> Rownum (cost=0.00..47983.00 rows=1 width=18) StopKey: (ROWNUM = '***') -> Seq Scan on order_items oi (cost=0.00..47983.00 rows=1 width=18) Filter: (order_id = o.id), (Expression Flatten Optimized)执行计划强制使用全表扫描(Seq Scan),避免了大量回表和Filter过滤操作。在高负载场景下,暂时无法创建更优索引时,可使用SQL Patch作为临时调优手段。
- 重新执行如下SQL。
- 对比优化前后指标。可以看到执行速度得到了有效提升。
指标
优化前
优化后
提升
扫描方式
Index Scan Backward
Seq Scan
-
执行时间
2.1985s
1.0434s
2.11倍
方案二:创建最优复合索引
- orders表没有user_id、id相关索引,导致必须全表扫描,再筛选、排序。
- order_items没有order_id + id + product_name索引,子查询只能用主键反向扫描+大量回表,无法快速定位当前订单的商品。
在业务非高负载期间,可以通过分析执行计划来创建更优的索引。
- 创建如下复合索引。
-- 1. 解决 orders 全表扫描 + 额外排序 CREATE INDEX idx_orders_user_id_id ON orders(user_id, id DESC); -- 2. 解决子查询慢,消除低效扫描 CREATE INDEX idx_order_items_order_id_id_product_name ON order_items(order_id, id DESC, product_name);
- 对于指定的慢SQL,检查是否存在已创建的SQL Patch,若存在需关闭或删除SQL Patch。
- 登录云数据库GaussDB控制台。
- 在“实例管理”页面,选择指定的实例,单击实例的名称,进入实例详情页面。
- 单击“诊断优化 > SQL诊断”。
- 在“慢SQL”页签,选择指定节点,时间范围,组合过滤SQL文本、归一化SQL ID以及其他扩展字段,单击查询按钮,获取慢SQL列表。
- 在慢SQL列表,选择对应慢SQL,在“操作”列单击“更多 > SQL Patch”。 图10 查看SQL Patch详情
- 如果已创建SQL PATCH,单击
按钮关闭SQL Patch,或单击“删除”按钮直接删除SQL Patch。 图11 关闭/删除SQL Patch
- 验证效果。
- 重新执行SQL语句。
SELECT o.order_no, o.total_amount, o.customer_name, o.status, o.create_time, (SELECT oi.product_name FROM order_items oi WHERE oi.order_id = o.id AND ROWNUM = 1 ORDER BY oi.id DESC) as last_product FROM orders o WHERE o.user_id = 12345 ORDER BY o.id DESC LIMIT 20; - 执行完后得到如下的SQL详情。执行时间为0.001s,执行计划如下。 图12 查看SQL计划和执行时间
执行计划:
[Parameterized] Datanode Name: dn_6001_6002_6003 Limit (cost=0.00..3.54 rows=1 width=61) -> Index Scan using idx_orders_user_id_id on orders o (cost=0.00..3.54 rows=1 width=61) Index Cond: (user_id = '***') SubPlan 1 -> Rownum (cost=0.00..1.27 rows=1 width=18) StopKey: (ROWNUM = '***') -> Index Only Scan using idx_order_items_order_id_id_product_name on order_items oi (cost=0.00..1.27 rows=1 width=18) Index Cond: (order_id = o.id)总耗时从2.1985s降至0.001s,执行效率提升2198.5倍。orders表从主键全扫描+Filter改为索引查找,order_items子查询实现了仅索引扫描(Index Only Scan), 避免了回表操作和全表顺序扫描,大幅降低了随机 I/O 和总查询成本,远超未添加索引时的查询效率。
- 重新执行SQL语句。
问题场景
电商平台进行大促活动期间,某爆款商品页面被大量用户频繁访问。后台持续执行查询商品库存的SQL,每秒执行次数超过10000次,导致数据库CPU使用率飙升至90%+。该SQL属于并发型高危慢SQL,高频执行会导致数据库资源被大量占用,从而引起其他正常业务(如订单提交、支付)响应缓慢甚至超时。
问题SQL
SELECT id, product_name, price, stock, status FROM products WHERE id = 991015 and status = 'available' and stock > 0;
问题表现
| 指标 | 正常时期 | 大促时期 |
|---|---|---|
| SQL执行频率 | 50次/秒 | 10000次/秒 |
| CPU使用率 | 30% | 90%+ |
| 其他业务响应 | 正常 | 超时 |
优化方案
- 登录云数据库GaussDB控制台。
- 在“实例管理”页面,选择指定的实例,单击实例的名称,进入实例详情页面。
- 单击“诊断优化 > SQL诊断”。
- 在“慢SQL”页签,选择指定节点、时间范围、SQL文本,配置SQL阈值,单击“查询”按钮,获取慢SQL列表。
- 在慢SQL列表,选中执行的SQL,单击“创建SQL限流任务”。 图13 创建SQL限流任务
- 填写限流任务名称,按当前的实际情况填写并发数,选择限流生效时间,输入“YES”,随后单击“创建”下发SQL限流任务。 图14 创建SQL限流任务
验证效果
本案例中将并发数设置为200,同一时刻该SQL最多同时执行200次,超过限制时将报错拦截,以防止在业务高峰期时,并发型高危慢SQL导致其他正常业务(如订单提交、支付)响应缓慢甚至超时。
【执行SQL:(1)】 SELECT id, product_name, price, stock, status FROM products WHERE id = 991015 and status = 'available' and stock > 0; 执行失败,失败原因:[***:52656/***:8635] ERROR: The workload rule takes effect and this request will be cancelled. rule_id: 1, rule_name: "sql-limit"
问题场景
某电商后台管理系统新上线了“订单统计导出”功能,用户选择“导出全部订单”时,SQL缺少分页限制,一次性查询几百万条订单数据。该SQL首次执行时导致数据库内存耗尽(OOM),严重时可能触发实例宕机,影响全部业务。运维人员需要立即拦截该高危慢SQL,防止再次执行,但业务代码修复需要走发布流程(预计2小时),期间需用ABORT补丁紧急止损。
问题SQL
SELECT u.username, u.email, o.order_no, o.total_amount, o.customer_name, o.status, o.create_time FROM orders o LEFT JOIN users u ON o.user_id = u.id WHERE o.create_time >= '2025-01-01' ORDER BY o.create_time DESC; -- 缺少 LIMIT,一次性返回几百万条数据
问题表现
| 指标 | 执行前 | 执行时 | 执行后 |
|---|---|---|---|
| 数据库内存 | 正常 | 耗尽(OOM) | 严重时实例宕机 |
| 业务影响 | 无 | 业务中断/执行慢 | 全部业务无法执行 |
| 故障类型 | 无 | 内存溢出崩溃 | 内存溢出崩溃 |
优化方案
通过GaussDB服务SQL Patch的ABORT功能,禁止该SQL执行,并提前报错返回,从而避免再次触发崩溃。
- 登录云数据库GaussDB控制台。
- 在“实例管理”页面,选择指定的实例,单击实例的名称,进入实例详情页面。
- 单击“诊断优化 > SQL诊断”。
- 在“慢SQL”页签,用户可按需选择指定节点,时间范围,SQL文本,SQL阈值,单击查询按钮,获取慢SQL列表。
- 在慢SQL列表找到执行的SQL,下拉更多,找到SQL Patch。 图15 创建SQL Patch
- 选择PATCH类型为ABORT,输入PATCH名称,点击创建。 图16 配置SQL PATCH
验证效果
重新执行SQL语句,会被限制住:
【执行SQL:(1)】 SELECT u.username, u.email, o.order_no, o.total_amount, o.customer_name, o.status, o.create_time FROM orders o LEFT JOIN users u ON o.user_id = u.id WHERE o.create_time >= '2025-01-01' ORDER BY o.create_time DESC; 执行失败,失败原因:[***:52656/***:8635] ERROR: Statement 1858775530 canceled by abort patch test2
禁止该SQL执行,提前报错返回,避免再次触发崩溃。