質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

WordPress

WordPressは、PHPで開発されているオープンソースのブログソフトウェアです。データベース管理システムにはMySQLを用いています。フリーのブログソフトウェアの中では最も人気が高く、PHPとHTMLを使って簡単にテンプレートをカスタマイズすることができます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Q&A

解決済

1回答

5194閲覧

トランザクションとテーブルロックはどちらが先ですか?

yakan

総合スコア19

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

WordPress

WordPressは、PHPで開発されているオープンソースのブログソフトウェアです。データベース管理システムにはMySQLを用いています。フリーのブログソフトウェアの中では最も人気が高く、PHPとHTMLを使って簡単にテンプレートをカスタマイズすることができます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

0グッド

1クリップ

投稿2020/05/31 01:45

###問題が発生する状況
以下test_insert_table2()という関数は、wp_mainswp_sub1sという2つのテーブルを更新し、片方でもエラーになれば両方共ROLLBACKするという処理です。

この書き方はQiitaのこちらのページ(https://qiita.com/atomo/items/b96ad8808af7e5c8760b)を参考にしました。

ですが、wp_sub1sでエラーを起こした場合、なぜかROLLBACKが処理されずwp_mainsだけが更新されてしまうという問題が発生しました。
$main_id=999の場合は文末の「テーブル構造」によって外部キーエラーが起こります。)

php

