複数のMySQLテーブルと1つの大きなテーブルのどちらがより効率的ですか?


103

MySQLデータベースにさまざまなユーザーの詳細を保存します。もともとは、さまざまなテーブルで設定されていたため、データがUserIdとリンクされ、必要に応じてデータを表示および操作するための複雑な呼び出しを介して出力されていました。新しいシステムをセットアップする場合、これらのすべてのテーブルを関連するコンテンツの1つの大きなテーブルに結合することはほぼ理にかなっています。

  • これは助けになりますか、それとも妨げになりますか?
  • 呼び出し、更新、検索/操作の速度に関する考慮事項?

これが私のテーブル構造の例です:

  • users-ユーザーID、ユーザー名、電子メール、暗号化パスワード、登録日、IP
  • user_details-Cookieデータ、名前、住所、連絡先の詳細、所属、人口統計データ
  • user_activity-投稿、最後のオンライン、最後の閲覧
  • user_settings-プロファイル表示設定
  • user_interests-ターゲット設定可能な変数の広告
  • user_levels-アクセス権
  • user_stats-ヒット、タリー

編集:私はこれまですべての回答に賛成してきましたが、それらにはすべて、基本的に私の質問に答える要素があります。

ほとんどのテーブルには1:1の関係があり、これが非正規化の主な理由でした。

これらのセルの大部分が空のままである可​​能性が高いときに、テーブルが100以上の列にまたがっている場合に問題が発生しますか?


この他の質問も参考になるかもしれません
モスティモスタチョ2013年

回答:


65

複数のテーブルは、次の方法/ケースで役立ちます。

(a)さまざまな人々がさまざまなテーブルを含むアプリケーションを開発しようとしている場合、それらを分割することは理にかなっています。

(b)データ収集のさまざまな部分について、さまざまな人々にさまざまな種類の権限を与えたい場合は、それらを分割する方が便利な場合があります。(もちろん、ビューの定義とビューへの許可の付与を適切に確認できます)。

(c)特に開発中にデータを別の場所に移動する場合は、テーブルを使用してファイルサイズを小さくすることは理にかなっています。

(d)単一のエンティティの特定のデータコレクションでアプリケーションを開発する場合、フットプリントが小さいと快適になります。

(e)これは可能性です。単一の値のデータとして考えていたものが、将来的には実際には複数の値になる可能性があります。たとえば、与信限度は現在のところ単一の値フィールドです。しかし、明日、値を(開始日、終了日、クレジット値)に変更することを決定する場合があります。分割テーブルが便利になるかもしれません。

私の投票は、データが適切に分割された複数のテーブルに対するものです。

幸運を。


3
@RohitKhatri:私の知る限り、複数のテーブルがあると、ほとんどの場合パフォーマンスが向上します。
Hari Harker 2016

1
@HariHarker回答ありがとうございます。ただし、アクセスパターンに依存することがわかりました。
Rohit Khatri 2016

最近まで、私は常にすべてのデータを1つのテーブルに格納していましたが、考えてみると、パフォーマンス(コースのユースケースによって異なります)、セマンティクス(一部のデータは、別のテーブル)と開発。たとえば、私は今、レガシーシステムの上にカスタムERPシステムを開発しています。列を追加して古いデータベーステーブルを拡張する必要がありました。新しいデータ用に新しいテーブルを作ることにしました。一部の新機能はレガシーシステムで便利です。今では、古いクエリを書き直すことなく簡単に統合できます
Ogier Schelvis

35

テーブルを結合することを非正規化と呼びます。

JOINメンテナンス地獄を作成する代わりに、いくつかのクエリ(多数のsを作成する)を高速に実行すると役立つ場合とそうでない場合があります。

MySQLJOINメソッドのみを使用できますNESTED LOOPS

これは、駆動テーブル内の各レコードについて、駆動MySQLされるテーブル内の一致するレコードをループで検索することを意味します。

