本文介绍了在达梦数据库中使用 SF_INJECT_HINT 为 SQL 语句 注入 HINT 规则,并提供了简单的实践例子。
在达梦数据库中,SQL 注入 HINT 规则可以让我们在不修改原始 SQL 的情况下,强制优化器使用指定的执行策略。在实际生产环境中,这对于解决一些特殊问题非常有用。在索引失效、连接顺序不当等场景下,也可以通过注入 HINT 指定索引或连接方式来快速解决性能瓶颈问题。
HINT(优化器提示)是一种嵌入在 SQL 语句中的指令,用于告诉数据库优化器按照指定的方式生成执行计划。通常,HINT 可以指定访问路径(如使用哪个索引)、连接顺序、并行度、访问策略等。在达梦数据库中,我们可以通过 SF_INJECT_HINT
实现无需改 SQL 源码而向指定查询注入这类 HINT,使该 SQL 语句按照注入的执行策略运行。
无需修改 SQL 语句:使用注入 HINT 规则可以在不改动应用程序或存储过程源码的前提下,改变查询的执行计划。这对于无法轻易重写或部署 SQL 的场景十分关键,如业务系统中存在慢 SQL 而又不能立即上线修改时。
强力控制执行计划:HINT 可用于指定索引、连接类型、排序方式等。例如,通过注入 INDEX(TEST,IDX_TEST_ID)
HINT,即可让优化器强制使用 IDX_TEST_ID
索引扫描,从而显著提升查询性能。再比如上文提到的,将 SORT_FLAG(0)
注入到问题 SQL 中,就可以避免全局排序区的不足。
全局生效,可灵活启用/禁用:注入的 HINT 规则一经创建,就全局有效,对后续所有匹配的会话均生效。且通过 SYSINJECTHINT
视图可以查看所有已注入规则,通过 SF_ALTER_HINT
可以修改规则属性,SF_DEINJECT_HINT
可随时删除规则,使管理更灵活。
💡 提示
语句匹配一个hint之后,后续再重新打hint都不生效,不管是否精准匹配,一个sql只能对应匹配一个规则
执行计划依赖提示:过度依赖 HINT 会导致查询执行计划僵化,失去了成本优化器自动调整的优势。若数据量、统计信息或表结构发生较大变化,原有的 HINT 可能不再合适,需要人工修改甚至去掉。
维护成本高:如果 HINT 写在代码中,那么数据变化或业务变更后往往需要再次改代码,维护成本大。使用注入 HINT 虽然不用改源码,但也需要定期检查和清理不再需要的规则,以免规则过多造成混乱。
全局影响需谨慎:注入 HINT 是全局生效的,如果规则设置不当可能影响其他 SQL。比如精确匹配(exact)和模糊匹配(fuzzy)规则同时存在时,会优先使用精确匹配,且在同一分类下每条 SQL 只能有一个精确匹配规则,否则会报错。因此在注入时必须保证 SQL 文本完全匹配并全局唯一,否则规则可能不生效。
DM.INI 参数 ENABLE_INJECT_HINT 默认为 0,也就是默认不启用注入功能,我们可以通过达梦数据库将其设置为 1,长期启用INJECT_HINT功能。
修改参数后重启数据库生效
我们也可以设置会话参数开启 HINT 注入功能
sp_set_para_value(1, 'ENABLE_INJECT_HINT', 1);
参数 ENABLE_INJECT_HINT=1
表示启用 HINT 注入功能。
在达梦8中搭建一个员工信息表 employee_info
,并使用sqlark生成10w条数据(可选)
CREATE TABLE employee_info (
emp_id INT,
emp_name VARCHAR(100),
dept VARCHAR(50),
salary DECIMAL(10,2)
);
-- 创建员工信息表
表内容如下图
创建索引并收集统计信息
CREATE INDEX idx_emp_id ON employee_info(emp_id); --建立索引
stat 100 on employee_info(emp_id); --收集统计信息
在注入 HINT 前,我们查看默认的执行计划
explain SELECT * FROM employee_info WHERE emp_id > 1;
可以看到默认情况下会进行全表扫描(未使用索引)。
💡 提示
SF_INJECT_HINT拥有多种定义语法,使用不同的参数会匹配不同的定义,这里我们采用定义 2,详细参考:《DM8_SQL语言使用手册》16. 为 SQL 注入 HINT 规则
SF_INJECT_HINT(
'SELECT * FROM employee_info WHERE emp_id > 1;', --sql_text text
'INDEX(EMPLOYEE_INFO,IDX_EMP_ID)', --hint_text text
'INJECT_EMP_INDEX', --name varchar(128)
'强制 employee_info 查询使用 emp_id 索引以优化性能', --description varchar(256)
TRUE, --validate boolean
TRUE --fuzzy boolean
);
sql_text:待注入 HINT 规则的 SQL 语句。
hint_text:待注入的 HINT 规则,必须指定为非 NULL 值。
name:HINT 规则的名称,指定为 NULL 值时系统为其命名。
description:对 HINT 规则的详细描述。
validate:HINT 规则是否生效。TRUE 是;FALSE 否。
fuzzy:指定 SQL 的匹配规则为精准匹配或模糊匹配,该参数值不能为 NULL。该参数类型为 BOOLEAN 时,使用定义
2 或定义 3 的语法,值为 TRUE 时,为模糊匹配;值为 FALSE 时,为精准匹配。
此处缺省参数
need_clear:是否同步清空所有缓存的计划,该参数值不能为 NULL。缺省时默认不清空缓存计划。
再次查看执行计划
explain SELECT * FROM employee_info WHERE emp_id > 1;
此时应看到使用了索引扫描,在部分场景下查询语句速度将提升。
我们还可以通过 SYSINJECTHINT 视图查看 SQL 语句及其注入的 HINT 规则信息。
select * from SYSINJECTHINT;
让指定的 HINT 规则失效
SF_ALTER_HINT('INJECT_NAME', 'STATUS', 'DISABLED');
修改规则名,且不清空缓存的计划
SF_ALTER_HINT('INJECT_NAME', 'NAME', 'INJECT_NAME_NEW', FALSE);
为 SQL 删除已注入的 HINT 规则
SF_DEINJECT_HINT('INJECT_NAME');
为 SQL 删除已注入的 HINT 规则,且不清空缓存的计划:
SF_DEINJECT_HINT('INJECT_NAME', FALSE);
注入 HINT 规则能灵活应对性能突发问题,但也需要谨慎运用。最佳实践是在充分评估数据分布和查询特性的基础上使用,定期审视和清理规则,并结合统计信息收集、物理结构优化等手段共同提升数据库性能。
文章
阅读量
获赞