38

一見シンプルなコンセプトに戸惑いました。Mysqlは、決定論的関数を次の関数として定義します。

同じ入力パラメータに対して常に同じ結果を生成します

だから私の理解では、次のような機能

CREATE FUNCTION foo (val INT) READS SQL DATA
BEGIN
   DECLARE retval INT;
   SET retval = (SELECT COUNT(*) FROM table_1 WHERE field_1 = val);
   RETURN retval;
END;

決定論的ではありません(関数への2回の呼び出しの間に削除/更新/挿入が行われないという保証はありません)。同時に、ほとんど同じことを行う多くの関数、つまりクエリの結果に基づく戻り値を確認し、として宣言しましDETERMINISTICた。非常に基本的なものが欠けているようです。

誰かがこの問題を明確にすることができますか?

ありがとう。

更新 回答してくれた人に感謝します(+1); DETERMINISTICこれまでのところ、キーワードの誤用が広まっているようです。多くの人がそうしているとはまだ信じられないので、他の回答を少し待ちます。

4

6 に答える 6

19

MySQL 5.0リファレンスから:

ルーチンの性質の評価は、作成者の「誠実さ」に基づいています。MySQLは、DETERMINISTICと宣言されたルーチンに非決定論的な結果を生成するステートメントがないことをチェックしません。ただし、ルーチンを誤って宣言すると、結果に影響したり、パフォーマンスに影響したりする可能性があります。非決定論的ルーチンをDETERMINISTICとして宣言すると、オプティマイザーが誤った実行プランを選択するため、予期しない結果が生じる可能性があります。決定論的ルーチンをNONDETERMINISTICとして宣言すると、使用可能な最適化が使用されなくなるため、パフォーマンスが低下する可能性があります。MySQL 5.0.44より前では、DETERMINISTIC特性は受け入れられますが、オプティマイザでは使用されません。

これで、保存されたルーチンにタグを付けることができますが、そうDETERMINISTICでない場合でも、予期しない結果やパフォーマンスの問題が発生する可能性があります。

于 2011-10-30T17:50:03.563 に答える
15

DETERMINISTIC結果は、異なる時間に返される異なる結果セットを指していません(その間に追加されたデータによって異なります)。さらに、同じデータを使用する異なるマシンの結果セットへの参照です。たとえば、uuid()またはサーバー変数の参照を含む関数を実行する2台のマシンがある場合、これらは決定的ではないと見なす必要があります。これは、関数呼び出しがバイナリログ(マスター)に保存され、スレーブによっても実行されるため、たとえばレプリケーションで役立ちます。詳細と例については、http://dev.mysql.com/doc/refman/5.0/en/stored-programs-logging.htmlを参照してください。

したがって、DETERMINISTICの使用は(99%の確率で)正しく、誤用とは見なされません。

于 2012-06-18T18:45:28.467 に答える
11

あなたのルーチンは決定論的だと思います。ドキュメントはあまり明確ではなく、これにより多くの人がこの問題について非常に混乱しています。これは実際には何よりも複製に関するものです。

2つのデータベース間でレプリケーションが設定されている状況を考えてみます。マスターデータベースは、入力パラメータを含む実行されたすべての保存されたルーチンのログを保持し、このログをスレーブに送信します。スレーブは、同じ入力パラメータを使用して、同じ格納されたルーチンを同じ順序で実行します。スレーブデータベースには、マスターデータベースと同じデータが含まれますか?保存されたルーチンがGUIDを作成し、それらをデータベースに保存する場合、いいえ、マスターデータベースとスレーブデータベースは異なり、レプリケーションは中断されます。

DETERMINISTICフラグの主な目的は、この保存されたルーチンへの呼び出しをレプリケーションログに含めると、マスターデータベースとレプリケートされたスレーブの間に違いが生じ、安全ではないかどうかをMySQLに通知することです。

DETERMINISTICフラグが保存されたルーチンに適しているかどうかを判断するときは、次のように考えてください。2つの同一のデータベースから開始し、同じ入力パラメーターを使用して両方のデータベースでルーチンを実行した場合、データベースは引き続き同一ですか。もしそうなら、私のルーチンは決定論的です。