レコードの検索は非常にコストのかかる操作であり、純粋なレコードのスキャンよりも数十倍かかる場合があります。

すべてのレコードを1つのテーブルに移動すると、この操作を取り除くのに役立ちますが、テーブル自体が大きくなり、テーブルのスキャンに時間がかかります。

他のテーブルに多数のレコードがある場合、テーブルスキャンを増やすと、レコードが順番にスキャンされるメリットを過大評価する可能性があります。

一方、メンテナンス地獄は保証されています。


1
10000人のユーザーがいて、外部キーを使用して正しく設定されたデータベースと結合している場合、select * from users where name = "bob"のようなことを行うだけで、強力な検索が必要になります。bobを取得したら、bobのIDを使用しているため、インデックスを使用してbobに結合されたテーブルを検索します。これは、クエリで結合を実行しているか、bobをクエリしてからテーブルを個別にクエリしているかに関係なく発生します。もちろん、うまくいけば、2番目のクエリはbobのIDに基づいており、他のものには基づいていません。
Rudy Garcia

17

それらはすべて1対1の関係ですか?つまり、ユーザーが異なるユーザーレベルに属している場合や、ユーザーの興味がユーザーの興味テーブルの複数のレコードとして表されている場合、それらのテーブルをマージすることはすぐには問題外です。

正規化に関するこれまでの回答については、データベースの正規化ルールはパフォーマンスを完全に無視しており、きちんとしたデータベースの設計とは何かを検討しているにすぎません。それはたいてい達成したいことですが、パフォーマンスを追求して積極的に非正規化することが理にかなっている場合があります。

全体として、問題はテーブルにあるフィールドの数と、それらがアクセスされる頻度にあると思います。ユーザーのアクティビティがあまり面白くないことが多い場合は、パフォーマンスメンテナンスの理由から、常に同じレコードに記録するのは面倒なことかもしれません。設定などの一部のデータが非常に頻繁にアクセスされるが、単にフィールドが多すぎる場合、テーブルをマージすることも不便かもしれません。パフォーマンスの向上のみに関心がある場合は、設定を個別に維持し、独自のセッション変数に保存して、データベースに頻繁にクエリを実行する必要がないようにするなど、他のアプローチを検討できます。


正規化はきちんとしたことにのみ焦点を当て、パフォーマンスを完全に無視するというあなたのコメントに完全に同意する必要があります。両方のシナリオでトレードオフがあり、非正規化は実際にデータの整合性を危険にさらします。データベースの正規化は、非正規化テーブルからの無視できるほどのわずかなパフォーマンス向上ではなく、データベースの全体的なパフォーマンスを実際に改善すると言えます。
Rudy Garcia

具体的には1対1の関係についての議論なので、テーブルの分割は正規化タスクではありませんよね?重複する情報がない場合は、単一のテーブルであっても正常です。(まあ、それは3NF正規化を満たさないかもしれないので、それを解決するために2番目のテーブルの恩恵を受けますが、それはOPが他のテーブルを参照しているものではないようです。)
ToolmakerSteve

14

くださいすべてのこれらのテーブルのは、持っている1-to-1関係?たとえば、各ユーザー行には、user_statsまたはに対応する行が1つだけありuser_levelsますか?その場合、それらを1つのテーブルに結合することは理にかなっています。ただし、関係がない 場合は、1 to 1それらを組み合わせる(非正規化する)ことはおそらく意味がありません。

数十万または数百万のユーザーレコードがない限り、それらを別々のテーブルと1つのテーブルに置いても、パフォーマンスへの影響はほとんどありません。あなたが得る唯一の真の利益は、それらを組み合わせることによってクエリを単純化することからです。

ETA:

多すぎることを懸念する場合は、通常一緒に使用するものを考え、それらを組み合わせて、残りを別のテーブル(または必要に応じていくつかの別のテーブル)に残します。

