我对一个看似简单的概念感到困惑。 Mysql将确定性函数定义为
的函数始终为相同的输入参数生成相同的结果
所以在我的理解中,像
这样的功能CREATE FUNCTION foo (val INT) READS SQL DATA
BEGIN
DECLARE retval INT;
SET retval = (SELECT COUNT(*) FROM table_1 WHERE field_1 = val);
RETURN retval;
END;
不确定(不保证在2次调用函数之间不会发生删除/更新/插入)。同时,我看到许多函数几乎完全相同,即根据查询结果返回值,并声明为DETERMINISTIC
。看起来我错过了一些非常基本的东西。
有人可以澄清这个问题吗?
感谢。
更新
谢谢那些回答(+1);到目前为止,似乎普遍滥用DETERMINISTIC
关键字。我很难相信有这么多人这样做,所以我会等待其他答案。
答案 0 :(得分:14)
来自MySQL 5.0参考:
对例程性质的评估基于创建者的“诚实”:MySQL不会检查声明为DETERMINISTIC的例程是否没有产生非确定性结果的语句。但是,错误地说明例程可能会影响结果或影响性能。将非确定性例程声明为DETERMINISTIC可能会导致优化程序选择错误的执行计划,从而导致意外结果。将确定性例程声明为NONDETERMINISTIC可能会导致不使用可用的优化,从而降低性能。在MySQL 5.0.44之前,DETERMINISTIC特性被接受,但优化器不使用。
所以你有它,你可以将存储的例程标记为DETERMINISTIC
,即使它不是,但它可能会导致意外的结果或性能问题。
答案 1 :(得分:12)
DETERMINISTIC结果不是指在不同时间返回的不同结果集(取决于平均时间内添加的数据)。此外,它是对使用相同数据的不同机器上的结果集的引用。例如,如果您有两台运行包含uuid()或引用服务器变量的函数的计算机,那么这些计算机应被视为NOT DETERMINISTIC。这在复制中很有用,因为函数调用存储在二进制日志(主服务器)中,然后也由从服务器执行。有关详细信息和示例,请参阅http://dev.mysql.com/doc/refman/5.0/en/stored-programs-logging.html
因此,确定使用DETERMINISTIC(99%的时间)是正确的,不应被视为滥用。
答案 2 :(得分:7)
我认为你的日常工作是确定性的。文档不是很清楚,这导致很多人对这个问题非常困惑,实际上更多的是关于复制而不是其他任何问题。
考虑在两个数据库之间设置复制的情况。 master数据库记录所有已执行的存储例程(包括其输入参数),并将此日志发送给从站。从站以相同的顺序使用相同的输入参数执行相同的存储例程。 slave数据库现在是否包含与master数据库相同的数据?如果存储的例程创建GUID并将它们存储在数据库中,则不会,主数据库和从属数据库将不同,复制将被破坏。
DETERMINISTIC标志的主要目的是告诉MySQL在复制日志中包含对此存储例程的调用是否会导致master数据库与复制的从服务器之间存在差异,因此不安全。
当判断DETERMINISTIC标志是否适合存储的例程时,请考虑如下:如果我从两个相同的数据库开始并且我在具有相同输入参数的两个数据库上执行我的例程,我的数据库是否仍然相同?如果他们是,那么我的惯例是确定性的。
如果你声明你的例程是确定性的,那么你的主数据库的副本可能与原始数据库的副本不同,因为MySQL只会将过程调用添加到复制日志,并且在slave上执行过程不会产生相同的结果。
如果您的例程是非确定性的,那么MySQL必须在复制日志中包含受影响的行。如果您将例程声明为非确定性,则不会破坏任何内容,但复制日志将包含所有受影响的行,只需过程调用就足够了,这可能会影响性能。
答案 3 :(得分:2)
你没有遗漏任何东西。此功能是不确定的。声明确定性不会导致数据库融化,但可能会影响性能。 From the MySQL site:“将一个非确定性例程声明为DETERMINISTIC可能会导致优化程序选择错误的执行计划,从而导致意外结果。”但MySQL并没有强制执行或检查你声明的确定性例程是否确实是确定性的--MySQL相信你知道自己在做什么。
答案 4 :(得分:0)
如果您已启用复制或可能有一天使用复制,确定性非常重要。例如,需要使用二进制(基于行)复制导致行更改(更新或插入)的非确定性函数调用,其中可以基于语句复制确定性函数。 在查看上面的SQL示例时,这会变得很有趣,当使用基于语句的复制时,哪些会发生相同(给出相同的结果),并且应该使用在master中获得的结果(基于行)来复制。如果使用适当的锁定执行语句并且可以保证在Slave上以相同的顺序执行,那么它们确实是确定性的。如果Slave使用的锁定/语句顺序(没有并发,按启动顺序对语句进行串行处理)意味着答案可能不同,那么该函数应该是非确定性的。
答案 5 :(得分:0)
我正在浏览答案并决定提供一个更紧凑和更新的答案。
在数据库的相同状态下,给定相同的输入参数,确定性函数总是返回相同的结果。例如 POW,SUBSTR(),UCASE().
在数据库的相同状态下,给定相同的输入参数,非确定性函数不一定总是返回相同的结果。例如 CURDATE()、RAND()、UUID()。
MySQL 8.0 参考手册对此有一些更新
8.2.1.20 函数调用优化
<块引用>MySQL 函数在内部被标记为确定性或非确定性。一个函数是不确定的,如果给定参数的固定值,它可以为不同的调用返回不同的结果。非确定性函数的示例:RAND()、UUID()。如果一个函数被标记为非确定性,则在 WHERE 子句中对每一行(从一个表中选择时)或行组合(从多个表中选择时)评估对它的引用-table join).MySQL 还根据参数类型确定何时评估函数,参数是表列还是常量值。每当该列更改值时,都必须评估以表列作为参数的确定性函数。 非确定性函数可能会影响查询性能。例如,某些优化可能不可用,或者可能需要更多锁定。以下讨论使用 RAND() 但也适用于其他非确定性函数。
此代码示例来自 MySQL 8.0 参考手册。您可以创建表,然后用 49 行填充数据,如 id 列 1 到 49 和 col_a 一些独特的字符串,如“AA”、“AB”、“AC”,直到 49 行。你实际上可以做 15 行,但你需要将 49 更改为 15,这更多的是随机函数的主题。
CREATE TABLE t (id INT NOT NULL PRIMARY KEY, col_a VARCHAR(100));
SELECT * FROM t WHERE id = POW(1,2);
SELECT * FROM t WHERE id = FLOOR(1 + RAND() * 49);
代码将有助于说明这一点,MySQL 8.0 参考手册正在尝试制作。希望这有助于谢谢!