2010-11-19 10 views
84

データベースとの整合性を保証し、SELECTとUPDATEが同期したままで、他の接続がそれに干渉しないことを確認するために、トランザクションテーブルとロックテーブルを少し混同しています。私が行う必要があります。MySQL:トランザクションとロックテーブルとの比較

SELECT * FROM table WHERE (...) LIMIT 1 

if (condition passes) { 
    // Update row I got from the select 
    UPDATE table SET column = "value" WHERE (...) 

    ... other logic (including INSERT some data) ... 
} 

私は他のクエリは、その接続が行を更新が終了する前に、同じSELECTが(「古い値」を読んで干渉しないと実行されますことを確認する必要があります

私がデフォルトにすることができます知っています。 LOCK TABLES tableに接続すると、一度に1つの接続しか行っていないことを確認し、完了したらロックを解除しますが、これは過度のようです。処理している間に別の処理が行われている場合)、またはSELECT ... FOR UPDATEまたはSELECT ... LOCK IN SHARE MODEが良いでしょうか。

答えて

124

ロックテーブルは、あなたがロックされてきた行/テーブルに影響を与えることから、他のDBのユーザーを防ぎます。しかし、ロック自体は、あなたのロジックが一貫した状態で出てくることを保証するものではありません。

銀行システムを考えてください。請求書をオンラインで支払うと、取引の影響を受けるアカウントが少なくとも2つあります。そして、お金が転送される受信者のアカウント。また、銀行の口座には、取引に課されたすべてのサービス料を喜んで預ける予定です。 (誰もがこれらの日を知っているとして)与えられた銀行は非常に愚かであることを、のは、彼らのシステムがこのように動作します言わせて:

$balance = "GET BALANCE FROM your ACCOUNT"; 
if ($balance < $amount_being_paid) { 
    charge_huge_overdraft_fees(); 
} 
$balance = $balance - $amount_being paid; 
UPDATE your ACCOUNT SET BALANCE = $balance; 

$balance = "GET BALANCE FROM receiver ACCOUNT" 
charge_insane_transaction_fee(); 
$balance = $balance + $amount_being_paid 
UPDATE receiver ACCOUNT SET BALANCE = $balance 

、ロックなしの取引がないと、このシステムは、最大の様々な競合状態に対して脆弱ですこれはあなたの口座で複数の支払いが行われているか、受取人の口座が並行して行われているかを示します。あなたのコードが残高を取得していて、huge_overdraft_fees()とそれ以外のものを実行している間に、他の支払いで同じタイプのコードが並行して実行される可能性があります。彼らはあなたの残高($ 100など)を取得し、トランザクション(あなたが支払っている$ 20を取り除きます、そして彼らはあなたを悩ませています)を行い、両方のコードパスに$ 80と$ 70ドル。最後に終了したものに応じて、$ 50 - $ 100 - $ 20 - $ 30の代わりに、アカウントの2つの残高のいずれかになります。この場合、「あなたの好意での銀行の誤り」。

ここで、ロックを使用するとします。請求書の支払い($ 20)が最初にパイプに当たって、アカウントレコードを獲得してロックします。今、あなたは排他的な使用を持っており、残高から20ドルを差し引いて、新しい残高を平和に書き戻すことができます。あなたのアカウントは、予想通り$ 80で終わります。しかし...うーん...あなたは受信者のアカウントを更新しようとすると、ロックされ、コードよりも長い時間ロックされ、トランザクションがタイムアウトします...私たちは愚かな銀行を扱っているので、コードはちょうどexit()を引っ張って、あなたの$ 20は電子のパフに消えます。今あなたは20ドル外れですが、あなたはまだ20ドルの手紙を受け取っています。あなたの電話は取り戻されます。

だからトランザクションを入力してください。あなたは取引を開始し、あなたの口座から$ 20を引き落とし、$ 20で信用を得ようとすると、何かが再び爆発する。しかし今度は、exit()の代わりに、rollbackとコードを実行するだけで、あなたの20ドルはあなたのアカウントに魔法のように追加されます。最後に

、それはこれに沸く:

ロックは、あなたが扱っているすべてのデータベースレコードに干渉から誰を保ちます。トランザクションは、「後の」エラーがあなたが行った「以前の」ことを妨げないようにします。どちらも単独では物事が最終的には正常に機能することを保証することはできません。しかし一緒に、彼らはします。

明日のレッスン:デッドロックの喜び。

+2

