38

我对一个看似简单的概念感到困惑。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;

不是确定性的(不能保证在两次调用函数之间不会发生删除/更新/插入)。同时,我看到许多功能几乎相同,即根据查询结果返回值,并声明为DETERMINISTIC. 看起来我错过了一些非常基本的东西。

谁能澄清这个问题?

谢谢。

更新 感谢那些回答(+1)的人;DETERMINISTIC到目前为止,关键字的滥用似乎很普遍。对我来说仍然很难相信有这么多人这样做,所以我会等待其他答案。

4

6 回答 6

19

来自 MySQL 5.0 参考:

对例程性质的评估基于创建者的“诚实”:MySQL 不检查声明为 DETERMINISTIC 的例程是否没有产生不确定结果的语句。但是,错误地声明例程可能会影响结果或影响性能。将非确定性例程声明为 DETERMINISTIC 可能会导致优化器做出不正确的执行计划选择,从而导致意外结果。将确定性例程声明为 NONDETERMINISTIC 可能会导致不使用可用的优化,从而降低性能。在 MySQL 5.0.44 之前,DETERMINISTIC 特性被接受,但不被优化器使用。

所以你有了它,你可以标记一个存储的例程,DETERMINISTIC即使它不是,但它可能会导致意想不到的结果或性能问题。

于 2011-10-30T17:50:03.563 回答
15

确定性结果不是指在不同时间返回的不同结果集(取决于同时添加的数据)。此外,它是对使用相同数据的不同机器上的结果集的引用。例如,如果您有 2 台机器运行一个函数,包括 uuid() 或引用服务器变量,那么这些应该被视为 NOT DETERMINISTIC。这在复制中很有用,因为函数调用存储在二进制日志(主)中,然后也由从属执行。有关详细信息和示例,请参阅http://dev.mysql.com/doc/refman/5.0/en/stored-programs-logging.html

因此,DETERMINISTIC 的使用(99% 的时间)是正确的,不应被视为误用。

于 2012-06-18T18:45:28.467 回答
11

我认为你的例程是确定性的。文档不是很清楚,这导致很多人对这个问题感到非常困惑,这实际上更多的是关于复制而不是其他任何事情。

考虑在两个数据库之间设置复制的情况。主数据库保留所有已执行的存储例程的日志,包括它们的输入参数,并将此日志发送到从属。从站使用相同的输入参数以相同的顺序执行相同的存储例程。从属数据库现在是否包含与主数据库相同的数据?如果存储的例程创建 GUID 并将它们存储在数据库中,那么不,主数据库和从数据库将不同,并且复制将被破坏。

DETERMINISTIC 标志的主要目的是告诉 MySQL 是否在复制日志中包含对该存储例程的调用会导致主数据库和复制的从属数据库之间存在差异,因此是不安全的。

在决定 DETERMINISTIC 标志是否适用于存储例程时,可以这样想:如果我从两个相同的数据库开始,并且我在两个数据库上执行我的例程并使用相同的输入参数,我的数据库是否仍然相同?如果是,那么我的例程就是确定性的。

如果您声明您的例程不是确定性的,那么您的主数据库的副本可能与原始数据库不同,因为 MySQL 只会将过程调用添加到复制日志中,并且在从属服务器上执行该过程不会产生相同的结果.

如果您的例程是不确定的,那么 MySQL 必须在复制日志中包含受影响的行。如果您将例程声明为非确定性的,但它不会破坏任何内容,但复制日志将包含所有受影响的行,而仅过程调用就足够了,这可能会影响性能。

于 2016-04-15T22:19:58.650 回答
3

你没有错过任何东西。这个函数是不确定的。将其声明为确定性不会导致您的数据库崩溃,但可能会影响性能。来自 MySQL 站点:“将非确定性例程声明为 DETERMINISTIC 可能会导致优化器做出不正确的执行计划选择,从而导致意外结果。” 但是 MySQL 不会强制执行或检查您声明的确定性例程是否实际上是确定性的——MySQL 相信您知道自己在做什么。

于 2011-10-30T17:50:12.500 回答
2

如果您打开了复制功能或者有一天可能会使用它,那么确定性就很重要。例如,导致行更改(更新或插入)的非确定性函数调用将需要使用二进制(基于行)进行复制,其中确定性函数可以基于语句进行复制。当查看上面的 SQL 示例时,这变得很有趣,当使用基于语句的复制时,哪些会发生相同的情况(给出相同的结果),哪些应该使用在主服务器中获得的结果(基于行)进行复制。如果语句使用适当的锁定执行并且可以保证在从属设备上以相同的顺序执行,那么它们确实是确定性的。如果 Slave 使用的锁/语句顺序(没有并发,

于 2015-08-21T05:34:22.233 回答
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 参考手册正在尝试制作。希望这会有所帮助,谢谢!

于 2021-06-29T04:10:47.850 回答