データベース設計-毎回状態を保存するか状態を計算しますか?


17

リレーショナルデータベースアプリケーションと「ユーザー」オブジェクトと「メッセージ」オブジェクトがあるとします。次に、このユーザーに未読メッセージの数を表示します。

これをアーカイブする最良の方法は何ですか?ユーザーにフィールドを導入し、ユーザーがメッセージを受信した場合にカウントアップし、メッセージを読んだ場合にカウントを減らしますか?または、毎回クエリを実行して、未読のフラグが付けられたユーザーのメッセージ数を計算しますか?

最初のアプローチはより複雑でエラーが発生しやすいと思いますが、2番目のアプローチよりもパフォーマンスが向上します。

これは通常どのように行われますか、またはより良いアプローチは何ですか?


1
多くの要因に依存します。DBはパーティション分割されていますか?何行/ユーザーを期待していますか?予想されるDBの合計DBサイズ(または合計ユーザー数)毎秒何件のリクエストを期待していますか?このすべてが正確でなければならないのではなく、いくつかの大まかなアイデア...
オマールイクバル

10
+1これは、古典的なリレーショナルデータベースの質問です。正規化するか、正規化しないか?それが問題です。スキーマ内の高貴な人が、とんでもない複製のスリングと矢に苦しむのか、引き金を引き受けて採用することによって、それらを終わらせるのか?
ロスパターソン

これが古典的なRelであると主張します。db。質問、サイトに既に回答があるはずです。これはDUPとして閉じる必要があります。または、回答がなく、これを開いたままにしておく必要があります。
マッテンツ14年

回答:


14

これは通常どのように行われますか、またはより良いアプローチは何ですか?

最適なアプローチは、余分なフィールドなしで最初に試して、パフォーマンスを測定し、実際に遅すぎることが判明した場合は、最適化を試みます。これは、追加のフィールドを使用して最初のアプローチに切り替えることを意味しますが、他のオプションもテストすることを検討する必要があります。たとえば、メッセージの結合フィールド(「未読」、「userID」)に追加のインデックスを付ける


2
最善のアプローチは、(より単純な方法を最初に使用する)ことです。一般的なルールは仕様よりも優れています、fwiw。(ただし、「テスト!」の場合。)
DougM 14年

9

データベース理論による教科書のソリューションは、他のデータの値に依存する値がデータベースにないことです。これらは推移的な依存関係だからです。他のフィールドに基づいて計算された値であるフィールドを持つことは、冗長な情報につながるため、正規化違反です。

ただし、教科書の内容と実際の最も実用的な方法は異なる場合があります。各ページビューの未読メッセージの数を数えることは、非常に高価な操作になる可能性があります。-tableで数値をキャッシュするとuser、パフォーマンスが大幅に向上します。コストは、データベースに不整合が存在する可能性があることです。未読カウンターも更新することを忘れずに、メッセージを削除、追加、または読み取ることができる場合があります。


4
一貫性の問題は、カウンターをオンINSERTまたはに調整するトリガーを使用すると簡単になめられますDELETE。(またはUPDATE、メッセージの所有者の変更に対応するため。)。優れたDBMSが操作を実行し、同じトランザクションでトリガーを実行するため、すべてが発生するか、まったく発生しません。
Blrfl 14年

4

潜在的な問題はパフォーマンスであり、パフォーマンスの問題はまだありません。ソリューション#1でこれを処理するために選択したデータベースに応じてできることはたくさんあります。これは、ユーザーが現在の未読メッセージ数を取得する頻度に依存します。これらの選択肢の多くは、アプリ側でカスタムコーディングを必要としないため、コードを変更するか、ごくわずかで実装できます。アプリで成長しやすくします。

ユーザーが接続/ログインすると、データベースからカウントを取得するのはそれほど悪くありません。アプリは、メールなどのメッセージのリストを常に更新していますか?ここから未読件数を取得するためにデータベースに再度アクセスする必要はありません。新しいメッセージを取得するには、とにかくdbトリップが必要です。

IsReadにフラグを立てるためにメッセージが読み取られるたびにdbにアクセスしますか?フィールドは、別のフィールドの再計算なしで十分です。

ソリューション#2(フィールド/ディスク上のカウントを保持)を使用すると、問題が発生したときにこのフィールドを定期的に再構築/再計算するルーチンが必要ですか?そして、常に問題があります。これらすべてをトランザクションでラップしますか?誰かが他の誰かにメッセージを送信するたびに、Userテーブルのロックが原因で受信ユーザーのUnreadCountを更新できないため、失敗する可能性がありますか?または、このフィールド用に別のテーブルを作成しますか?


カウントフィールドを最新の状態に保つことに関するパフォーマンスの問題に言及して+1
winkbrace 14年

0

私が行う方法は、毎回クエリを実行することです。つまり、2番目のアプローチです。クエリのパフォーマンスを向上させるために、ユーザーテーブルへの外部キーとして機能する列のメッセージテーブルにインデックスを追加してください。

次に、Docが言うように、このアプローチのパフォーマンスを測定すると、別のパスを取る必要があるかどうかを判断できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.