【问题标题】:Deterministic function in mysqlmysql中的确定性函数
【发布时间】:2011-12-18 07:03:48
【问题描述】:

我对一个看似简单的概念感到困惑。 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 关键字似乎被广泛滥用。对我来说仍然很难相信有这么多人这样做,所以我会等待其他答案。

【问题讨论】:

  • 您能举一个您所说的“许多功能”的例子吗?
  • @Mat: 例如,dev.mysql.com/doc/refman/5.0/en/create-procedure.html(在用户 cmets 部分,搜索“CREATE FUNCTION db.fnfullname” - 它从 db 中读取行以获取传递的 id 并根据结果返回值选择)。
  • "... 用户 cmets ..." ... 一些真正的 mysql 功能你想到了吗?
  • 我喜欢这个问题。我没有看到 MySQL 文档中的哪个地方声明一个函数不能同时是 DETERMINISTICREADS SQL DATA(以及这对函数意味着什么)。
  • @Mat:我同意你的观点,“用户 cmets”部分不是很可靠的来源。我的第一个想法是我不应该太认真。所以我在问这个问题之前搜索了一下,发现了许多类似的实现(另一个例子,databasejournal.com/features/mysql/article.php/3569846/…,“访问存储函数中的表”)。然后我意识到这要么是一个常见的错误,要么是我遗漏了一些明显的东西......

标签: mysql user-defined-functions deterministic


【解决方案1】:

来自 MySQL 5.0 参考:

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

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

【讨论】:

  • 所以我们可能总是有 50% 的机会选错一个?伟大的。大声笑你的评论让我更了解它。
  • 在“嵌套集模型”中,例如。 mikehillyer.com/articles/managing-hierarchical-data-in-mysql 我正在使用例程来插入和删除节点。 1)我们是否可以认为在插入点(选定节点作为参考)右侧(之后)更新所有节点边界(在实际边界上加 2)是确定的? 2)因此也破坏了一个节点? 3)将节点移动到其实际位置的左侧? 4) 将节点移动到其实际位置的右侧?
  • 为什么会有这个功能?如果函数像3 + x = output 这样真正具有确定性,编译速度可能会更快?
【解决方案2】:

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

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

【讨论】:

  • 来自 mysql :'如果例程总是为相同的输入参数产生相同的结果,则认为例程是“确定的”,否则认为是“不确定的”。来自其他来源的“确定性函数”的定义也具有“对于相同的参数总是返回相同的结果”。使用某些 DML(至少 SELECT)是函数体内完全有效的操作。如果你说它没有引用不同的结果集,那么所有定义都是错误的,因为“总是”意味着函数体内所有可能的有效语句。
  • “产生相同的结果”表示在数据库中产生相同的结果。这并不意味着返回相同的结果集。为什么 MySQL 会关心结果集是否每次都不同?但是,它确实关心结果存储的数据是否不同 - 有关更多详细信息,请参阅我的答案。
  • 感谢 Jon Gilbert 和 bikeman868 的回答...我希望有一些官方消息来源可以支持他们。 This dba.stackexchange.com answer 拥有 23k 代表的人给出了相反的答案。
【解决方案3】:

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

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

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

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

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

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

【讨论】:

    【解决方案4】:

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

    【讨论】:

    • 此例程是确定性的,因为如果您在两个相同的数据库上执行此例程,结果将始终相同。
    【解决方案5】:

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

    【讨论】:

      【解决方案6】:

      我正在查看答案并决定提供更紧凑和更新的答案。

      在相同的数据库状态下,给定相同的输入参数,确定性函数总是返回相同的结果。例如 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 参考手册正在尝试制作。希望这会有所帮助,谢谢!

      【讨论】:

        猜你喜欢
        • 2017-11-27
        • 2013-12-13
        • 1970-01-01
        • 1970-01-01
        • 2016-04-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多