23

2 つの部分からなる質問:

  1. 私の CodeIgniter スクリプトでは、トランザクションを開始し、行を挿入し、insert_id() を php 変数に設定し、新しい ID を外部キーとして使用して別のテーブルにさらに行を挿入し、すべてをコミットします。

    だから私の質問は: トランザクションを終了する前にすべてがコミットされない場合、何も挿入されていない場合、mysql はどのようにして最後の挿入 ID を返すことができますか? 私のスクリプトは (ほぼ) 完全に機能し、新しい ID は後続のクエリで使用されます。

    (「ほぼ」と言っているのは、PDO mysql ドライバーを使用すると、insert_id() を返すはずの最初の挿入が重複することがあるためです。2 回挿入されます。その理由は何か考えはありますか?それは最後の取得に関連していますか? ID? mysqli または mysql ドライバーを使用している場合は発生しません。)

  2. 最初にトランザクションなしでスクリプトを書いたので、次のような途中で mysql エラーをチェックするコードがあります。

    if(!$this->db->insert($table, $data)) {
        //log message here
    }
    

    すべての mysql コードをトランザクションにラップすると、これは mysql プロセスにどのように影響しますか? 目に見えるエラーは発生していませんが (上記の問題とは無関係であることを願っています)、削除する必要がありますか?

ありがとうございました。

4

1 に答える 1

32

あなたの最初の質問に答えるために...

トランザクションを使用する場合、接続に関する限り、クエリは通常どおり実行されます。コミットするか、それらの変更を保存するか、ロールバックしてすべての変更を元に戻すかを選択できます。次の擬似コードを検討してください。

insert into number(Random_number) values (rand()); 
select Random_number from number where Number_id=Last_insert_id();

// php

if($num < 1)
   $this->db->query('rollback;'); // This number is too depressing.
else
   $this->db->query('commit;'); // This number is just right.

生成された乱数は、コミットする前に読み取って、すべての人が見ることができるように保存する前に適切であることを確認できます(たとえば、行のコミットとロック解除)。

PDOドライバーが機能しない場合は、mysqliドライバーの使用を検討してください。それがオプションでない場合は、いつでもクエリ'select last_insert_id()asid;'を使用できます。$ this-> db-> insert_id()関数ではなく。

2番目の質問に答えるために、他のモデルが更新または読み取るデータを挿入または更新する場合は、必ずトランザクションを使用してください。たとえば、列'Number_remaining'が1に設定されている場合、次の問題が発生する可能性があります。

Person A reads 1
Person B reads 1
Person A wins $1000!
Person A updates 1 to be 0
Person B wins $1000!
Person B updates 0 to be 0

同じ状況でトランザクションを使用すると、次の結果が得られます。

個人Aがトランザクションを開始します個人Aが
Number_remainingから「1」を読み取ります(更新の選択
が使用さ れている場合、行はロックされます)個人BはNumber_remainingを読み取ろうとします-待機を強制されます個人Aは$1000を獲得します個人Aは1を0に更新します個人Aは個人をコミットしますBは0を読み取りますBは$1000を獲得しませんBは泣きます







トランザクション分離レベルについても確認することをお勧めします。

この場合に発生する可能性のあるデッドロックに注意してください。

人物Aは行1を読み取ります(select ... for update
人物Bは行2を読み取ります(select ... for update
人物Aは行2を読み込もうとします、強制的に待機
します人物Bは行1を読み込もうとします、強制的に待機します
人物Aはinnodb_lock_wait_timeout(デフォルトは50秒)に到達し、切断さ
れます行1で、通常どおり続行します

最後に、人物BはおそらくPHPに到達しているmax_execution_timeため、現在のクエリはPHPとは独立して実行を終了しますが、それ以上のクエリは受信されません。これがautocommit=0のトランザクションであった場合、PHPサーバーへの接続が切断されると、クエリは自動的にロールバックされます。

于 2013-03-14T05:21:42.170 に答える