0

PostgreSQL には 2 つのテーブルがあります。

私のアプリケーションで最も頻繁に使用される SELECT の 1 つは、次のとおりです。

SELECT urls.* 
FROM urls 
JOIN hosts ON urls.host = hosts.host 
WHERE urls.projects_id = ? 
  AND hosts.is_spam IS NULL 
ORDER by urls.id DESC, LIMIT ?

urls テーブルに 100,000 を超える行があるプロジェクトでは、クエリの実行が非常に遅くなります。

テーブルが大きくなったため、クエリの実行はますます遅くなります。非常に大きなテーブルを処理するように設計された NoSQL データベース (MongoDB など) についてよく読んだことがあり、データを MongoDB に移動することを検討しています。URL テーブルからデータを選択するときにホスト テーブルをチェックする必要がなければ、すべてが簡単になります。MongoDB は結合をサポートしていないと聞いたので、上記の問題を解決するにはどうすればよいですか? ホストに関する情報を URL コレクションに入れることはできますが、フィールド hosts.is_spam はユーザーによって更新される可能性があり、URL コレクション全体を更新する必要があります。私はそれが正しい解決策であることを知りません。

アドバイスをいただければ幸いです。

4

4 に答える 4

2

結合を使用しない場合、リレーショナル データベースも非常に高速に動作します。これは、パフォーマンスのために非正規化が必要な場合だと思います。

オプション1

is_spam列を URL テーブルにコピーします。ホストのこの値が変更されたら、関連するすべての URL を更新します。あまり頻繁にしなければ大丈夫です。

オプション 2

あなたのアプリはわかりませんが、スパム ホストの数は比較的少ないと思います。この場合、ID をメモリ内ストア (memcached、redis など) に配置し、すべての URL をクエリして、アプリ内のスパム URL を除外できます。この方法では、ページネーションが少し壊れますが、実行可能なオプションになる場合があります。

于 2012-07-09T21:18:00.227 に答える
0

MongoDB が結合をサポートしていないのは事実です。このような場合、urlsコレクションを次のように構成します

urls : {
    name,
    some_other_property,
    host
}

その後、特定の URL のホストを取得し、コレクションis_spam内のフィールドを確認できます。hostsこれは、クライアントが DB にクエリを実行する必要があり、JOIN の場合のように DB 自体で実行できないことに注意してください。

于 2012-07-09T21:10:46.440 に答える
0

問題は結合であるという点であなたは正しいですが、私の推測では、それは単に間違った種類の結合であるということです。Frank H. が述べたように、PostgreSQL は、hosts.is_spam. おそらく、urlsテーブルをクラスターid化して、order by-limit フェーズを最適化する必要があります。あなたは気にするだけなので、回避するホストの短いリストだけを簡単に取得できる場所にurls.*部分インデックスを作成することで、ディスク IO を最小限に抑えることができます。hosts.hostis_spam is not null

これを試して:

select urls.* 
from urls 
left join hosts 
   on urls.host = hosts.host 
   and hosts.is_spam is not null
where urls.projects_id = ? 
and hosts.host is null

またはこれ:

select * 
from urls
where urls.projects_id = ? 
and not exists (
   select 1
   from hosts
   where hosts.host = urls.hosts
   and hosts.is_spam is not null
)

これにより、PostgreSQL はアンチ結合を使用して、既知のスパムのホストにマップされていない URL のみを取得できます。null または無効なホストを持つ URL がある場合、結果はクエリとは異なる場合があります。

于 2012-07-10T08:40:46.790 に答える
0

@xbonesの回答に似ていますが、具体的な例があります

ドキュメントにhost_idフィールドを配置するのも 1 つの方法です。urls最初に URL ドキュメントの結果を取得し、次にスパム ホストの結果を取得してから、クライアント コードでローカルにフィルター処理する必要があります。

だいたい:

var urls = db.urls.find({projects_id:'ID'}, {_id: 1, host_id: 1});
var hosts = db.hosts.find({is_spam: 1}, {_id: 1});

# psuedocode
ids_array = _id for _id in urls if host_id is not in hosts

urls = db.urls.find({_id: {$in: ids_array}});

または:

var urls = db.urls.find({projects_id:'ID'});
var hosts = db.hosts.find({is_spam: 1}, {_id: 1});

# psuedocode
urls = url for url in urls if host_id is not in hosts

最初の例では、project_idクエリの結果が巨大になる可能性があり (そして URL ドキュメントが大きくなる)、可能な限り最小限のデータのみを取得したいと想定し、ローカルでフィルター処理してから、完全な最終 URL ドキュメントをバッチで取得します。

2 番目の例では、開始する完全な URL ドキュメントを取得し、それらをローカルでフィルター処理します。

于 2012-07-09T21:36:01.327 に答える