データの使用方法を見ると、クエリの80%がそのデータの20%を使用しており、残りの80%のデータはたまにしか使用されていないことがわかると思います。頻繁に使用する20%を1つのテーブルにまとめ、あまり使用しない80%は別のテーブルに残してください。そうすれば、妥協点が見つかるでしょう。


はい、各テーブルにはユーザーごとに1行しかありません。これは、複製された大量のデータを管理するという頭痛を軽減するためです。これが、1つのテーブルが適していると思う理由です。ユーザーデータが複数の行にまたがっている場合、それらのテーブルがメインのユーザーテーブルから分離されることを期待します。
Peter Craig、

1
すべてのテーブルに1対1のリレーションがある場合、1つのテーブルが使いやすくなります。その場合、テーブルを分割する必要はありません。テーブルを分割すると、行が1行以上あることが示唆されます。これにより、別の開発者がそのように処理する場合があります。
リチャードL

データベーステーブルの設計に80/20を適用する非常に興味深い考え。OOP(私は主にJava開発者です)クラスの設計についても考え、同じことがそこに効果があるのではないかと考えました(1つのクラスに主要な80%のアプリケーション機能を置き、残りを他のクラスに置きます)。
Zack Macomber

1
@ZackMacomber-いいえ、クラス分割は参照の局所性に基づく必要があります。複数のクラスに分割する利点は、機能の小さな単位の周りに境界線を引くことです。これにより、理解/テスト/変更が容易になり、その単位が他の機能単位と相互作用する場所が明確になります。目標は、ほとんどの接続(参照、呼び出し) 1つのユニットに保持し、ユニット間の接続を少なくすることです。クラスが実装するいくつかのインターフェースを、ユースケースごとに異なるインターフェースで定義することは、その分割に向けた最初のステップとして役立ちます。
ToolmakerSteve

@ToolmakerSteve良い考え+1
ザック

9

1つの大きなテーブルを作成すると、リレーショナルデータベースのプリンシパルに反することになります。それらすべてを1つのテーブルにまとめることはしません。繰り返しデータの複数のインスタンスを取得します。たとえば、ユーザーに3つの関心がある場合、3つの行があり、3つの異なる関心を格納するためだけに同じユーザーデータが含まれます。間違いなく、複数の「正規化された」テーブルアプローチを採用してください。データベースの正規化については、この Wikiページを参照してください。

編集: あなたがあなたの質問を更新したので、私は私の答えを更新しました...それ以来、私の最初の答えにさらに同意します...

これらのセルの大部分は空のままである可​​能性があります

たとえば、ユーザーが関心を持っていなかった場合、正規化すると、そのユーザーの関心テーブルに行が含まれなくなります。1つの大規模なテーブルにすべてがある場合、NULLのみを含む列(および明らかにそれらの多く)があります。

私は、大量のテーブルが存在するテレフォニー会社で働いていました。データを取得するには、多くの結合が必要になる可能性があります。これらのテーブルからの読み取りのパフォーマンスが重要である場合、作成されたプロシージャは、レポートが指す結合、計算などを必要としないフラットテーブル(つまり、非正規化テーブル)を生成できます。これらは、SQLサーバーエージェントと組み合わせて使用​​され、一定の間隔でジョブを実行します(つまり、一部の統計の週次ビューが週に1回実行されるなど)。


非正規化データは一時的な瞬間のスナップショットとしてのみ一時的に存在するため、このアプローチが好きです。挿入/変更/削除の問題はありません。完了したら破棄してください。
ToolmakerSteve

7

誰もが持つ基本的なユーザー情報を含むユーザーテーブルを用意し、基本的にユーザーIDに関連付けられた任意のキーと値のペアにできる「user_meta」テーブルを追加して、Wordpressが行う同じアプローチを使用しないのはなぜですか。したがって、ユーザーのすべてのメタ情報を検索する必要がある場合は、それをクエリに追加できます。また、ログインなどの必要がない場合は、常に追加のクエリを追加する必要はありません。このアプローチの利点は、Twitterハンドルや個々の関心を保存するなど、ユーザーに新しい機能を追加するためにテーブルを開いたままにすることです。また、すべてのメタデータをルール化する1つのテーブルがあり、50ではなく1つの関連付けに制限するため、関連付けられたIDの迷路に対処する必要はありません。

