web-dev-qa-db-ja.com

CREATE FUNCTIONエラー「この関数には、DETERMINISTIC、NO SQL、またはREADS SQLDATAがありません」

私たちのデータベースには注文番号を生成する機能があります。設定テーブルから値を読み取り、インクリメントしてから、新しい値を返します。例えば:

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(), ...

バイナリロギングが有効になっている場合、CREATEFUNCTIONは次のエラーを出します。

この関数の宣言にはDETERMINISTIC、NO SQL、またはREADS SQL DATAがなく、バイナリロギングが有効になっています(安全性の低いlog_bin_trust_function_creatorsを使用する可能性があります可能性があります変数)

どのbinlog_formatが設定されているかに関係なく、上記の関数に本当に問題がありますか?関連する MySQLページ を読んだところによると、この関数がROWまたはSTATEMENTレベルのバイナリロギングでレプリケーションと互換性がない理由はわかりません。

関数が安全な場合、グローバルlog_bin_trust_function_creators = 1を設定すると不安になります。すべての機能でこのチェックを無効にしたくはありません。これだけです。代わりに、警告を抑制するために関数にNO SQLのフラグを立てることはできますか?私はそれを試しました、そしてそれは働きました。これは問題を引き起こしますか?

15
richb

私はグーグルで検索しました、そしてここにいます。私は方法を見つけました:

SET GLOBAL log_bin_trust_function_creators = 1;

ただし、注意してください。データの回復や複製には安全でない可能性があります。

25
SpiderWan

私の控えめな表現によると、それはデータ復旧または複製時に問題を引き起こします

参照: 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()が上記のように定義されているとします。

6
Rikin Patel

これを修正する方法は2つあります。

MySQLコンソールで以下を実行します。

SET GLOBAL log_bin_trust_function_creators = 1;

Mysql.ini構成ファイルに以下を追加します。

log_bin_trust_function_creators = 1

この設定により、非決定論的関数のチェックが緩和されます。非決定論的関数は、データを変更する関数です(つまり、ステートメントを更新、挿入、または削除します)。詳細については、こちらをご覧ください。

バイナリログが有効になっていない場合、この設定は適用されないことに注意してください。

1
Wale

バイナリログに何が書き込まれるかを考えてください。

マスターで作成されたオーダーが、トランザクションがスレーブで再生されたときに同じシーケンスが生成されることを保証することはできません。あるいは、クラスター内の別のマスターによって生成される可能性が非常に高くなります。例えば.

 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変数の動作を変更するために多くの機能が追加されたのには理由があります。

シーケンスジェネレーターから値を取得して挿入ステートメントに埋め込むプロシージャで挿入をラップすると、当面の問題は解決されますが、異なるデータベースノードを使用して同じ番号を2回割り当てないようにする方法を検討する必要があります。

0
symcbean

代わりに、警告を抑制するために関数にNO SQLのフラグを立てることはできますか?私はそれを試しました、そしてそれは働きました。これは問題を引き起こしますか?

これによると Mysql doc

関数の性質の評価は、作成者の「誠実さ」に基づいています。MySQLは、DETERMINISTICと宣言された関数に非決定論的な結果を生成するステートメントがないことをチェックしません。

だからそれはあなた次第です。メソッドが問題を引き起こさないと確信している場合...

0
matof