Mysql “创建函数错误”;此函数没有确定性、无SQL或读取SQL数据;

Mysql “创建函数错误”;此函数没有确定性、无SQL或读取SQL数据;,mysql,stored-functions,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='NextOr

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

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函数会出现以下错误:

此函数不包含以下内容: 确定性、无SQL或读取SQL 声明和二进制文件中的数据 日志记录已启用(您可能需要 使用不太安全的 日志\u bin\u信任\u函数\u创建者 变量)

不管设置了什么binlog_格式,上述函数真的有问题吗?根据我对相关文件的阅读,我看不出该函数与复制(行或语句级二进制日志记录)不兼容的任何原因


如果函数是安全的,那么将全局日志\u bin\u trust\u函数\u creators设置为1会让我感到不安。我不想对所有函数禁用此检查,仅此一个。我是否可以将函数标记为NO-SQL以抑制警告?我试过了,它成功了。这会导致任何问题吗?

思考一下二进制日志中写入了什么

您无法确保在主服务器上创建的订单在从服务器上播放事务时会生成相同的序列,或者更可能的情况是,由集群中的另一个主服务器为其生成相同的序列。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
现在,两个节点上的订单100引用不同的数据,没有订单101

有一个原因是添加了许多功能来修改自动增量变量的行为


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

我在谷歌上搜索过,现在我在这里。 我找到了一个方法:

设置全局日志\u bin\u信任函数\u creators=1;

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

据我所知,它在数据恢复或复制时会导致问题

参考:

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

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

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

例如


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

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

SET @@global.log_bin_trust_function_creators = 1;
并将
修改SQL数据
添加到声明中


还有。。。好吧,您要求不要对函数本身进行注释,但我建议您删除
number
变量,只需执行
返回最后一个插入ID()

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

在MySQL控制台中执行以下操作:

SET GLOBAL log_bin_trust_function_creators = 1;
将以下内容添加到mysql.ini配置文件中:

log_bin_trust_function_creators = 1
该设置放松了对非确定性函数的检查。非确定性函数是修改数据的函数(即具有update、insert或delete语句)。有关更多信息,请参见此处

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

我是否可以将函数标记为NO-SQL以抑制警告?我试过了,它成功了。这会引起什么问题吗

据此:

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


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

添加
读取SQL数据,声明该函数为只读函数:

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

symcbean—我知道这种方法在集群中不起作用。我不是在问这种情况。我问的是简单的主从式设置,插入/更新只发生在一个地方。这不起作用。log\u bin\u trust\u function\u创建者是全局的,不是会话变量。嗯?全球行为不受影响??是的。您正在设置一个全局变量。这并不能解决任何问题。我认为这是不对的。查看您将看到斜体…对于存储的函数,记录函数中的行更改,而不是函数调用。对于触发器,将记录触发器所做的行更改。。。斜体字。所以,对于我来说,很难解释为什么logbin_format=ROW仍然不允许未标记为确定性的函数。当我从视图中选择表时,我面临着这个错误。解决办法是什么?
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