INSERT DELAYED ...
INSERT
ステートメントの
DELAYED
オプションは、もし
INSERT
が完了するのを待つことができない、または待つ必要がないクライアントを持っている場合に大変有効となる、スタンダード
SQL の MySQL 拡張子です。これは、MySQL
をログに利用し、完了までに長時間かかる
SELECT
と
UPDATE
ステートメントを定期的に起動させるときによく起こる状態です。
クライアントが
INSERT DELAYED
を利用するとき、サーバーからすぐに OK
が出て、テーブルが別のスレッドによって使用中でなければ行が挿入されるためにキューを作ります。
INSERT DELAYED
を利用することのそれ以外の大きな利益は、たくさんのクライアントからの挿入は一緒にまとめられ、ひとつのブロックに書き込まれると言うことです。これは、別々の挿入を何度も行うよりも早く機能します。
INSERT DELAYED
は、もしテーブルがほかの形で利用されていないのであれば、通常の
INSERT
よりも遅いということに注意してください。また、サーバーには、遅れている行を持つ各テーブルに別々のスレッドを扱うための、追加オーバーヘッドもあります。これは、本当に
INSERT DELAYED
が必要だということが確実なときだけ利用するべきであるということを意味します。
キューを作った行は、テーブルに挿入されるまでメモリー内だけで保持されます。これは、もし
mysqld
を強制的に終了させたり
(たとえば、kill -9
を利用して)、mysqld
が突然停止してしまったりすると、ディスクに書き込まれる前のキューを作った行はすべて失われてしまう
ということを意味します。
DELAYED
の利用に関しては、いくつかの制限があります。
INSERT
DELAYED
は、MyISAM
、MEMORY
、ARCHIVE
、および
(MySQL 5.1.19 では)
BLACKHOLE
テーブルでのみ機能します。DELAYED
をサポートしていないエンジンの場合は、エラーが発生します。
挿入は、ロックを保持するセッションではなく、別のスレッドによって処理される必要があるため、INSERT
DELAYED
が、LOCK
TABLES
を使用してロックされたテーブルとともに使用された場合はエラーが発生します。
もしデータファイル中にフリーブロックがなければ、MyISAM
テーブルには並列
SELECT
と
INSERT
ステートメントがサポートされます。これらの条件下では、INSERT
DELAYED
を
MyISAM
と一緒に利用しなければいけないことはほとんどありません。
INSERT
DELAYED
は、値リストを指定する
INSERT
ステートメントにだけ利用されなければいけません。サーバーは、INSERT
... SELECT
か
INSERT
... ON DUPLICATE KEY UPDATE
に対して
DELAYED
を無視します。
INSERT
DELAYED
ステートメントがすぐに返されるので、そのステートメントが生成するであろう
AUTO_INCREMENT
値を得るために、行が挿入される前に、LAST_INSERT_ID()
を利用することはできません。
DELAYED
行は、実際に挿入されるまでは
SELECT
ステートメントには見えません。
ステートメントで複数の行を挿入するときに、バイナリログが有効になっていて、グローバルなロギング形式でステートメントベースのロギングが使用される
(binlog_format
が
STATEMENT
に設定されている)
場合、INSERT
DELAYED
は通常の
INSERT
として処理されます。この制限は、行ベースのバイナリログには適用されません。
INSERT
DELAYED
がスレーブ上で通常の
INSERT
として処理されるように、DELAYED
はスレーブレプリケーションサーバー上で無視されます。これは、DELAYED
のために、スレーブにマスターとは異なるデータが存在することになる場合があるためです。
テーブルが書き込みロックされ、ALTER
TABLE
がテーブル構造を変更するのに利用されると、保留中の
INSERT
DELAYED
ステートメントは失われてしまいます。
INSERT
DELAYED
はビューでサポートされていません。
INSERT
DELAYED
は、パーティション化されたテーブルに対してはサポートされていません。
次に、INSERT
や
REPLACE
に
DELAYED
を利用したときに何が起こるかを詳しく説明しています。この説明の中では、「スレッド」
は INSERT
DELAYED
ステートメントを受け取ったスレッドで、「ハンドラ」
は特定のテーブルのためにすべての
INSERT DELAYED
ステートメントを扱うスレッドを表しています。
スレッドが DELAYED
ステートメントをテーブルに実行したとき、もし同じようなハンドラがすでに存在していなければ、すべての
DELAYED
ステートメントをテーブルに生成するためにハンドラスレッドが作成されます。
このスレッドは、ハンドラが以前に
DELAYED
ロックを取得したかどうかを確認します。取得していない場合は、ハンドラスレッドに取得するよう指示します。もしほかのスレッドが
READ
か
WRITE
ロックをテーブル上に持っていても、DELAYED
ロックを得ることができます。しかし、ハンドラはテーブル構造が最新であるかどうかを確認するために、すべての
ALTER TABLE
ロックや
FLUSH
TABLES
ステートメントが終了するのを待ちます。
スレッドは
INSERT
ステートメントを実行しますが、行をテーブルに書き込む代わりに、ハンドラスレッドに管理されているキューに最終行のコピーを置きます。構文エラーはすべてスレッドに見つけられ、クライアントプログラムにリポートされます。
クライアントは、挿入操作が完了する前に
INSERT
が返るため、複製行の数や、結果として生じる行の
AUTO_INCREMENT
値をサーバーから得ることができません。(もし
C API を利用すると、同じ理由で
mysql_info()
関数からは意味のある答えが返りません)。
行がテーブルに挿入されたとき、バイナリログはハンドラスレッドによって更新されます。複合行挿入の場合、最初の行が挿入されたときにバイナリログが更新されます。
delayed_insert_limit
行が書き込まれるたびに、ハンドラは、引き続き保留中の
SELECT
ステートメントがないかどうかを確認します。もしあれば、続ける前にそれらを実行させます。
ハンドラのキューにそれ以上行がなくなると、テーブルはロック解除されます。もし新しい
INSERT
DELAYED
ステートメントが
delayed_insert_timeout
秒以内に受信されたら、ハンドラは終了します。
もし
delayed_queue_size
を超える行が、特定のハンドラキューの中で保留中だったら、INSERT
DELAYED
をリクエストしているスレッドは、キューの中にスペースができるまで待ちます。これは、遅れたメモリーのキューのために
mysqld
がすべてのメモリーを使わないことを保障するために行われます。
ハンドラスレッドは、Command
カラム内の
delayed_insert
と共に、MySQL
プロセスリスト内に現れます。FLUSH
TABLES
ステートメントを実行するか、または
KILL
で強制終了した場合は、強制終了されます。しかし、終了する前にまずテーブル内でキューを作っているすべての行を格納します。この最中は、別のスレッドから新しい
thread_id
INSERT
ステートメントを受け入れません。もしこの後に
INSERT
DELAYED
ステートメントを実行すると、新しいハンドラスレッドが作成されます。
もし起動中の
INSERT
DELAYED
ハンドラがあったら、INSERT
DELAYED
ステートメントは通常の
INSERT
ステートメントより高い優先権を持つということを意味します。その他の更新ステートメントは、INSERT
DELAYED
キューが空になるか、だれかが
(KILL
で)
ハンドラスレッドを終了するか、またはだれかが
thread_id
FLUSH
TABLES
を実行するまで待機する必要があります。
次のステータス変数は、INSERT
DELAYED
ステートメントに関する情報を提供します。
状態変数 | 意味 |
Delayed_insert_threads |
ハンドラスレッド数 |
Delayed_writes |
INSERT DELAYED
で書かれた行数 |
Not_flushed_delayed_rows |
書き込みを待つ行数 |
SHOW STATUS
ステートメントか、mysqladmin
extended-status
コマンドを実行することでこれらの変数を見ることができます。