1$result2 = test_insert_table2(); 2var_dump($result2); 3 4function test_insert_table2(){ 5 global $wpdb; 6 $result = ['insert_id'=>null,'errors'=>[]]; 7 $user_id = get_current_user_id(); 8 9 // トランザクションとテーブルロック 10 $wpdb->query('START TRANSACTION'); 11 $wpdb->query('LOCK TABLES wp_mains WRITE, wp_sub1s WRITE'); 12 13 // 保存 14 $result_mains = $wpdb->insert( 'wp_mains', ['users_ID'=>$user_id,'content' =>'本文'], ['%d','%s'] ); 15 $main_id = 999; // 999 は存在しないのでエラーになる 16 //$main_id = $wpdb->insert_id; // これならエラーにならない 17 $result_sub1s = $wpdb->insert( 'wp_sub1s', ['mains_ID'=>$main_id,'title'=>'タイトル'], ['%d','%s'] ); 18 19 if($result_mains && $result_sub1s) { 20 $result['insert_id'] = $main_id; 21 $wpdb->query('COMMIT'); 22 $wpdb->query('UNLOCK TABLES'); 23 } 24 else { 25 $result['errors'] = ['$result_mains'=>$result_mains,'$result_sub1s'=>$result_sub1s]; 26 $wpdb->query('ROLLBACK'); 27 $wpdb->query('UNLOCK TABLES'); 28 } 29 return $result; 30}

###解決した方法
一晩格闘し、ふと何の根拠もなくトランザクションとテーブルロックの順番を下記のように逆にしたところ、無事ROLLBACKが起こりました。

php

1 // $wpdb->query('START TRANSACTION'); 2 // $wpdb->query('LOCK TABLES wp_mains WRITE, wp_sub1s WRITE'); 3 // ↓ 4 $wpdb->query('LOCK TABLES wp_mains WRITE, wp_sub1s WRITE'); 5 $wpdb->query('START TRANSACTION');

質問事項

そこで質問なのですが、トランザクションとテーブルロックの順番は、
1つ目のコードのように「トランザクション→テーブルロック」ではなくて
2つ目のコードのように「テーブルロック→トランザクション」であっているのでしょうか?

MySQLのドキュメンテーションは読みましたが、この点についての言及がなく(あるのに読解できないせいか)、よく理解できません。

そして1つ目のコードでは、なぜwp_mainsだけが更新されROLLBACKが処理されないのか、原因がわかる方がいらっしゃいましたら教えて頂けませんでしょうか。

テーブル構造

尚、上記wp_mainswp_sub1sのテーブル構造は次のようになっているので、$main_id=999の場合は外部キーエラーが起こるといった仕組みです。

php

1function test_create_table(){ 2 global $wpdb; 3 require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); // dbDelta() のため必要 4 5 // wp_mains テーブルを作成 6 $sql = "CREATE TABLE wp_mains ( 7 `ID` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT 8 ,`users_ID` BIGINT(20) UNSIGNED NOT NULL 9 ,`content` VARCHAR(1000) 10 ,PRIMARY KEY (`ID`) 11 ,FOREIGN KEY (`users_ID`) REFERENCES wp_users(`ID`) 12 );"; 13 add_option($table_name."_version", '1.0'); 14 dbDelta($sql); 15 16 // wp_sub1s テーブルを作成 17 $sql = "CREATE TABLE wp_sub1s ( 18 `mains_ID` BIGINT(20) UNSIGNED NOT NULL 19 ,`title` VARCHAR(50) NOT NULL 20 ,PRIMARY KEY (`mains_ID`) 21 ,FOREIGN KEY (`mains_ID`) REFERENCES wp_mains(`ID`) 22 );"; 23 add_option($table_name."_version", '1.0'); 24 dbDelta($sql); 25}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hope_mucci

2020/05/31 03:48

データ挿入後の判定処理はcommit側に進んでいるのかrollback側に進んでいるのかどちらですか?autocommitの設定は0,1のどちらでしょうか?
yakan

2020/05/31 04:41

返信は頂戴したご回答へのコメント欄に記載させて頂きました。宜しくお願い致します。
guest

回答1

0

ベストアンサー

1番目の質問だけ回答します。
テーブルロックの方法は「トランザクション→テーブルロック」が正解です。
なぜならSTART TRANSACTIONを実施すると既存のロックは全て解消されてしまうからです。つまり、テーブルロックを先に行っても意味なし。
質問者がリンクを張ったMySQLのドキュメントにしっかり明記されていますのでもう一度読んでみてください。

根本原因は追記依頼にも書きましたが、きちんとロールバック処理に進んでいればautocommitが怪しく、コミット処理側に進んでいればsub1へinsertした際の戻り値が怪しいです。

追記

コメントより、以下の方法でうまく動作したそうです。autocommitの値を変更する方法。

php

1$wpdb->query('SET autocommit=0'); // これで0を指定 2// $wpdb->query('START TRANSACTION'); // これは不要 3$wpdb->query('LOCK TABLES wp_mains WRITE, wp_sub1s WRITE');

投稿2020/05/31 03:51

編集2020/05/31 05:16
hope_mucci

総合スコア4447

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yakan

2020/05/31 04:01

>1番目の質問だけ回答します。 なるほど順番について理解できました。どうもありがとうございます。
yakan

2020/05/31 04:13 編集

>根本原因は... >データ挿入後の判定処理はcommit側に進んでいるのかrollback側に進んでいるのかどちらですか? エラーの際はきちんとロールバック処理に進んでいることを、下記のerror_logで確認しました。 else { error_log('ロールバック処理に進んでいる'); $result['errors'] = ['$result_mains'=>$result_mains,'$result_sub1s'=>$result_sub1s]; $wpdb->query('ROLLBACK'); $wpdb->query('UNLOCK TABLES'); } ということは「>autocommitが怪しく」になるのかと思いますが、そうなると >autocommitの設定は0,1のどちらでしょうか? という設定がよくわかっておりません。 調べたところ、1だと自動でコミットされてしまうためROLLBACKが効かないということになるのでしょうか? phpMyAdminの画面で SELECT @@autocommit; を実行すると「1」と表示されましたが、この確認方法であっていますか? しかし、質問の処理についてのautocommitを確認すべきですから、ここで「1」と表示されても意味ないのでしょうか?
hope_mucci

2020/05/31 04:11

autocommitの確認方法はあっています。 1だとコマンドを発行するたびにcommitが発生します。トランザクション開始中はその限りではないですが。 MySQLのドキュメントにも明記されていますが、一番下に書かれている「正しい方法」でもう一度やってみてはどうでしょうか(すなわち START TRANSACTIONするのではなくSET autocommit=0から始める方法で)
yakan

2020/05/31 04:14

ご返信どうもありがとうございます。 「正しい方法」を見ていませんでした。早速やってみます。
yakan

2020/05/31 05:16 編集

仰る通り「正しい方法」にある以下のコードでできました。誠にありがとうございます!本当に助かりました…。 $wpdb->query('SET autocommit=0'); // これで0を指定 // $wpdb->query('START TRANSACTION'); // これは不要(0にした場合のトランザクションは、SQL文の実行によって暗黙的に開始するため) $wpdb->query('LOCK TABLES wp_mains WRITE, wp_sub1s WRITE'); 一つよろしいでしょうか。上記ではautocommit=0と指定しているわけですが、指定しなければできないということは通常の処理では1になっているということかと思います。 なので質問のコードが済んだ時点で1に戻すべきかと思うのですが、戻すならば以下のようにUNLOCK TABLESの後に戻すような流れであっているでしょうか? $wpdb->query('UNLOCK TABLES'); $wpdb->query('SET autocommit=1'); // これで1に戻す それともこれは自動的に1に戻るような仕組みなのでしょうか? といいますのは、今改めてphpMyAdminの画面で SELECT @@autocommit; を実行すると、やはり「1」と表示されまして、 ということは先ほどの $wpdb->query('SET autocommit=0'); // これで0を指定 は自動的に1に戻るような仕組みなのかと考えたためです。
hope_mucci

2020/05/31 05:15

autocommitはデフォルト1で、DBとの接続が切れるまで有効になり、また別の接続には影響しません。 しかし、おそらく$wpdbは接続しっぱなしなのでcommit/rollbackした後はautocommitを1に戻しておくことをお勧めします。0のままだと別のページで影響が出るかもしれませんので。
yakan

2020/05/31 05:21

なるほど、重ねがさねありがとうございます。 そして誠に僭越ながら、先のリンクの文末に次のように書かれてありました。 >autocommit = 1 を指定すると、LOCK TABLES の呼び出しの直後に InnoDB によって内部のテーブルロックが解放され、デッドロックが非常に発生しやすくなる場合があるため、この指定は行わないようにしてください。 これに従い1にはしない方がいいのか、それとも仰るように「>$wpdbは接続しっぱなし」に鑑みて1にした方がいいのか、判断がつきません。 そこでお尋ねしたいのですが、「>別ページでの影響」とは例えばどのようなものでしょうか。
hope_mucci

2020/05/31 06:46

autocommit=1の状態でLOCKをすると悪影響が出かねない、ということなので次のLOCKが発生する前に0に戻してやればよい、ということになります。 別ページでの影響というのは、複数のページに同時にアクセスした場合にロックやautocommitの競合が発生することがあるかもしれないという懸念ですが、あまりphpの挙動に詳しくはないのですがphpはシングルスレッドで動く(同時にリクエストが発生しても処理自体は並行で動かない、順番待ちをする)ということなのでphpでは影響がないのでしょう。javaなど他の言語で開発している身としては恐ろしい話なのですが。
yakan

2020/05/31 07:13

なるほど。最後まで丁寧に教えて下さいまして誠にありがとうございます。大変勉強になりました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問