演習として、私は複数の会議室用のシンプルなカレンダー予約システムを設計しようとしています。特定の時間帯に空いている部屋を見つけたり、部屋の予約を調べたりするなど、いくつかの要件に頭を悩ませました。ただし、特定のレース状況で、2人のユーザーが部屋Aをまったく同じ時間に予約しようとしているため、予約シナリオに少し行き詰まっているようです。選択する時間範囲はまったく同じか、または重複している可能性があります。たとえば、ユーザー1は部屋Aを午前10時から午後12時まで予約しようとし、ユーザー2は部屋Aを午前9時から午前11時まで予約しようとしています。空室状況を調べるときに、この部屋は両方のユーザーに表示され、要求された時間枠で空室があると述べました。この場合、それらが重複しているため、1つの予約のみを受け入れ、もう1つの予約は失敗する可能性があります。簡単にするために、ユーザー設定は指定せず、先着順です。この部分を効率的に解決するにはどうすればよいですか?私はいくつかの方法でこれに取り組むことを考えていました:
リクエストで予約を通過させ、キューを使用して予約を後処理します。予約をデキューするたびに、実際にDBに挿入して予約を確認する前に、空室状況チェックを実行します。提案シナリオでは、1つの予約のみが最初にキューに入ることができるため、2番目の予約は失敗します。その後、システムは方向を変え、予約が失敗したか成功したかをユーザーに通知します。 (これはOutlookが会議室の設定を処理する方法に似ていることに気付きました)。しかし、これはもうすぐです。これにより、キューを単一スレッドで処理するよう強制され、順序を維持できます。2つのスレッドがそれらをデキューする場合、2つのスレッドが同じ結果を表示するようになったという円に戻りました。このプロセスを配布する予定なので、状態を再確認します。
時間帯をロックしてみてください。しかし、これは私が部屋の時間帯を事前に設定している場合にのみ機能します(たとえば、9-10、10-11などから修正されました)。これにより、この予約システムを任意の時間範囲で開いたままにしておくという私の目的が失われます。このため、データベースと通信する場合、そのようなシステムでは読み取り/書き込みが許容されますか?レコードに楽観的ロックをかける場合、DBに書き込む前に、行を読み取ってバージョンを比較する必要があるためですか?
この作業を確認するもう1つの方法は、リクエストを通過させ、別のプロセスで予約カレンダーを再確認して重複を検出し、最初の有効な予約のみを保持することです。しかし、すべての予約でこれを行う必要があり、部屋が人気があり、多くのユーザーが予約したい場合、これは効率的ではないと感じます。好転するまでにはかなりの時間がかかります。
このようなシステムを設計する必要がある場合、どのようにそれを実行しますか?とにかくリアルタイムで予約を確認できますか?仮の状況で同じ問題に直面する予約ユニットが1日である場合、ホテル予約システムはどのように機能しますか?ユーザーが予約しようとしている部屋は1つだけですか?
キューは、この問題に対するエレガントなソリューションのように見えます。まともなキューイングの実装により、1つのデキュー操作で1つのメッセージのみが確実に表示されるため、指定した条件が発生しません(独自のキューを作成することを考えていなかった場合は、これを行わないでください)。
1つの方法は、1日を15分刻みに分割することです。次に、部屋、日付、間隔IDで主キーを作成します。誰かが別のレコードを挿入しようとすると、主キーエラーが発生します。このアプローチでは1つのテーブルが必要です。データベースはアトミックであり、重複した主キーを許可しないため、トランザクションは必要ありません。
期間を予約できるようなものが必要な場合は、キーの使用に問題があるため、トランザクションが必要になります。トランザクションは、テーブルのレコードを調べて、会議の開始と終了が他のレコードと重複していないかを確認する必要があります。その場合は、「予約してトランザクションを終了できない」というメッセージを返し、そうでない場合は部屋を予約してトランザクションを完了します。あなたが見ている間、他の誰も予約することができないので、彼らは彼らが彼らの取引を始めることができる前にあなたの取引が終わるまで待たなければなりません。これによりブロッキングが発生します。軽減するには...
このシナリオでは、各部屋を独自のテーブルにして、ロックとその部屋のトランザクションがある場合、他の部屋を予約している他のユーザーが、予約が完了するまで待つ必要がないようにすることができます。これにより、部屋全体のスケーラビリティが向上し、2人のユーザーが同時に部屋を予約しようとした場合にのみブロックされます(可能性は低いですが、可能です)。