web-dev-qa-db-ja.com

mysqlの最後の挿入IDの取得はトランザクションでどのように機能しますか? +取引に関する質問

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プロセスにどのように影響しますか?それは目に見えるエラーを引き起こしていません(うまくいけば、上記の問題とは無関係です)が、削除する必要がありますか?

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

22
timetofly

最初の質問に答えるには...

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

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()as id;」をいつでも使用できます。 $ 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」を読み取りました
select for update が使用されている場合、行はロックされます)
人物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秒)に達し、切断されます
人物Bは行1を読み取り、通常どおり続行します

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

30