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

时间:2011-02-07 10:50:10

标签: 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

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

我们使用此功能如下:

INSERT INTO Orders(OrderNumber, ...)
SELECT NextOrderNumber(), ...

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

  

此功能没有   DETERMINISTIC,NO SQL或READS SQL   DATA在其声明和二进制文件中   已启用日志记录(您可能想要   使用不太安全   log_bin_trust_function_creators   变量)

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

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

7 个答案:

答案 0 :(得分:25)

我用Google搜索,我在这里。 我找到了办法:

SET GLOBAL log_bin_trust_function_creators = 1;

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

答案 1 :(得分:6)

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

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

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

但是,即使函数本身发生数据更改,在SELECT等语句中发生的函数调用也不会记录,这些函数调用不会更改数据。这可能会导致问题。

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

E.g。

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

SELECT myfunc();

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

答案 2 :(得分:1)

有两种方法可以解决这个问题:

在MySQL控制台中执行以下命令:

SET GLOBAL log_bin_trust_function_creators = 1;

将以下内容添加到mysql.ini配置文件中:

log_bin_trust_function_creators = 1

该设置放宽了对非确定性功能的检查。非确定性函数是修改数据的函数(即具有更新,插入或删除语句)。有关详细信息,请参阅此处。

请注意,如果未启用二进制日志记录,则此设置不适用。

答案 3 :(得分:0)

想一想写入二进制日志的内容。

当在从属设备上播放事务时,您无法确保在主设备上创建的订单具有为其生成的相同序列 - 或者更有可能的是,集群中的另一个主设备。 e.g。

 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

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

有一个原因是为了修改auto_increment变量的行为而添加了许多功能。

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

答案 4 :(得分:0)

  

我是否可以将该函数标记为NO SQL以禁止警告?我尝试了它,它的工作原理。这会导致任何问题吗?

根据Mysql doc

  

对函数性质的评估基于创建者的“诚实”:MySQL不会检查声明为DETERMINISTIC的函数是否没有产生非确定性结果的语句。

所以它取决于你。如果您确定该方法不会导致任何问题...

答案 5 :(得分:-2)

添加READS SQL DATA,声明这是一个只读函数:

CREATE FUNCTION NextOrderNumber() RETURNS INTEGER UNSIGNED NOT DETERMINISTIC
READS SQL DATA
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

答案 6 :(得分:-3)

在创建函数之前执行此操作:

SET @@global.log_bin_trust_function_creators = 1;

并在声明中添加MODIFIES SQL DATA

另外......好吧,你要求不对功能本身发表评论,但我建议您删除number变量,然后执行RETURN LAST_INSERT_ID()