skimemo


Laravel-20190113

_ insertGetIdはスレッドセーフなのか?

DBにデータをinsertした後、Auto Incrementのフィールドに何が入ったかを知りたいケースがあります。そんな時に便利なのが、insertGetId()です。(自動増分)

ソースを見ると以下のように書かれており、insert後にPDO::lastInsertIdを実行して取得していることが分かります。

  1
  2
  3
  4
  5
  6
  7
  8
public function processInsertGetId(Builder $query, $sql, $values, $sequence = null)
{
    $query->getConnection()->insert($sql, $values);
 
    $id = $query->getConnection()->getPdo()->lastInsertId($sequence);
 
    return is_numeric($id) ? (int) $id : $id;
} 

ここで気になるのは、別クライアントから同時にアクセス(実行)され、insert(1) - insert(2) - lastInsertId(1) - lastInsertId(2) という順序で処理が走った場合、結果の整合性は補償されるのか? という点です。

そこで、以下のように検証してみました。

_ テストコード

簡単なテスト用のテーブルを作って(下記ではlog用のテーブルを流用して)繰り返しinsertするコードを書きます。
これは「php artisan command:test」で実行されるように書きました。

  1
  2
  3
  4
for($cnt=0;$cnt<10;$cnt++){
    $result = DB::table('logs')->insertGetId([適当な内容]);
    echo $result."\n";
} 

_ 呼び出すバッチファイル

test.bat

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
start cmd /c sub.bat
start cmd /c sub.bat
start cmd /c sub.bat
start cmd /c sub.bat
start cmd /c sub.bat
start cmd /c sub.bat
start cmd /c sub.bat
start cmd /c sub.bat
start cmd /c sub.bat
start cmd /c sub.bat


sub.bat

  1
  2
php artisan command:test
pause

_ 検証

仮に上記のような順序で実行され、「lastInsertId(1) - lastInsertId(2)」のように連続でlastInsertId()が走るのであれば、同じ番号が返ってくるケースが生じることになります。
test.batを実行すると10個のDOS窓が同時に開き、各々で同時並行的に10回ずつinsertが走ることになりますが、各窓に表示される数字が全て重複していなければ、概ね問題無いと考えられます。

果たして結果は、全てuniqueな値が100個返ってきました。
中身はtransactionになってるんでしょうかね(ちゃんと読んでない・・・)。


Last-modified: 2019-02-10 (日) 18:50:06 (161d)