一部の関数は条件によっては適切に複製されません。
-
USER()
、CURRENT_USER()
(またはCURRENT_USER
)、UUID()
、VERSION()
およびLOAD_FILE()
関数は変更なしでレプリケートされるため、行ベースのレプリケーションが有効になっていないかぎり、レプリカで確実に動作しません。 (セクション17.2.1「レプリケーション形式」を参照してください。)USER()
およびCURRENT_USER()
は、MIXED
モード使用時に行ベースレプリケーションを使用して自動的に複製され、STATEMENT
モードでは警告を生成します。 (セクション17.5.1.8「CURRENT_USER() のレプリケーション」も参照してください。) これは、VERSION()
およびRAND()
にも当てはまります。 -
NOW()
の場合、バイナリログはタイムスタンプを含みます。 これは、値ソースでこの関数を呼び出したときに返されますがレプリカにレプリケートされることを意味します。 異なるタイムゾーンの MySQL サーバー間でレプリケートするときに予期しない結果が発生しないようにするには、ソースとレプリカの両方でタイムゾーンを設定します。 詳細は、セクション17.5.1.33「レプリケーションとタイムゾーン」を参照してください。異なるタイムゾーンのサーバー間でレプリケートする際の潜在的な問題を説明するために、ソースがニューヨークにあり、レプリカがストックホルムにあり、両方のサーバーがローカル時間を使用しているとします。 さらに、次に示すように、ソースでテーブル
mytable
を作成し、このテーブルに対してINSERT
ステートメントを実行し、テーブルから選択するとします:mysql> CREATE TABLE mytable (mycol TEXT); Query OK, 0 rows affected (0.06 sec) mysql> INSERT INTO mytable VALUES ( NOW() ); Query OK, 1 row affected (0.00 sec) mysql> SELECT * FROM mytable; +---------------------+ | mycol | +---------------------+ | 2009-09-01 12:00:00 | +---------------------+ 1 row in set (0.00 sec)
ストックホルムの現地時間はニューヨークより 6 時間遅れているため、その瞬間にレプリカで
SELECT NOW()
を発行すると、値2009-09-01 18:00:00
が返されます。 このため、表示されたCREATE TABLE
およびINSERT
ステートメントがレプリケートされた後に、mytable
のレプリカコピーから選択した場合、mycol
に2009-09-01 18:00:00
という値が含まれている可能性があります。 ただし、これは当てはまりません。mytable
のレプリカコピーから選択すると、ソースとまったく同じ結果が得られます:mysql> SELECT * FROM mytable; +---------------------+ | mycol | +---------------------+ | 2009-09-01 12:00:00 | +---------------------+ 1 row in set (0.00 sec)
SYSDATE()
関数は、NOW()
とは異なり、レプリケーションに安全ではありません。バイナリログ内でSET TIMESTAMP
ステートメントに影響されず、ステートメントベースロギングが使用される場合は非決定的であるためです。 行ベースロギングを使用する場合は、これは問題ではありません。ほかの方法は
--sysdate-is-now
オプションを使用することで、SYSDATE()
がNOW()
のエイリアスになります。 これは、ソースおよびレプリカで正しく機能するために必要です。 このような場合でも、この関数によって警告が発行されますが、--sysdate-is-now
がソースとレプリカの両方で使用されているかぎり、無視しても問題ありません。SYSDATE()
は、MIXED
モードの使用時に行ベースのレプリケーションを使用して自動的にレプリケートされ、STATEMENT
モードで警告を生成します。セクション17.5.1.33「レプリケーションとタイムゾーン」も参照してください。
-
次の制限は、ステートメントベースレプリケーションにのみ適用され、行ベースレプリケーションには適用されません。 ユーザーレベルのロックを処理する
GET_LOCK()
,RELEASE_LOCK()
,IS_FREE_LOCK()
およびIS_USED_LOCK()
関数は、ソース上の同時実行性コンテキストを認識していないレプリカでレプリケートされます。 したがって、レプリカ上のコンテンツが異なるため、これらの関数を使用してソーステーブルに挿入しないでください。 たとえば、INSERT INTO mytable VALUES(GET_LOCK(...))
などのステートメントを発行しないでください。これらの関数は、
MIXED
モード使用時に行ベースレプリケーションを使用して自動的に複製され、STATEMENT
モードで警告を生成します。
ステートメントベースレプリケーションが有効のときに前述の制限に対する回避策として、問題のある関数結果をユーザー変数に保存して、後続のステートメントでその変数を参照する方法を使用できます。 たとえば、次の単一行 INSERT
は、UUID()
関数を参照するため問題があります。
INSERT INTO t VALUES(UUID());
この問題を回避するには、代わりにこれを実行してください。
SET @my_uuid = UUID();
INSERT INTO t VALUES(@my_uuid);
このステートメントの連続は複製されます。@my_uuid
の値が INSERT
ステートメントの前にユーザー変数イベントとしてバイナリログに格納されて INSERT
で使用できるためです。
同じ概念が複数行挿入に適用されますが、使用するのが面倒です。 2 行挿入の場合、このようにできます。
SET @my_uuid1 = UUID(); @my_uuid2 = UUID();
INSERT INTO t VALUES(@my_uuid1),(@my_uuid2);
ただし、行数が多いか不明の場合、この回避策は困難であるか実用的でありません。 たとえば、次のステートメントを個々のユーザー変数が各行に関連付けられているものに変換することはできません。
INSERT INTO t2 SELECT UUID(), * FROM t1;
ストアドファンクション内で、RAND()
は、関数の実行中に 1 回だけ呼び出されるかぎり、正しく複製されます。 (関数実行タイムスタンプおよび乱数シードは、ソースとレプリカで同一の暗黙的な入力とみなすことができます。)
FOUND_ROWS()
と ROW_COUNT()
関数がステートメントベースレプリケーションを使用して複製されるときは、信頼性がありません。 回避策は、関数呼び出しの結果をユーザー変数に格納してから、INSERT
ステートメントでこれを使用することです。 たとえば、mytable
という名前のテーブルに結果を格納する場合は、普通は次のように実行するかもしれません。
SELECT SQL_CALC_FOUND_ROWS FROM mytable LIMIT 1;
INSERT INTO mytable VALUES( FOUND_ROWS() );
しかし、mytable
を複製する場合は、次のように SELECT ... INTO
を使用してから変数をテーブルに格納することをお勧めします。
SELECT SQL_CALC_FOUND_ROWS INTO @found_rows FROM mytable LIMIT 1;
INSERT INTO mytable VALUES(@found_rows);
このように、ユーザー変数はコンテキストの一部としてレプリケートされ、レプリカに正しく適用されます。
これらの関数は、MIXED
モード使用時に行ベースレプリケーションを使用して自動的に複製され、STATEMENT
モードで警告を生成します。 (Bug #12092、Bug #30244)