ルーチンが決定論的でないと宣言した場合、MySQLはプロシージャ呼び出しをレプリケーションログに追加するだけであり、スレーブでプロシージャを実行しても同じ結果が得られないため、メインデータベースのレプリカは元のデータベースと同一ではない可能性があります。

ルーチンが非決定論的である場合、MySQLは代わりに影響を受ける行をレプリケーションログに含める必要があります。ルーチンを非決定論的として宣言した場合、そうでない場合は何も壊れませんが、プロシージャの呼び出しだけで十分であり、パフォーマンスに影響を与える可能性がある場合、レプリケーションログには影響を受けるすべての行が含まれます。

于 2016-04-15T22:19:58.650 に答える
3

あなたは何も見逃していません。この関数は非決定論的です。決定論的であると宣言しても、データベースが溶けることはありませんが、パフォーマンスに影響を与える可能性があります。MySQLサイトから:「非決定論的ルーチンをDETERMINISTICとして宣言すると、オプティマイザが誤った実行プランを選択するため、予期しない結果が生じる可能性があります。」しかし、MySQLは、宣言された決定論的ルーチンが実際に決定論的であるかどうかを強制またはチェックしません---MySQLは、ユーザーが何をしているかを知っていると信頼します。

于 2011-10-30T17:50:12.500 に答える
2

レプリケーションをオンにしている場合、またはいつか使用する可能性がある場合は、確定的が重要です。たとえば、行の変更(更新または挿入)を引き起こす非決定論的関数呼び出しは、バイナリ(行ベース)を使用して複製する必要がありますが、決定論的関数はステートメントベースで複製できます。これは、上記のSQLの例を見ると興味深いものになります。ステートメントベースを使用してレプリケートすると同じ結果が発生し(同じ結果が得られ)、マスターで取得した結果を使用してレプリケートする必要があります(行ベース)。ステートメントが適切なロックで実行され、スレーブで同じ順序で実行されることが保証されている場合、それらは確かに決定論的です。スレーブが使用するロック/ステートメントの順序(同時実行なし、

于 2015-08-21T05:34:22.233 に答える
0

私は答えを調べていて、よりコンパクトで更新された答えを提供することにしました。

決定論的関数は、データベースの同じ状態で同じ入力パラメーターが与えられた場合、常に同じ結果を返します。例:POW、SUBSTR()、UCASE()。

非決定論的関数は、データベースの同じ状態で同じ入力パラメーターが与えられた場合、必ずしも同じ結果を返すとは限りません。例:CURDATE()、RAND()、UUID()。

MySQL8.0リファレンスマニュアルにはこれに関するいくつかの更新があります

8.2.1.20関数呼び出しの最適化

MySQL関数は、内部的に決定論的または非決定論的としてタグ付けされています。関数の引数に固定値が与えられた場合、呼び出しごとに異なる結果を返す可能性がある場合、関数は非決定的です。非決定論的関数の例:RAND()、UUID()。関数に非決定論的タグが付けられている場合、WHERE句での関数への参照は、すべての行(1つのテーブルから選択する場合)または行の組み合わせ(複数のテーブルから選択する場合)に対して評価されます。 -table join).MySQLは、引数がテーブル列であるか定数値であるかに関係なく、引数のタイプに基づいて関数を評価するタイミングも決定します。テーブルの列を引数として取る決定論的関数は、その列の値が変更されるたびに評価する必要があります。非決定論的関数は、クエリのパフォーマンスに影響を与える可能性があります。たとえば、一部の最適化が利用できない場合があります。以上のロックが必要になる場合があります。以下の説明ではRAND()を使用していますが、他の非決定論的関数にも適用されます。

MySQL8.0リファレンスマニュアルのこのコード例。テーブルを作成してから、id列1から49のような49行と、49行まで「AA」、「AB」、「AC」のような一意の文字列をcol_aでデータに入力できます。実際には15行を実行できますが、ランダム関数のトピックである49を15に変更する必要があります。

CREATE TABLE t (id INT NOT NULL PRIMARY KEY, col_a VARCHAR(100));

SELECT * FROM t WHERE id = POW(1,2);
SELECT * FROM t WHERE id = FLOOR(1 + RAND() * 49);

このコードは、MySQL8.0リファレンスマニュアルが作成しようとしている要点を説明するのに役立ちます。うまくいけば、これは感謝に役立ちます!

于 2021-06-29T04:10:47.850 に答える