2011-02-07 60 views
14

我們的數據庫具有生成訂單號的功能。它從Settings表中讀取一個值,然後遞增它,然後返回新的值。例如:CREATE FUNCTION錯誤「此函數沒有DETERMINISTIC,NO SQL或READS SQL DATA」

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給出了這樣的錯誤:

This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you might want to use the less safe log_bin_trust_function_creators variable)

無論設定什麼binlog_format的,是真的有問題以上功能?根據我的相關MySQL page的閱讀,我看不出任何原因,爲什麼此功能將與複製不兼容,無論是ROW或STATEMENT級二進制日誌記錄。

如果函數是安全的,那麼設置全局log_bin_trust_function_creators = 1會讓我感到不安。我不想禁用所有功能的檢查,只是這一個。我是否可以將該函數標記爲NO SQL來禁止警告?我試過了,它工作。這會導致任何問題嗎?

回答

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 

現在訂單100的2個節點上是指不同的數據,並沒有爲了101

還有一個原因,出現了大量的功能加入到修改AUTO_INCREMENT變量的行爲。

如果將插入程序包裝在過程中 - 從序列生成器中檢索值然後將其嵌入到插入語句中,則立即解決問題,但是您需要考慮如何避免使用兩次相同的數字不同數據庫節點。

+0

symcbean - 我知道這種方法在羣集中不起作用。我不是在問那種情況。我在詢問簡單的主從設置,插入/更新只發生在一個地方。 – richb 2011-02-07 23:11:58

24

我已經google了,在這裏我。 我已經找到一種方法:

SET GLOBAL log_bin_trust_function_creators = 1; 

但要小心,這可能是不安全的數據恢復或複製......

+8

這沒有以任何方式解決問題。 – richb 2014-05-29 02:19:23

+0

@richb,但它的作品。 – 2018-02-13 20:53:03

5

按我低估了它會導致問題,當數據恢復或複製

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

如果存儲函數的聲明中調用,如選擇不修改數據,函數的執行不寫入二進制日誌,即使函數本身修改數據。這種記錄行爲有可能導致問題。假設函數myfunc()的定義如上。

+0

我不認爲這是正確的。看[鏈接](https://dev.mysql.com/doc/refman/5.7/en/stored-programs-logging.html),你會看到_italics _...對於存儲函數,函數內進行的行更改是記錄,而不是函數調用。對於觸發器,記錄由觸發器進行的行更改。 ... _italics_。所以這對我來說是莫名其妙的,爲什麼logbin_format = ROW仍然看起來不允許沒有標記爲DETERMINISTIC的函數。 – 2016-03-19 20:23:57

-3

剛剛創建函數之前執行此:

SET @@global.log_bin_trust_function_creators = 1; 

並添加MODIFIES SQL DATA的聲明。

另外...好吧,你要求不要評論函數本身,但我建議你放棄number變量,只需做RETURN LAST_INSERT_ID()

+1

這不起作用。 log_bin_trust_function_creators是GLOBAL,而不是會話變量。 – richb 2013-06-12 02:44:19

1

有兩種方法來解決這個問題:

執行在MySQL控制檯執行以下操作:

SET GLOBAL log_bin_trust_function_creators = 1; 

添加以下到mysql.ini配置文件:

log_bin_trust_function_creators = 1 

的設置放鬆對非確定性功能的檢查。非確定性函數是修改數據的函數(即具有更新,插入或刪除語句)。欲瞭解更多信息,請看這裏。

請注意,如果未啓用二進制日誌記錄,則此設置不適用。

0

Could I instead just flag the function as NO SQL to suppress the warning? I tried it and it worked. Will this cause any problem?

根據這一Mysql doc

Assessment of the nature of a function is based on the 「honesty」 of the creator: MySQL does not check that a function declared DETERMINISTIC is free of statements that produce nondeterministic results.

所以,這取決於你。如果您確定該方法不會導致任何問題...