
このカテゴリには初めて投稿いたします。
どうにも解決ができないため、質問させていただきました。
cakephpで、注文番号の連番発行のために、今回初めてトランザクション処理が必要なケースが出てきまして、
ネットで検索して出て来る情報を元に、そのとおりに記述しているのですが、どうやってもうまく行きません。
DBのテーブルはInnoDBになっています。
参考にしたサイトは例えば
http://wataame.sumomo.ne.jp/archives/3812
などです。
--------------------実際のコード
-----以下はあるモデル内(ここでは、SamplemodelDataとしています)に記述した関数での処理です。$thisはそのモデルを示します。
$dataSource = $this->getDataSource();
$dataSource->begin($this);
$res = $this->find('first',array('conditions'=>array('classification'=>$classification,'commoncode'=>$commoncode)));
if($this->getNumRows()==0){//レコードなしの場合
$returnNumber = 1;
$this->save(array('classification'=>$classification, 'commoncode'=>$commoncode, 'number'=>1));
}else{
$res['ExsamplemodelData']['number'] += 1;
$returnNumber = $res['SamplemodelData']['number'];
$this->set('id', $res['SamplemodelData']['id']);
$this->saveField('number', $returnNumber);
}
$dataSource->commit($this);
return $returnNumber;
--------------------
■目的
ユーザーが同時刻に何人同時に注文しようと、注文番号を重複させずに注文番号を採番することが目的です。
極端なことを言えば、ある同じ時刻(秒まで一緒)に世界各国から100人同時に全く同じタイミングで注文が入っても、注文番号を重複させないようにしたいです。
※DBのシリアルを使えば確実に重複させないようにできることは知っています。しかし、今回は単純に連番だけでなくいろいろなケースにおいて意味をもつ文字列も付与したものをプライマリキーとしているので、単純なシリアルではだめなのです。
■うまくいかない点
・上記の記述でも、トランザクション自体は機能しているようです。
最後の $dataSource->commit($this); をコメントアウトにすると、DBの番号が永遠にインクリメントされませんので。
・begin ~ commit までの間に、他のスレッドで、
$this->find('first',array('conditions'=>array('classification'=>$classification,'commoncode'=>$commoncode)));
が実行されると、インクリメントされる前の番号が返されるのです。
=>それよりも前のスレッドが begin をした瞬間からcommitするまでは、他のスレッドでfindしても、待ち状態になって欲しいのです。
・検証用プログラムで、上記の処理を、2つのブラウザから同時に100回繰り返す(2つ併せて200回繰り返し処理させる)と、
毎回200件中、5~7件程度、番号が重複してしまいます。
■質問内容
・このような精度を求めるようなケースでは、cakephpでトランザクション処理をしても、もともと無理な要望なのでしょうか?
・上記の記述で不足している部分は何でしょうか?例えば、mysqlのトランザクションには他スレッドから、updateだけを禁止にする指定と、updateとselectも禁止にする指定ができるようですが、上記の記述だと他スレッドではupdateしか禁止されていないために、selectであるfindは待ちが発生しないということなのでしょうか?しかし、selectも禁止にするとかそういう指定方法がどう探してもそういう情報が見つけられませんでした。
要約すると、つまり、
「cakephpでトランザクション処理(beginからcommitの間は、他スレッドからはupdateもselectも禁止)にする方法はどうやったらよいのでしょうか?」
ということでございます。
ご存じの先生方、是非、お力お貸しいただけますでしょうか。
何卒よろしくお願いいたします。
No.2ベストアンサー
- 回答日時:
find('first', のとき、降順設定しないと、取得する先頭1行目は最小値だけど、そこは設定してますか?
ちゃんと最大値を得るようになっていたとして、あとは、cakePHPよりは、MySQL innodb の問題じゃないかな。
http://dev.mysql.com/doc/refman/5.1/ja/innodb-lo …
innodb のtransaction は、defaultは REPEATABLE READ で、select 文でのデータ取得時は、他者に読み書き可能なので、これを防ぐには、先に発行される select文に for update が必要です。これで、他者の読み書きをブロックします。
Modelの findメソッドでは、それを追加するすべはないので(接続先データベースによって対応の違う物は実装されていない)
よって、 Model->query(string $sql ,array $placedata) メソッドで直接SQL文を渡して実行することになります。
プレースホルダーも使えるので、pdoでのプレースホルダー指定方法に則って作成するとよいです。
SQL例
SELECT max( number ) as number FROM exsample_models where classification=? and commoncode=? FOR UPDATE;
mpro-gram様
詳細のご指南ありがとうございました。
いただいた情報にて、全て解決することができました。
すべては、mpro-gram様のご回答のおかげでございます。
検証で1000回繰り返し×3ブラウザで同時 で合計3000回処理させても1件も重複しなくなりました!
(別途、Model->queryで検証した際に2回目以降の同じクエリだとキャッシュが有効になることを知らなかったため、少し嵌ってしまいましたが、それも方法が分かり解消できました。)
cakephpの標準コードだけでは、for updateの完全な排他制御はできないのですね。
本当に感謝申し上げます。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- 英語 「this is the/my first time~」の文法上の制約について 1 2023/04/06 09:48
- 英語 共通の前置詞の目的語を持つ前置詞句を列挙する際の表現方法について(省略の位置と方法) 3 2023/08/24 09:40
- Visual Basic(VBA) ExcelからAccessのテーブルに書き込む時に時間がかかる 1 2022/10/14 20:38
- Visual Basic(VBA) batにて、異なるフォルダの同名ファイルを参照し、文字列を判別。擬似配列で変数へ格納 3 2022/04/10 03:37
- 英語 "this day"が当日か今日を表すのかの使い分け方等について 5 2023/08/08 14:29
- PHP if(preg_match("/[^0-9]/",$gu_d)){意味を教えてください。 1 2022/05/06 05:37
- Ruby pandasでsqlite3にテーブル作成・追加・読み出しでindexの取り扱い方教えてください 5 2023/03/08 09:57
- PHP コメント機能に返信欄を矢印で追加したい 1 2022/05/09 21:17
- Visual Basic(VBA) 【前回の続き続きです、ご教示ください】VBAの記述方法がわかりません。 2 2022/08/24 20:49
- 英語 「名詞+形容詞/現在分詞/過去分詞+to不定詞の副詞的用法」の語順について 7 2022/09/22 05:27
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
pascalについて知りたいのです...
-
ファイルの書き込みについて教...
-
mb_encode_numericentityでタグ...
-
C言語の配列をPush(追加)する...
-
cakephpでのトランザクション処...
-
C言語 最大値と最小値を求めて...
-
pythonのnumpyでの繰り返しでの...
-
phpの掲示板で新しい順に表...
-
ネストが深い時のforeachはどう...
-
file_existsでファイル名の部分...
-
$_SESSIONに二次元配列を使える...
-
String だと「 ByRef引数の型が...
-
ftokが動かない?
-
ラジオボタンをランダムに表示...
-
プルダウンメニューにDBの内容...
-
PHP掲示板で新着順に表示させた...
-
PHPからCSVをアップロード後、m...
-
配列をmb_ereg_replaceで一気に...
-
C言語でCSVファイルの行数を読...
-
ヒアドキュメントの中のfor文
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
C言語の配列をPush(追加)する...
-
行列
-
CArrayの要素としてCStringArra...
-
Texでの数式
-
読み(あ行~わ行)ごとに分け...
-
CArrayのソート
-
C言語 最大値と最小値を求めて...
-
fgetc関数について
-
PHPのmin関数、「1」以上の数値...
-
連想配列の文字コードを変換
-
STLのvectorで作った配列をメン...
-
C言語のchar ポインタ
-
C言語で全角文字の扱いについて
-
配列を指定した順序でソート
-
php で1から100までの素数の表...
-
delphi 2次元配列がわかりません
-
np.stack()とnp.array()の違い
-
array_intersectで空欄を比較し...
-
cakephpでのトランザクション処...
-
verilogで、配列の一部をタスク...
おすすめ情報