テーブルをロックすると、他の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とそれらがあなたをねじ込んでいる$ 30を取り出す)、そして今や両方のコードパスは2つの異なる残高を持っています:$ 80と70ドル。どれが最後に終了するかに応じて、最終的に$ 100-$ 20-$ 30になるはずの$ 50の代わりに、アカウントのこれらの2つの残高のいずれかになります。この場合、「あなたに有利な銀行エラー」
ここで、ロックを使用するとします。請求書の支払い($ 20)が最初にパイプに当たるので、それが勝ってアカウントレコードをロックします。これで独占使用権を得て、残高から$ 20を差し引いて新しい残高を安心して書き戻すことができます...そして、アカウントは予想どおり$ 80になります。しかし...ええと...あなたはレシーバーのアカウントを更新しようとします、そしてそれはロックされ、コードが許可するよりも長くロックされ、トランザクションがタイムアウトします...私たちは愚かな銀行を扱っているので、適切なエラーではなく処理すると、コードは単にを引き出し、exit()
20ドルは電子のパフに消えます。これであなたは$ 20を使い果たしましたが、受信者に$ 20を支払う必要があり、あなたの電話は取り戻されます。
だから...トランザクションを入力してください。トランザクションを開始し、アカウントに$ 20を借方記入し、受信者に$ 20を入金しようとすると、何かが再び爆破します。しかし、今回はexit()
、コードの代わりに、コードで実行できるのでrollback
、$ 20がアカウントに魔法のように追加されます。
結局、それはこれに要約されます:
ロックは、処理しているデータベースレコードを他のユーザーが妨害しないようにします。トランザクションは、「後の」エラーが、実行した「以前の」ことを妨害しないようにします。どちらを使用しても、最終的に問題なく動作することを保証することはできません。しかし、一緒に、彼らはそうします。
明日のレッスン:デッドロックの喜び。