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

これを修正するには、次の 2 つの方法があります。

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

SET GLOBAL log_bin_trust_function_creators = 1;

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

log_bin_trust_function_creators = 1

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

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

于 2015-09-22T08:42:41.517 に答える
0

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

このMysqlドキュメントによると:

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

だからそれはあなた次第です。この方法で問題が発生しないことが確実な場合は...

于 2016-04-12T12:19:22.327 に答える
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 つのノードの注文 100 は異なるデータを参照しており、注文 101 はありません。

auto_increment 変数の動作を変更するために多くの機能が追加されたのには理由があります。

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

于 2011-02-07T12:37:10.887 に答える
-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 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
于 2018-05-26T05:13:31.917 に答える