Apache2.xとPHP4.3.xとMySQL4.1.xを使用しています。
InnoDBで作成したテーブルがあります。
あるデータをselectして
その結果によってupdateをかけ、
その後、また、selectし、
その結果によって、全体の処理を巻き戻したりしたいと考えています。
さて、このとき、別ユーザーから同時に接続されると結果がおかしくなるので、
LOCK TABLES・・・を使います。
あとで、巻き戻す(ROLLBACK)ために、
最初にBEGINしたいのですが、
LOCK TABLESの後にBEGINすると、テーブルのロックが解除されてしまいます。
LOCK TABLESとBEGIN~ROLLBACKを併用することは不可能なのでしょうか?
また、不可能な場合、別の方法などがあれば教えてください。
コメント(6件)
LOCK TABLESは以下の制限があります。
以下の注記なのでトランザクション中
では使うと副作用があるのでlock tables
とトランzクション制御は同時に使う事は
出来ません。
注意: LOCK TABLES はトランザクションセーフではありません。アクティブなトランザクションは、テーブルロックの試行前に暗黙的にコミットされます。
http://dev.mysql.com/doc/refman/4.1/ja/lock-tables.html
http://dev.mysql.com/doc/refman/4.1/en/lock-tables.html
排他するだけなら。
begin
select xxxx for update;
update xxx;
select xxx for update;
update xxxx;
rollback or commit;
但し参照SQLは
select xxx lock in shared mode;
を全て記述する必要がある。
LOCK TABLESを使う場合は、
set AUTOCOMMIT = 0
begin
lock tables xxx write
select xxx
commit or rollback
unlock tables
参照側
http://dev.mysql.com/doc/refman/4.1/en/innodb-and-autocommit.html
set AUTOCOMMIT = 0
select xxxx
> 排他するだけなら。
> begin
> select xxxx for update;
> update xxx;
> (※1)
> select xxx for update;
> update xxxx;
>
> rollback or commit;
このとき、※1のタイミングで別ユーザーが同じ処理をした場合、整合性があわなくなる可能性がありますよね。
-*--*--*--*--*--*--*--*--*--*--*--*--*-
> LOCK TABLESを使う場合は、
> set AUTOCOMMIT = 0
> begin
> lock tables xxx write
> select xxx
> (※2)
> commit or rollback
> unlock tables
この場合、※2でupdate or insertしたとき、rollbackしても戻らないんですよね。
-*--*--*--*--*--*--*--*--*--*--*--*--*-
リンクを確認したところ、やはり、LOCKとROLLBACKは同時には機能できないようですね。
InnoDBであればfor updateするとロック
されて通常同時には実行できません。
InnoDB以外では、begin/commit/rollback
等はトランザクション機能しません。
(99999は最大待ち時間)
SELECT GET_LOCK("TABLE",99999);
begin
・・・
commit or rollback
select RELEASE_LOCK("TABLE");
>※2でupdate or insertしたとき、
>rollbackしても戻らないんですよね
それはおかしい。
4.1.22で試してみましたが正しく処理されます。
※1、※2ともに動作がおかしいテーブルが
InnoDBであればfor updateするとロック
される。
テーブルがInnoDBではないと思われます。
show create table xxx;
確認するか
alter table xxx engine=innodb;
としてInnoDBに変換する。
http://dev.mysql.com/doc/refman/4.1/ja/innodb-transaction-isolation.html
いろいろ実験している間に、InnoDBからMyISAMに変更したことを忘れてしまっていました。
InnoDBで試したところ、正常に動作しました。
申し訳ございません、お騒がせいたしました。
ありがとうございました。
200ポイント送付しておきましたので、ご確認ください。
厳密には以下の問題があります。
(MYSQLも同様の考え)
リードコミッティド隔離レベル
http://www.postgresql.jp/document/pg823doc/html/transaction-iso.html
テーブルロックしたくない場合
ロック用のテーブルを別に作り
create table locktable(
id int , primary key(id),
lock datetime,
ref int default 0
)
insert into locktable(1,now(),0);
insert into locktable(2,now(),0);
insert into locktable(3,now(),0);
insert into locktable(4,now(),0);
最初にロックテーブルの特定カラムを
強制的にロック。
begin
update locktable set lock=now(),ref=ref+1 where id=1
...
commit or rollback
する方法があります。