21

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

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 页面的阅读,我看不出这个函数与复制不兼容的任何原因,无论是 ROW 还是 STATEMENT 级别的二进制日志记录。

如果函数是安全的,设置全局 log_bin_trust_function_creators=1 会让我感到不安。我不想为所有功能禁用此检查,仅此一项。我可以改为将函数标记为 NO SQL 以抑制警告吗?我试过了,它奏效了。这会导致任何问题吗?

4

8 回答 8

29

我已经用谷歌搜索了,我在这里。我找到了一种方法:

SET GLOBAL log_bin_trust_function_creators = 1;

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

于 2013-04-08T12:57:59.247 回答
7

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

参考: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();

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

于 2013-05-23T05:40:23.173 回答
1

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

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

SET GLOBAL log_bin_trust_function_creators = 1;

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

log_bin_trust_function_creators = 1

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

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

于 2015-09-22T08:42:41.517 回答
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

现在2个节点上的order 100指的是不同的数据,没有order 101。

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

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

于 2011-02-07T12:37:10.887 回答
0

我可以改为将函数标记为 NO SQL 以抑制警告吗?我试过了,它奏效了。这会导致任何问题吗?

根据这个Mysql 文档

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

所以这取决于你。如果您确定该方法不会引起任何问题...

于 2016-04-12T12:19:22.327 回答
0

编写属性对我有帮助。在此函数中,您需要编写 - MODIFIES SQL DATA - 因为该函数使用 UPDATE。如果函数中只使用 SELECT,那么我们将编写 READS SQL DATA。如果函数体中同时使用数据读取和写入操作符,您也可以编写这两个属性。

CREATE FUNCTION NextOrderNumber() 
RETURNS INTEGER 
UNSIGNED 
NOT DETERMINISTIC
MODIFIES 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
于 2021-11-19T11:01:25.807 回答
-3

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

SET @@global.log_bin_trust_function_creators = 1;

并添加MODIFIES SQL DATA到声明中。

另外...好吧,您要求不要评论函数本身,但我建议您删除number变量并简单地执行RETURN LAST_INSERT_ID().

于 2013-05-23T05:49:17.377 回答
-3

添加READS SQL DATAwhich 声明是一个只读函数:

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
于 2018-05-26T05:13:31.917 回答