嵐のコンサートがあるとダブルブッキングしてしまうホテル予約システムを作ってみた今年の5月1日に、仙台市内のホテルで多重予約のトラブルが発生したと報道されています。部屋数203室の仙台市のビジネスホテルで、9月18~23日の宿泊予約を数千件受け付けるトラブルがあった。アイドルグループ「嵐」のライブが宮城県内で開催される期間だった。インターネットでの申し込みが殺到し、システム障害が起きたとみられるという。トラブルがあったのは、仙台市泉区の「ホテルルートイン仙台泉インター」。ホテルなどによると、9月19、20、22、23日に宮城スタジアム(宮城県利府町)で嵐がライブを開くことが明らかになった後の5月1日午前5時ごろ、ネットを使った予約申し込みが殺到していることに気づいたという。引用5月1日の朝に何があったのか調べてみると、この日の早朝にテレビや新聞でコンサートの情報が流れたようですね。引用つまり、こういうことのようです。当該ホテルがシステム障害を起こし、多重予約に至るでは、このような多重予約がなぜ発生してしまったかですが、その原因は公表されていないので憶測するしかありません。「ありそうな可能性」としては、排他制御が十分でなかったのではないかという仮説が成り立ちます...ということで、表題のように、「嵐のコンサートがあるとダブルブッキングしてしまう予約システム」を作ってみました。予約システムの概要まずはテーブル定義です。rooms_availableは宿泊日と客室タイプ毎の空き部屋数、transaction_logは予約の履歴を保持しています。AUTO_INCREMENT=1;続いて、客室予約のスクリプトです。宿泊日と顧客番号(整数)を指定して、予約を申し込むものです。処理の性質上POSTリクエストにすべきものですが、テストの都合でGETにしています。空室数を1減じてDBに書き戻す。'ただいまアクセスが集中しております。}処理の大まかな流れは下記の通りです。rooms_availableテーブルの状態は下記のとおりです。連続的に予約を行い、嵐ファンの予約の様子をシミュレーションしました。siegeを用いた理由は、リクエスト毎にパラメータをファイル指定で変化させやすいからです。負荷テストの模様を映像で紹介します。画面上半分がsiegeを実行している様子。下半分は宿泊日毎の残室と予約済室数のモニタです。1,218件(203×6)の予約しか受け付けられないはずのところ、約4,400件の予約が成立しました。多重予約が発生してしまう原因は、排他制御の不備にあります。以下は、リクエストAとリクエストBが同日の宿泊予約をほぼ同時に要求している場合の模式図です。上図のように、空室数を同時に(並行して)求めて、それぞれ独自に空室判定をしているところに問題があります。同じ日付の空室については、1つのスレッドのみが判定をしなければ多重予約が起きる可能性があります。空室数を求めてから、空室数をアップデートするまでは排他的に処理を行う必要があります。このような箇所はクリティカルセクションと呼ばれます。上記現象の対策としては排他制御をきちんとするということになりますが、MyISAMはトランザクションに対応していないので、対策方針としては以下の二案が考えられます。MyISAMのままテーブルロックを使用するストレージエンジンをInnoDBに変更して、トランザクションと行ロックを使用するどちらでも対策は可能ですが、予約システムは更新処理が多いという特性から、InnoDBとトランザクションを用いるほうが一般的でしょう。ストレージエンジンをInnoDBに変更トランザクション処理を利用するためには、ストレージエンジンとしてMyISAMではだめで、InnoDBを用いる必要があります。TABLE文のENGINE=MyISAMとなっている2箇所をENGINE=InnoDBに変更します。トランザクションの使用と行ロック次に、トランザクションの使用と行ロックを指示します。PDO+MySQLの組み合わせの場合、デフォルトではSQLの呼び出しのたびにトランザクションを開始して自動的にコミットされます(オートコミット)。これでは排他制御ができないので、beginTransaction()メソッドで明示的にトランザクションを開始して、commit()メソッドでコミットします。UPDATE」を指定することで、選択された行に対して行ロックを行います。予約ができなかった場合や例外を捕捉した場合は、ロールバックにより処理を取り消します。行ロックはコミットあるいはロールバックにより解除されます。'ただいまアクセスが集中しております。たってからアクセスしてください';}改修後の実行結果は以下の通り、各日の予約数は203となり、ダブルブッキングは生じなくなりました。|+----+------------+-----------+-----------+----------+この際の処理の流れは以下のように並行ではなく、排他的な処理になり、一貫性は維持されます。PHP入門書での対応PHP入門書ないし中級レベルの解説書では、通常トランザクション処理や排他制御の解説はありません。トランザクションの簡単な説明がありますが、具体例などは説明されていないようです。トランザクションの詳しい説明がありますが、これは著者の石井達夫氏がPosgreSQLの専門家なので当然かもしれません。まとめ嵐のコンサート情報が引き起こしたホテル多重予約問題を題材として、トランザクションと排他制御の重要性について説明しました。当該の事故の原因はおそらくもっと複雑なものであろうと予想しますが、ここで説明したレベルの単純な排他制御であってもPHPの入門書等では解説されていないので、初めて知ったという読者も多いのではないでしょうか。ホテル予約のダブルブッキングがセキュリティ上の問題かといえば、広義に捉えればセキュリティ上の問題と言えると思いますが、排他制御不備が個人情報漏洩等につながる場合もあり、セキュリティに直結する場合もあります。このため、拙著4.15節では、「共有資源に関する問題」として(SQLの問題ではありませんが)排他制御の重要性について説明しています。【HASHコンサルティング広告】HASHコンサルティング株式会社は、セキュリティエンジニアを募集しています。興味のある方は、twitterやfacebookのメッセージ、あるいは問い合わせページからお問い合わせください。