私はまだ/まだ混乱しています。受取人の口座に100ドルの口座があり、口座から20ドルの支払いを追加しているとします。私がトランザクションを理解することは、トランザクションを開始するときに、トランザクション内の操作では、トランザクションの開始時の状態でデータベースが認識されるということです。すなわち、私たちがそれを変更するまで、レシーバ口座は$ 100です。それで... 20ドルを追加すると、実際には120ドルの残高が設定されました。しかし、取引中に誰かがレシーバアカウントを$ 0にしてしまったらどうなりますか?これは何とか防止されていますか?彼らは魔法のように$ 120をもう一度手に入れますか?これもロックが必要な理由ですか? – Russ

+0

はい、それがロックが作用する場所です。適切なシステムはレコードの書き込みロックを行い、トランザクションの進行中に誰もレコードを更新できないようにします。編集的システムはレコードに無条件ロックをかけるので、誰も「失効した」残高を読むことはできません。 –

+1

基本的には、トランザクションをコードパス内のセキュリティで保護されたものとして扱います。ロックは、「並列」コードパスを介して物事を安全にします。デッドロックが発生するまで... –

0

は私が

START TRANSACTION WITH CONSISTENT SNAPSHOT; 

を開始するために使用したい、と

COMMIT; 

で終了します。

ストレージエンジンがトランザクション(InnoDB)をサポートしている場合、中間の作業はデータベースの他のユーザーと分離されます。

+1

彼が選択しているテーブルを除いて、特にロックしない限り(またはUPDATEが発生するまで)他のセッションにロックされることはありません。つまり、他のセッションがSELECTとUPDATEの間で変更される可能性があります。 –

+0

MySQLドキュメントのSTART TRANSACTION WITH CONSISTENT SNAPSHOTを読んだ後、同じ行を更新している別の接続を実際にロックアウトする場所が表示されません。私の理解では、トランザクションの開始時にテーブルが開始されていることがわかります。したがって、別のトランザクションが進行中で、既に行を取得して更新しようとしている場合、2番目のトランザクションは更新前の行を表示します。そのため、他のトランザクションが行われている行と同じ行を更新しようとする可能性があります。それは正しいのですか、私は進歩に何かが欠けていますか? – Ryan

+1

@Ryanロックしません。あなたは正しいです。ロックするかどうかは、操作の種類(SELECT/UPDATE/DELETE)によって決まります。 –

6

IF NOT EXISTS ...を試した後、複数のスレッドが同じテーブルを更新しているときに競合状態を引き起こしたINSERTを実行すると、同様の問題が発生しました。

は、私がここでの問題への解決策が見つかりました:私は、これは直接あなたの質問に答えていない実現 How to write INSERT IF NOT EXISTS queries in standard SQL

が、チェックを実行し、単一の文が非常に便利であるとインサートのと同じ原理を、更新を実行するためにそれを変更できる必要があります。

+1

+1プラットフォームに依存しない素晴らしいリンク記事です。 – Russ

13

トランザクション内にあるかどうかにかかわらず、通常SELECTはテーブルをロックしないため、トランザクション内にSELECT ... FOR UPDATEまたはSELECT ... LOCK IN SHARE MODEを挿入します。どちらを選択するかは、トランザクションの進行中に他のトランザクションがその行を読み取れるようにするかどうかによって異なります。

http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html

START TRANSACTION WITH CONSISTENT SNAPSHOT他のトランザクションがまだ一緒に来て、その行を変更することができるよう、あなたのためのトリックを行うことはありません。これは以下のリンクの上部に記載されています。

他のセッションが同時に 更新同じテーブルには[...]あなたは データベースに存在しなかっ 状態でテーブルが表示されることがあります。

http://dev.mysql.com/doc/refman/5.0/en/innodb-consistent-read.html

2

ロック&取引と混同しています。それらはRMDBの2つの異なるものです。ロックは、トランザクションがデータ分離に焦点を当てている間に並行操作を防止します。清算と優雅な解決策については、thisの素晴らしい記事をご覧ください。

+0

ロックは、他の人があなたと作業しているレコードを妨害しないようにします。簡単に説明し、後のエラー(並行して変更を加える他の人のもの)が前のことを妨げないようにします。何かを並行して)かなりのトランザクションを要約...これらのトピックの彼の理解について混乱しているものは何ですか? – steviesama

2

トランザクションの概念とロックは異なります。しかし、トランザクションはロックを使用してACIDの原則に従いました。 読み取り/書き込み中に他の人が同じ時点で読み取り/書き込みを行えないようにするには、ロックする必要があります。 データの整合性と一貫性を確保したい場合は、トランザクションを使用する方がよいでしょう。 私はロックとトランザクションの分離レベルのコンセプトが混在していると思います。 トランザクションの分離レベルを検索してください。SERIALIZEは必要なレベルにする必要があります。