MySQL - 与外部应用程序共享 SQL 中使用的静态值的最有效方法?
Posted
技术标签:
【中文标题】MySQL - 与外部应用程序共享 SQL 中使用的静态值的最有效方法?【英文标题】:MySQL - Most efficient way to share Static Values used in SQL with external applications? 【发布时间】:2022-01-06 16:16:39 【问题描述】:我们有一个大型数据库系统,它有一个静态值表,通过一个简单的函数调用与外部应用程序共享这些值。目的是在数据库系统和后端系统之间进行精确的同步匹配,以便两个系统可以根据自定义名称引用相同的值。
实现相当简单。该表仅存储系统和字段的文本名称 (VARCHAR64) 以及一个值 (SMALLINT)。这本质上是我们与后端 javascript 团队共享的硬编码值的查找表,因此数据可能是:
CUSTOMER_RECORD_STATE, ACTIVE, 1
CUSTOMER_RECORD_STATE, INACTIVE, 2
CUSTOMER_RECORD_STATE, DELETED, 3
该函数用于查找这些值,如下所示。
SELECT tab.name_first FROM our_customers ourc WHERE ourc.record_state = get_gcs('CUSTOMER_RECORD_STATE','ACTIVE');
这是函数的创建代码:
SET NAMES 'utf8mb4';
DELIMITER $$
CREATE
DEFINER = 'root'@'localhost'
FUNCTION get_gcs (in_system varchar(128), in_field varchar(128), in_value varchar(128))
RETURNS smallint(6)
DETERMINISTIC
READS SQL DATA
BEGIN
DECLARE var_value smallint DEFAULT -1;
DECLARE out_result_value smallint;
DECLARE debug_definition json DEFAULT JSON_OBJECT('function', 'get_gcs');
DECLARE debug_details json DEFAULT JSON_OBJECT('source', debug_definition, 'parameters',
JSON_OBJECT('in_system', in_system, 'in_field', in_field, 'in_value', in_value));
SELECT
custom_value INTO var_value
FROM global_custom_setting
WHERE in_system = name_system
AND in_field = name_field
AND in_value = name_value;
RETURN var_value;
END
$$
DELIMITER ;
character_set_client:utf8mb4
collation_connection:utf8mb4_0900_ai_ci
数据库整理:utf8mb4_unicode_ci
系统简单,运行良好;后端团队不断调用此函数来检索数据库中定义的这些“静态”值。
问题是事件,虽然函数是 DETERMINISTIC,但它似乎显着减慢了调用中嵌入函数的复杂 SQL 调用。我对此感到惊讶,因为我认为 DETERMINISTIC 函数的处理方式会有所不同;就好像它们是一个静态值(在 SQL 调用中)。
所以问题:
-
这是在两个平台(数据库和后端)之间共享静态值的最佳方法吗?
为什么 mysql 引擎不将调用视为 DETERMINISTIC 并且只解析一次值,而不是通过每个迭代调用似乎是什么?
谢谢!
【问题讨论】:
var_value
和global_custom_setting
中有多少行?
请提供SHOW CREATE FUNCTION get_gcs
;我对创建时分配的排序规则感兴趣。还有什么排序规则在通话期间有效?
谢谢@RickJames。表中只有 745 行。我将 OP 更新为 SHOW CREATE FUNCTION。不管这些设置如何,提供对表数据的访问的函数是否是与外部应用程序(如 Node.js 后端)交叉共享静态“值”的最佳方式?
一共有三样,比如: character_set_client: utf8 collation_connection: utf8_general_ci Database Collation: utf8mb4_unicode_520_ci
@RickJames 已更新。
【参考方案1】:
应该(并且可以)修复的一件事——UCASE(in_system) = UCASE(name_system)
如果两列的字符集和排序规则相同,则不需要UCASE
。此外,排序规则必须是 ..._ci
才能“区分大小写”。
任何函数调用都会隐藏在任何索引中使用的列。因此函数中的WHERE
子句不能使用索引。
global_custom_setting
需要INDEX(name_system, name_field, name_value)
。 (列可以按任何顺序排列。)
显然,FUNCTION
使用的是collation_connection: utf8mb4_0900_ai_ci
,这可能与连接的排序规则不同。
建议在删除UPPERs
并添加INDEX
后尝试此操作:
SET NAMES utf8mb4, COLLATE utf8mb4_0900_ai_ci;
DROP FUNCTION get_gcs;
DELIMITER $$
((recreate the function))
DELIMITER ;
((similarly for get_gss))
这些似乎适用于函数中的字符串:
函数的参数:collation_database 函数中的文字字符串:collation_connection 表格列:列的排序规则函数永远不可能是 sargable(据我所知)。一些表达式涉及“隐式”函数,例如数据类型转换和排序规则不匹配。
【讨论】:
谢谢瑞克!该代码是近 2 年前编写的,所以我完全忘记了 UCASE() 的不必要使用。删除它确实加快了速度,但我的理解是核心问题是 MySQL 是针对查询中的每一行调用该函数,而不是注册静态值的单个调用。例如,如果我用硬编码的数字甚至变量替换函数调用,它的速度会快 5-10 倍。有什么办法——根本没有——让 MySQL 使用函数结果作为单一调用而不是递归?我以为 DETERMINISTIC 会这样做,但显然不是...... @Floobinator - 我还没有弄清楚你的DETERMINISTIC
问题。至于函数调用——那些明显是常量的函数会在在运行查询之前进行评估;例如,UPPER("abcd")
。但是那些不能预先评估的(例如,UPPER(column)
)会遇到“sargable”问题(参见***)。
那些只从表中选择一个值的函数呢?如果 UCASE() 封装被移除,那么该函数是 Sargable 还是非 Sargable 因为它是一个查找?
不一致的排序规则实际上是一个函数调用。这就是我在回答正文中所说的内容。
明白了;但假设排序规则没问题;编译器可以将根据传递的参数从表中获取值的 DETERMINISTIC 函数视为 Sargable 吗?我认为这是问题的根源。我认为函数中的 SELECT 存在可能会使函数的任何 Sargable 状态无效......我的目标是简单地将函数调用视为 1 次 CONST 引用;仅此而已。以上是关于MySQL - 与外部应用程序共享 SQL 中使用的静态值的最有效方法?的主要内容,如果未能解决你的问题,请参考以下文章
同一台机器上的 SQL Server Express,还是外部共享的 SQL Server Standard? (对于 ASP.NET 站点)
如何为 Spring Boot 应用程序外部化 data.sql