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

collat​​ion_connection:utf8mb4_0900_ai_ci

数据库整理:utf8mb4_unicode_ci

系统简单,运行良好;后端团队不断调用此函数来检索数据库中定义的这些“静态”值。

问题是事件,虽然函数是 DETERMINISTIC,但它似乎显着减慢了调用中嵌入函数的复杂 SQL 调用。我对此感到惊讶,因为我认为 DETERMINISTIC 函数的处理方式会有所不同;就好像它们是一个静态值(在 SQL 调用中)。

所以问题:

    这是在两个平台(数据库和后端)之间共享静态值的最佳方法吗? 为什么 mysql 引擎不将调用视为 DETERMINISTIC 并且只解析一次值,而不是通过每个迭代调用似乎是什么?

谢谢!

【问题讨论】:

var_valueglobal_custom_setting 中有多少行? 请提供SHOW CREATE FUNCTION get_gcs;我对创建时分配的排序规则感兴趣。还有什么排序规则在通话期间有效? 谢谢@RickJames。表中只有 745 行。我将 OP 更新为 SHOW CREATE FUNCTION。不管这些设置如何,提供对表数据的访问的函数是否是与外部应用程序(如 Node.js 后端)交叉共享静态“值”的最佳方式? 一共有三样,比如: character_set_client: utf8 collat​​ion_connection: utf8_general_ci Database Collat​​ion: 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))

这些似乎适用于函数中的字符串:

函数的参数:collat​​ion_database 函数中的文字字符串:collat​​ion_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

使用原始文件的共享外部包?

数据库与SQL语言基础(内含MySQL压缩包环境安装详细步骤)

mysql存储过程。。

mysql在导入外部sql文件时出现了错误,请问该怎么解决?