「繰り返し可能な読み取り」を使用すると、ファントム読み取りを生成できるはずですが、どのようにしたらよいでしょうか。CSの学生を教える例として必要です。
インデックス付けされていないフィールドxで「SELECT...WHERE x <= 888」を作成し、上限888が存在しないようにしてから、別の接続で888のすぐ下の値を持つ新しい行を挿入する必要があると思います。
それが機能しないことを除いて。非常に大きなテーブルが必要ですか?または、他の何か?
「繰り返し可能な読み取り」を使用すると、ファントム読み取りを生成できるはずですが、どのようにしたらよいでしょうか。CSの学生を教える例として必要です。
インデックス付けされていないフィールドxで「SELECT...WHERE x <= 888」を作成し、上限888が存在しないようにしてから、別の接続で888のすぐ下の値を持つ新しい行を挿入する必要があると思います。
それが機能しないことを除いて。非常に大きなテーブルが必要ですか?または、他の何か?
RR分離レベルでのMySQLの「ファントム読み取り」は深く隠されていますが、それでも再現できます。手順は次のとおりです。
テーブルab(a int主キー、b int);を作成します。
Tx1:
開始;
abから*を選択します。// 空集合
エリック、
非常に多くの行でテストしただけです。
InnoDB mysqlで、読み取りがコミットされた、またはより制限された分離レベルのファントムは見つかりません。ドキュメントで説明されています:
REPEATABLE READ:一貫性のある読み取りの場合、READ COMMITTED分離レベルとは重要な違いがあります。同じトランザクション内のすべての一貫性のある読み取りは、最初の読み取りによって確立されたスナップショットを読み取ります。この規則は、同じトランザクション内で複数のプレーンな(ロックされていない)SELECTステートメントを発行する場合、これらのSELECTステートメントは相互にも一貫していることを意味します。セクション13.6.8.2「一貫性のある非ロック読み取り」を参照してください。
ただし、読み取りコミット分離レベルでファントムを見つけることもできません。これは、MySQLのレプリケーションとリカバリが機能するために「ファントム行」をブロックする必要があるために必要です。
より詳細な情報:http ://dev.mysql.com/doc/refman/5.1/en/set-transaction.html
生徒にファントムを表示するには、別のデータベースブランドに移行する必要があると思います。MSSQLSERVERとOracleの両方を使用しています。
さて...あなたの最初の質問には残念です。
InnoDBは、他の人が書いているように、ファントム読み取りから保護する必要があります。
しかし、InnoDBには、ロックに関連する別の奇妙な動作があります。クエリがロックを取得すると、常に最新バージョンの行のロックが取得されます。だから、次を試してみてください
CREATE TABLE foo (i INT PRIMARY KEY, val INT);
INSERT INTO foo (i, val) VALUES (1, 10), (2, 20), (3, 30);
次に、2つの同時セッションで(2つのターミナルウィンドウを開きます):
-- window 1 -- window 2
START TRANSACTION;
START TRANSACTION;
SELECT * FROM foo;
UPDATE foo SET val=35 WHERE i=3;
SELECT * FROM foo;
REPEATABLE-READは、トランザクションの開始時に存在していたデータのみを2番目のウィンドウに表示することを意味するため、これは両方のSELECTでval = 10、20、30を示すはずです。
でも:
SELECT * FROM foo FOR UPDATE;
2番目のウィンドウは、行3のロックを取得するのを待ちます。
COMMIT;
これで、2番目のウィンドウのSELECTが終了し、val = 10、20、35の行が表示されます。これは、行をロックすると、SELECTに最新のコミットされたバージョンが表示されるためです。InnoDBのロック操作は、トランザクションの分離レベルに関係なく、READ-COMMITTEDで実行されるように機能します。
前後に切り替えることもできます。
SELECT * FROM foo;
SELECT * FROM foo FOR UPDATE;
SELECT * FROM foo;
SELECT * FROM foo FOR UPDATE;
InnoDBはマルチバージョン同時実行制御を使用するため、分離レベルのInnoDBエンジンのファントム読み取りを再現する可能性は疑わしいです-すべての行に対して、MVCCエンジンは、行が挿入および削除されたときのトランザクション番号を認識し、行の更新の履歴を再現できます。
したがって、同じこのトランザクションによって挿入、削除、または更新された行を除いて、結果として生じるすべてのSELECTステートメントは、トランザクションの開始時にテーブルの状態を表示します。他のトランザクションによってコミットされた新しい行は、このトランザクションよりも大きい挿入トランザクション番号を持つため、表示されません。行の範囲はここでは関係ありません。
マルチバージョン同時実行制御(この回答の執筆時点ではバージョン10.8.2.2)を使用していないため、ApacheDerbyデータベースの分離レベルREPEATABLEREADのPHANTOMREADSを再現できました。
再現するには、適切なトランザクションレベルを設定します(ij-DerbyのSQLクライアント)。
-- Set autocommit off
autocommit off;
-- Set isolation level corresponding to ANSI REPEATABLE READ
set isolation rs;
T1:
SELECT * FROM TableN;
T2:
INSERT INTO TableN VALUES(55, 1);
COMMIT;
再びT1:
SELECT * FROM TableN;
これで、T1にもう1行表示されます。
範囲ロックが存在しないためにファントム読み取りが発生する可能性があります。例は(擬似コード)です。
スレッド1
トランザクション1 TableN set X=2を更新します。ここでX=1 待つ(s1) X=1のTableNを選択します 専念
スレッド2
トランザクション2: tableN(id、X)に挿入values(55,1) 専念; 通知(s1)
ウィキペディアには、ファントム読み取りの別の例があります:ファントム読み取り|ウィキペディア
ここで重要なのはトランザクションの同期です。同期ポイントを使用できます。
mysqlスリープ関数を使用した編集例(テストされていません):
--on thread 1
Create TableN(id int, x int);
insert into TableN(id, X) values(1,1);
insert into TableN(id, X) values(2,1);
insert into TableN(id, X) values(3,1);
BEGIN TRANSACTION;
Update TableN set X=2 where X=1
SELECT SLEEP(30) FROM DUAL;
select TableN from where X=1;
COMMIT;
--In other thread, before 20 secs;
BEGIN TRANSACTION;
insert into TableN(id, X) values(55,1);
COMMIT;
Daniの良い答えを補完するために、MicrosoftSqlServerを使用してその動作を生徒に示すことができます。
SQL Serverは、ここのドキュメントで主張されているように、繰り返し可能な読み取り分離レベルでファントム読み取りを表示します。
Postgresは、ここで説明されているように、InnoDbと同じ概念にサブスクライブします。Postgresでも、繰り返し可能な読み取りでファントム読み取りが発生しないため、教訓的な目的にも適していません。
Sql Serverは、MySql InnoDbとPostgresが繰り返し可能な読み取りで行うことを実行する別の分離レベルであるスナップショットを提供します(これは、ファントム読み取りなしの繰り返し可能な読み取りのロックフリーのバージョンベースの実装ですが、シリアル化できません)。
Windowsマシンは必要ですが、SQLServerExpressは無料です。また、Windows Azureアカウントを取得して、オンラインのSqlAzureでその動作を示すこともできます。