Wordpressは特にプラグインを介して機能を追加できるようにするためにこれを行うので、プロジェクトをよりスケーラブルにでき、新しい機能を追加する必要がある場合に完全なデータベースのオーバーホールを必要としません。


Wordpress wp_usermetaテーブルは幾何学的に成長します。各ユーザーはX行をwp_usermetaテーブルに追加します。1行は、そのユーザーのために保持したいメタ情報の各部分に対応しています。ユーザーごとに8つのカスタムフィールドを保持すると、wp_usermetaのusers * 8行が長くなります。これはパフォーマンスの問題を引き起こしているようですが、それが問題であるかどうかは
わかり

1
何万人ものユーザーがいる場合、これがどのようにパフォーマンスの問題を引き起こすかを見ることができました。基本的に、データベースはユーザーメタテーブルの10000 * 8エントリを検索して、探しているエントリを見つける必要があります。ただし、必要なときにメタデータのみをクエリすると、パフォーマンスが向上すると思います。メタデータが不要な場合でも常に要求している場合は、問題が発生する可能性があります。常にメタデータが必要な場合は、おそらくテーブルを分割することは最善の方法ではありません。
Rudy Garcia 14

1
ちょうど昨日get_users()、ページ付けを計算するためだけに(を使用して)すべてのユーザーをロードしていたWPテーマを扱いました。SELECT COUNT(…)代わりにページネーションのクエリを使用するようにコードを修正すると、ページの読み込み時間は28秒から約400ミリ秒になりました。パフォーマンスが結合されたテーブルまたは単一のフラットテーブルとどのように比較されるのか、まだ疑問に思っています…Webでパフォーマンスメトリックを見つけるのに苦労しました。
サードパーティ、2014

前のコメントについて考えると、上記のページ分割の例など、何らかの理由ですべてのユーザーを選択する必要がない限り、テーブルの分割は依然として効率的であるように思われます。すべてのメタ情報を取得している場合でも、usermetaテーブルには80kのエントリがあります。それは検索するのがたくさんです。おそらく、誰かが両方の実装でスクリプトを実行し、それを100回実行して平均を取得することで、より優れたアプローチをテストできる可能性があります。
ルディガルシア

1
私はこれを今日だけもう一度読み、10000 * 8エントリについての私のコメントは真実であることに気付きましたが、データベースの動作方法は、ほとんど問題にならないはずです。なんらかの理由で10000人のユーザーすべてを取得していて、さらにそのメタ情報も取得している場合、これはとんでもないことです。あなたがこれを望んでいるようなシナリオは考えられません。データベースは、外部キーとインデックス付けがあるため、シングルユーザーのメタを非常に高速に簡単に取得します。dbモデルが正しく設定されていると仮定します。
Rudy Garcia

5

これは、「状況次第」の状況の1つだと思います。複数のテーブルを用意する方がクリーンであり、おそらく理論的には優れています。ただし、1人のユーザーに関する情報を取得するために6〜7個のテーブルを結合する必要がある場合、そのアプローチを再考する必要があるかもしれません。


1

他のテーブルが実際に何を意味するかによると思います。user_detailsには、1つ以上の/ユーザーなどが含まれていますか?正規化のどのレベルがニーズに最も適しているかは、要件によって異なります。

インデックスが優れているテーブルが1つある場合は、おそらくより高速になります。しかし、その一方で、おそらくメンテナンスがより困難です。

それはおそらくユーザーとの1対1の関係なので、私にはUser_Detailsをスキップできるように見えます。しかし、残りはおそらくユーザーあたりの行の多くですか?

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