私はフランチャイズで働いており、各フランチャイズが日常業務を管理するために使用するアプリケーションの完全な再設計を始めています。これは、POSおよびスケジューリングアプリケーションとして機能します。現在、各フランチャイズには独自のローカルSQL Expressデータベースがあります。デスクトップアプリケーションは施設内で実行され、このデータベースの読み取りと書き込みを行います。企業側では、すべてのフランチャイズに関する集計情報を取得する必要があるため、アプリケーションがローカルレコードを保存するたびに、すべてのフランチャイズのデータを同じスキーマの共有データベースに挿入するコピーを企業サーバーに送信します。その後、企業はすべてのフランチャイズのレコードを含む共有データベースでレポートを実行できます。なぜこのように設計されているのですか?その設計時(10年以上前)に、利害関係者はフランチャイズがインターネットに接続せずにデータにアクセスできることが重要であると感じました。
さて時代は変わり、ついに関係者全員がデスクトップアプリの代わりにすべてのフランチャイズをサポートするためにウェブアプリフロントエンドを備えた単一の集中型データベースに進んで進んで行きます。したがって、技術的には、共有データベースを備えたマルチテナントアプリを設計しています。先ほど述べたように、実際には既にデータベースが企業側にありますが、それは瞬間的なフランチャイズ内の読み取りと書き込みをサポートしていません。
私たちが扱っているデータの量を理解するために、現在数百のフランチャイズ、300万の顧客アカウントがあり、実際にはアクティブな顧客がはるかに少ない部分、800万の購入があります。最大のテーブルの1つには、毎週の顧客のカレンダーエントリが含まれ、9100万行を超えます。繰り返しますが、これらの各レコードは単一のフランチャイズに固有ですが、それらは一緒に保存されます。例えば。 tbl_Customersには300万人の顧客がいて、それぞれにtbl_Franchisesを指すfk_FranchiseIDがあります。
データベースは、毎日のフランチャイズ業務(Entity Frameworkベースのデータレイヤーを使用している可能性が最も高いWebアプリ)、個々のフランチャイズと集計企業レポートの両方のレポート、およびスケジュールや顧客アカウント情報の表示などの顧客向けWebサイト機能をサポートする必要があります。 。集計データベース、費用、新しいフランチャイズの追加のしやすさは、共有データベースのアイデアを推進する要因だと思います。さらに、私は現在、2つのWebサーバーと2つのデータベースサーバーを備えたロードバランサーを計画していると思います。
SQL Serverについてまだ理解できておらず、まだ学習していることがたくさんあるという警告の中で、私の最大の懸念は次のとおりです。
したがって、全体的な質問は次のとおりです。このような共有データベースでテナントデータを効率的に分離するために使用できる戦略と手法は何ですか。
この設計の主な関心事は、セキュリティとサイズです。しかし、そこに着く前に、誤解を解消したいと思います。
見た目が間違っているように、正規化を犠牲にして、フランチャイズIDをこれらの子テーブルの一部またはすべてに直接含める方が良いでしょうか?
なぜそう思うかもしれません。フランチャイズIDを属性として考える場合、それをすべてのテーブルに含めると、第3正規形に違反します。
しかし、これを見る別の方法は次のとおりです。論理設計では行キーにフランチャイズIDが含まれています:(FRANCHISE_ID, TABLE_ID)
。
しかし、あなたは言う、TABLE_ID
はID列です!私が答えるところ:はい、しかしそれは物理的な詳細であり、論理的な詳細ではありません。そして論理的には、テーブルは複数の「候補」キーを持つことが許可されています(このステートメントに対する権限として C。J. Date に切り替えます)。
そして、この論理設計を受け入れると、多くの物理的メリットが得られます。まず、データにアクセスするために結合は必要ありません。論理的には、結合には時間がかかりませんが、物理的にはそうです。さらに、クエリが同じフランチャイズの複数の行を取得する傾向がある場合は、 クラスター化インデックスを使用して行を連結する を使用することでもメリットがあります。
では、メイントピックに移りましょう。
企業経営の観点から、これはおそらくあなたの最も重要な問題です。明らかに、あるフランチャイズ店が別のフランチャイズ店に属するデータを表示することを許可することはできません。しかし、これを実現するには多くの方法があり、システムとその開発者に異なるレベルの負荷を課します。ここでは、検討するためのアイデアをいくつか紹介します。
個々のクエリに適用される述語
これは最も単純ですが、開発者に最も重い負荷を追加します。保護されたクエリのすべてに、フランチャイズIDに対するチェックを含める必要があります。一人でも忘れると、会社に経済的な影響を与える可能性があります(訴訟など)。
ただし、コードレビュー、静的分析、統合テストを組み合わせることで、おそらくこれを克服できると思います。すべてのクエリが厳密に検証されたデータアクセスレイヤーを通過するようにするには、規律が必要です。
ビュー
すべてのクエリにフランチャイズIDのチェックが含まれていることを確認するには、テーブルをビューの背後に隠し、各ビューにフランチャイズチェックが含まれていることを確認します。各フランチャイジーは、異なるスキーマに保存された独自のビューのセットを持ちます。
このアプローチのもう1つの利点は、フランチャイズ店に直接データを公開できることです。また、公開データに影響を与えることなく、物理テーブルを変更できます。
ただし、いくつかの重大な欠点があります。まず、開発者は、各クエリに対して正しいビューのセットを使用する必要があります(接続の管理方法によっては、それほど悪くないかもしれません)。次に、特定のビューを保持するすべてのスキーマに変更を伝達する必要があるため、長期的なメンテナンスコストがかかります(ただし、これは簡単に自動化できるはずです)。
行レベルのセキュリティ
私はSQL-Serverに精通していませんが、行レベルのセキュリティについての私の理解はそれがデータベースユーザーに基づいているため、フランチャイズごとに(少なくとも)1人のユーザーが必要です。つまり、フランチャイジーごとに(少なくとも)1つの接続が必要であり、データベースに過度の負荷がかかる可能性があります(あるいは、接続の継続的な作成/破棄)。また、開発者がフランチャイズ条件を含むクエリをコーディングする必要があるか、ランタイムエラーが発生することも推測します。そして、それらすべてのユーザーを管理する必要があります。
全体として、これは最も苦痛なルートのように見えますが、セキュリティを保証するものであるため、それがあなたの行く方法だと思います。
開発の観点からは、これはより大きな問題になります。特に、ユーザーが応答時間が遅いことに不満がある場合です。
最も重要な目標は、クエリごとにできるだけ少ないデータブロックに触れることです。過去に私が成功裏に使用したいくつかのテクニックを以下に示します。
できるだけ購入するRAMできるだけ購入する
あなたの目標は、データベース全体をメモリに保持することです。本当に。 SSDが盲目的に高速であることは問題ではなく、データブロックの読み取りと書き込みにはまだ時間が必要です。
完璧な世界では、起動時にデータベース全体をメモリに読み込み、書き込みはIOのみです。
テーブルの「アクティブ」サイズを縮小します
1つの91MM行テーブルについて言及しました。特定のクエリでこのテーブルのどれだけがアクセスされますか?アクセス頻度の低いデータが別のテーブルに格納されるようにテーブルを分割できますか? (SQL-Serverが宣言型パーティション分割をサポートしていることを前提としていますが、そうでない場合は手動で行を移動/複製できます)。
テーブルが大きいと、クエリは多くの行にアクセスする必要があります。インデックスがある場合でも、それらのインデックスも大きくなるためです。
データのコロケーション
デフォルトでは、データベースはスペースを見つけることができる場所ならどこにでも行を格納します。つまり、ユーザーのトランザクションなど、通常一緒にアクセスされるデータは、ディスク全体に分散される可能性があります。
ただし、クラスター化されたインデックス(上記のリンクを参照)を使用するか、インデックスをカバーするかのいずれかで、これをある程度制御できます。これらを最大限に活用してください。
リードレプリカを使用します
一般的なアプリケーションは、更新よりもはるかに頻繁に選択を実行し、更新が単一の行に影響を与える一方で、複数の行を選択する傾向があります。
これらの2つの操作を分離することにより、いくつかの利点があります。まず、容量を個別にスケーリングできます。読み取りが多い場合は、より多くの、またはより大きなマシンを購入できます。 2番目に、競合を減らすことができます。長時間実行の選択によって更新がブロックされることはありません(個人的には、これはたとえば20年前よりも今日の問題ではないと思いますが、検討する価値はあります)。
リードレプリカの欠点は、サーバーとレプリカで行が更新される時間に遅れがあることです。これはあなたにとって問題である場合とそうでない場合があります(私の経験では、遅延はサイズの小さいマシンが原因です。より多くのお金でその問題を解決できます)。
データウェアハウスへのレポートのオフロード
真の「レポート」クエリは、運用クエリとは大きく異なる傾向があります。たとえば、運用クエリでは1人のユーザーの最新の注文を取得できますが、レポートクエリでは特定の製品を購入したすべてのユーザーを検索できます。その結果、同じ物理設計で運用クエリとレポートクエリの両方をサポートしようとすると、失敗のレシピになります。
少なくとも、レポートを専用のリードレプリカにシフトしてください。完全に異なるDBMSを使用する方が良いです。DBMSのストレージとクエリの特性は、レポートのニーズにより厳密に一致します。 Amazon Redshift 、 Google BigQuery 、または Azure SQL Data Warehouse のようなもの。または、おそらく Apache Cassandra のようなローカルでホストされるオプション。
これを行わないでください。
マルチテナントソリューションの開発にかかる時間は、フランチャイズ店に関連性の高い機能を追加したり、現在のコードやプロセスを改善したりするのに利用できない時間です。
問題がメンテナンス、またはフランチャイズごとの資本支出である場合は、集中管理を可能にする代替手段を検討してください。たとえば、Azureまたは別のクラウドプロバイダーを、本社の運用担当者と共に使用します。フランチャイズごとに月額数百ドルのコストでクラウドベースのソリューションを展開できるはずです(その場合)。フランチャイズ店の資本コストと運用コストの両方を削減できます。
問題が報告である場合、より効率的なデータの取得と変換に焦点を当てます。この場合も、クラウドベースのソリューションが役立ちます。
クラウドプロバイダーに移行するという考え-そしてAzureは、Microsoftのショップのように私が選んだ唯一のオプションです-は、コンピューターオペレーターのトレーニングを受けていないフランチャイズ店によって引き起こされる問題を排除することです。
最も単純な形式では、クラウド内のフランチャイズごとにデータベースサーバーを作成し、既存のアプリケーションはローカルデータベースではなくそのサーバーをポイントします。データベースは常に稼働しているため、いつでもデータを取得できます。また、通常、クラウドプロバイダーは定期的なバックアップを行い、フォールトトレランスと回復のための他のオプションを提供します。
クラウドでの価格設定は、必要な機能に大きく依存しています。たとえば、 Azure Cloud SQLの料金ページ を見ると、「標準」データベースサービスの基本料金は$ 0.0202 /時間、または$ 15 /月です。データベースのパフォーマンスの観点から、これが実際にあなたに何をもたらすかはわかりません。私の経験では、月額100ドルの可能性が高いです。
クラウドホスティングを最初のステップとして使用して、真のマルチテナントソリューションに移行することを妨げるものは何もありません。そして、あなたが数百または数千のフランチャイジーを持っている場合、それはコストを管理する意味があります。しかし、本当の問題は運用管理の問題のようです。
table partitioning を調べることをお勧めします。テーブルのパーティショニングを使用しても、アプリケーションはそれが機能する単一の「論理」テーブルを表示します。ただし、物理的には、各クライアントのデータを個別のテーブル、ファイルグループ、データベース、またはSQL Serverインスタンスに配置することもできます。
これはあなたに二つのものを与えます
1)クライアントIDがパーティション化キーとして機能するため、すべてのテーブルにクライアントIDを追加する言い訳が必要です。
2)クライアントを互いに分離することができます。これは、クライアントがデータに直接アクセスできるようにする場合に非常に重要です。データをパーティション分割しなかった場合、不注意な開発者が他のクライアントに無意識のDoS攻撃を仕掛ける可能性があります。これは、データ読み取り権限しか持っていない場合でも、ロックする行が多すぎたり、テーブルを強く叩きすぎたりすることによって発生します。しかし、テーブルが物理的に分離されている場合、これは不可能です。
とは言っても、ライブデータベースへの直接アクセスは許可しないでしょう。それは非常に危険なようです。レポートやアドホッククエリを実行する必要がある場合は、データのみを含む data mart を与えることができます(追加料金が発生する可能性があります)。