CREATE FUNCTION错误"此函数没有DETERMINISTIC,NO SQL或READS SQL DATA"

ric*_*chb 15 mysql stored-functions

我们的数据库具有生成订单号的功能.它从"设置"表中读取值,递增它,然后返回新值.例如:

CREATE FUNCTION NextOrderNumber() RETURNS INTEGER UNSIGNED NOT DETERMINISTIC
BEGIN
  DECLARE number INTEGER UNSIGNED;
  UPDATE Settings SET IntegerValue=LAST_INSERT_ID(IntegerValue+1) WHERE KeyName='NextOrderNumber';
  SET number=LAST_INSERT_ID();
  return number;
END
Run Code Online (Sandbox Code Playgroud)

注意:不要批评这个功能我知道它有缺陷只是为了说明.

我们使用这个函数如下:

INSERT INTO Orders(OrderNumber, ...)
SELECT NextOrderNumber(), ...
Run Code Online (Sandbox Code Playgroud)

启用二进制日志记录时,CREATE FUNCTION会出现此错误:

此函数在其声明中没有DETERMINISTIC,NO SQL或READS SQL DATA,并且启用了二进制日志记录(您可能希望使用安全性较低的log_bin_trust_function_creators变量)

无论binlog_format设置了什么,上述功能是否真的存在问题?根据我对相关MySQL页面的阅读,我看不出任何理由为什么这个函数与复制不兼容,无论是ROW还是STATEMENT级二进制日志记录.

如果函数是安全的,设置全局log_bin_trust_function_creators = 1会让我感到不安.我不想为所有功能禁用此检查,只需这一项.我可以改为将该函数标记为NO SQL以禁止警告吗?我尝试了它,它的工作原理.这会导致任何问题吗?

小智 25

我用Google搜索,我在这里.我找到了一种方法:

SET GLOBAL log_bin_trust_function_creators = 1;
Run Code Online (Sandbox Code Playgroud)

但要小心,数据恢复或复制可能不安全......

  • 这并没有以任何方式解决这个问题. (9认同)

Rik*_*tel 6

根据我的不足,它会导致数据恢复或复制时出现问题

参考:http://dev.mysql.com/doc/refman/5.0/en/stored-programs-logging.html

MySQL 5.0.6:记录创建存储例程和CALL语句的语句.存储函数调用在更新数据的语句中发生时会被记录(因为这些语句被记录).

但是,即使在函数本身内发生数据更改,函数调用也会在SELECT等语句中发生,如果不更改数据,则不会记录它们.这可能会导致问题.

在某些情况下,如果在不同的时间或在不同的(主机和从机)机器上执行,功能和过程可能会产生不同的影响,因此可能对数据恢复或复制不安全.

例如

CREATE FUNCTION myfunc () RETURNS INT DETERMINISTIC
BEGIN
  INSERT INTO t (i) VALUES(1);
  RETURN 0;
END;

SELECT myfunc();
Run Code Online (Sandbox Code Playgroud)

如果在诸如SELECT之类的语句中调用存储函数而不修改数据,则函数的执行不会写入二进制日志,即使函数本身修改数据也是如此.此日志记录行为可能会导致问题.假设函数myfunc()定义如上.


sym*_*ean 0

考虑一下二进制日志中写入的内容。

您无法确保在主服务器上创建的订单在从服务器上执行交易时会生成相同的序列 - 或者更有可能的是,由集群中的另一个主服务器生成。例如

 0) Node 1 and Node 2 are in sync, NextOrderNumber=100
 1) Node 1 receives insert statement wrt order from customer A and assigns 
    order number 100, changes its NextOrderNumber to 101
 2) Node 1 writes the settings update to the log
 3) Node 1 writes the insert statement to the log
 4) Node 2 processes for customer B, asigns order number 100 and increments
 5) Node 2 writes the settings update from to the log
 6) Node 2 writes the insert statement to the log
 7) Nodes 2 reads settings update from the log @2 
         - Its NextOrderNumber is now 102
 8) Node 2 reads insert from log @3, tries to apply it but it fails 
         due to duplicate key
 9) Node 1 reads the update @5 - Its nextOrderNumber is also now 102
 10) Node1 reads insert from log @6 - 
         but this fails due to duplicate key
Run Code Online (Sandbox Code Playgroud)

现在2个节点上的订单100引用了不同的数据,并且没有订单101。

添加了很多功能来修改 auto_increment 变量的行为是有原因的。

如果您将插入包装在一个过程中 - 该过程从序列生成器检索一个值,然后将其嵌入到插入语句中,那么直接问题将得到解决,但是您需要考虑如何避免使用不同的数据库节点两次分配相同的数字。