mysqliでTRUNCATE TABLE文を発行したら、commitされなかったでござるの巻き

August 4, 2012,
tags: mysql php


このエントリーをはてなブックマークに追加

PHPでのお話です。
マスター/スレーブまでカバーしたDBライブラリを使用しているときに、なぜかSELECTしたあとTRUNCATE文を発行したら、なぜか永遠に処理が終わらないという現象に遭遇しました。
SELECTはスレーブに接続して、INSERT,UPDATE,DELETEはマスターに接続するという単純なライブラリです。
まあちょっと事情があってライブラリのソースは公開できないんですが、クエリーログ的には以下のような感じです。

Connect	root@localhost on test
Connect	root@localhost on test
Query	begin
Prepare	SELECT name FROM `test` WHERE name IN ( ?,?)
Execute	SELECT name FROM `test` WHERE name IN ( 'abc','deg')
Close stmt
Query	commit
Query	begin
Query	TRUNCATE TABLE test

つまり、testテーブルにSELECTしたあと、TRUNCATEしたわけですね。
で、一見クエリー的には最後にcommitされない現象いがいは至って普通なわけです。

クエリー的には本当に普通なんですが、コード的にはちょっと微妙な感じです。
だいたいこんな感じです。

<?php

$config = array(
  'type'		=> 'mysql',
  'host'		=> 'localhost',
  'database'	=> 'test',
  'user'		=> 'root',
  'password'	=> 'root'
);

$mysqli = new mysqli($config['host'],$config['user'],$config['password'],$config['database']);
$mysqli2 = new mysqli($config['host'],$config['user'],$config['password'],$config['database']);

$mysqli2->query('begin');
$stmt = $mysqli->prepare("SELECT name FROM `test` WHERE name IN ( ?,?)");
if ( $stmt->errno  ) {
  exit("error");
}
$a = "abc";
$b = "deg";
$stmt->bind_param('ss',$a,$b);
if ( $stmt->errno ) {
  echo "error";
  exit;
}
$stmt->execute();
$stmt->close();
unset($stmt);
$mysqli2->query('commit');

$mysqli2->query('begin');
$mysqli2->query("TRUNCATE TABLE test");
$mysqli2->query('commit');

問題なのは$mysqliでSELECTのクエリ実行する前に$mysqli2でbeginしているところ。
これが非常に問題なのですが、まあ既存のライブラリを僕が使ってただけなので僕は(ry

ってことはどうでもいいんですが、以下のクエリをちゃんとマスター/スレーブ分割した環境で使用するならある程度は問題なく実行できるんですが、開発環境等で、スレーブ設定めんどくさくてスレーブ接続先もマスターにしてしまうってことがあるかと思います。

そこまで原因を完璧には把握していませんが、どうやら二つ接続しているのとMySQLサーバ側のプリペアドステートメントを利用していることが原因で、TRUNCATE文で詰まってしまうんだと思います。

ほかの言語だとどんな感じか今度試してみます。

ちなみにmysqlのautocommitの値は0になっています。

追記

他の言語で検証しました。
MySQLdbでTRUNCATE TABLE文を発行したら、commitされなかったでござるの巻き

comments powered by Disqus