11

私は現在、かなり大きな質問/回答ベースのアプリケーション (stackoverflow / answerbag.com のようなもの) の真っ只中にいます。データ アクセスには SQL (Azure) と nHibernate、UI アプリには MVC を使用しています。

これまでのところ、単一のPostテーブル (質問と回答の両方を含む)があるという意味で、スキーマはおおよそ stackoverflow db の行に沿っています。

おそらく、次のリポジトリ インターフェイスの行に沿って何かを使用する予定です。

public interface IPostRepository
{
    void PutPost(Post post);
    void PutPosts(IEnumerable<Post> posts);

    void ChangePostStatus(string postID, PostStatus status);

    void DeleteArtefact(string postId, string artefactKey);
    void AddArtefact(string postId, string artefactKey);

    void AddTag(string postId, string tagValue);
    void RemoveTag(string postId, string tagValue);

    void MarkPostAsAccepted(string id);
    void UnmarkPostAsAccepted(string id);

    IQueryable<Post> FindAll();
    IQueryable<Post> FindPostsByStatus(PostStatus postStatus);
    IQueryable<Post> FindPostsByPostType(PostType postType);
    IQueryable<Post> FindPostsByStatusAndPostType(PostStatus postStatus, PostType postType);
    IQueryable<Post> FindPostsByNumberOfReplies(int numberOfReplies);
    IQueryable<Post> FindPostsByTag(string tag);
}

私の質問は次のとおりです。これらの「投稿」のクエリを改善するために、solrをこれにどこに/どのように適合させますか(Solrとの実際の通信にはsolrnetを使用します)

理想的には、SQL db を単なる永続ストアとして使用します。上記の IQueryable 操作の大部分は、ある種の SolrFinder クラス (またはそのようなクラス) に移動します。

Body プロパティは、現在問題を引き起こしているものです。これはかなり大きく、SQL でのクエリが遅くなります。

私の主な問題は、たとえば、誰かが投稿を「更新」した場合、たとえば新しいタグを追加した場合、その投稿全体のインデックスを再作成する必要があることです。明らかに、これを行うには次のようなクエリが必要になります。

「SELECT * FROM POST WHERE ID = xyz」

もちろん、これは非常に遅くなります。Solrnet には nHibernate 機能がありますが、これは上記と同じ結果になると思いますか?

私はこれを回避する方法を考えました。それについてあなたの意見をお願いします。

  • ID をキューに追加する (Amazon sqs など - 使いやすさが気に入っています)
  • 上記のクエリを実行し、ドキュメントを構築し、それをsolrに再度追加するサービス(または一連のサービス)をどこかに配置します。

私がデザインで抱えている別の問題: 「再インデックス」メソッドはどこから呼び出す必要がありますか? MVCコントローラー?または、IPostRepository のインスタンスをラップする「PostService」タイプのクラスが必要ですか?

これについては、どんな指針も大いに受けています!

4

3 に答える 3

27

私が働いている e コマース サイトでは、Solr を使用して製品カタログのファセットと検索を高速化しています。(Solr マニア以外の用語で言えば、これは、Zappos、Amazon、 NewEgg、および Lowe's.)

これは、Solr がこの種のことを迅速かつ適切に実行するように設計されているためです。従来のリレーショナル データベースでこの種のことを効率的に実行しようとしても、データベースでインデックスの追加と削除を開始したくない場合を除き、うまくいきません。飛んで完全なEAVに行きます。これは、Magentoのが愚かな咳です。したがって、SQL Server データベースは「信頼できる」データ ストアであり、Solr インデックスはそのデータの読み取り専用の「投影」です。

あなたは似たような状況にあるように聞こえるので、ここまで私と一緒にいます。次のステップは、Solr インデックスのデータが少し古くても問題ないかどうかを判断することです。あなたはおそらくそれがいくぶん古くなるという事実を受け入れたでしょうが、次の決定は

  • 古すぎるのはどのくらい古くなっていますか?
  • 古さよりも速度やクエリ機能を重視するのはいつですか?

たとえば、"Worker" と呼んでいるものがあります。これは、Quartz.NETを使用して C#IJob実装を定期的に実行する Windows サービスです。3 時間ごとに実行されるこれらのジョブの 1 つが であり、そのジョブが行うのはへRefreshSolrIndexesJobの ping だけです。これは、Solr の組み込みDataImportHandlerを使用して、実際に SQL データベースからデータを吸い込むためです。ジョブは、同期を機能させるために定期的にその URL に「触れる」必要があります。DataImportHandler は定期的に変更をコミットするため、これはすべて事実上バックグラウンドで実行され、Web サイトのユーザーには透過的です。HttpWebRequesthttp://solr.example.com/dataimport?command=full-import

これは、製品カタログの情報が最大 3 時間古い可能性があることを意味します。ユーザーは、カタログ ページで「Medium In Stock (3)」のリンクをクリックしても (この種のファセット データは SOLR をクエリすることによって生成されるため)、製品の詳細ページに在庫のない媒体が表示される場合があります (これ以降)。ページ、数量情報はキャッシュされ、データベースに対して直接照会されない数少ない情報の 1 つです)。これは面倒ですが、私たちの特定のシナリオでは一般的にまれです (私たちはかなり小規模なビジネスでありトラフィックがそれほど多くありません)。インデックス全体を最初から再構築すると、とにかく 3 時間で修正されるため、これを受け入れました。合理的なトレードオフとして。

この程度の「古さ」を受け入れることができる場合は、このバックグラウンド ワーカー プロセスを使用することをお勧めします。「数時間ごとにすべてを再構築する」アプローチを採用するか、リポジトリで ID をテーブルに挿入し、たとえばdbo.IdentitiesOfStuffThatNeedsUpdatingInSolr.データセットのサイズや複雑さを考えると、インデックス全体を定期的にゼロから作成することは合理的ではありません。

3 番目のアプローチは、現在のドキュメントに関して Solr インデックスをほぼ同時に更新するバックグラウンド スレッドをリポジトリに生成させることです。そのため、データは数秒間だけ古くなります。

class MyRepository
{
    void Save(Post post)
    {
         // the following method runs on the current thread
         SaveThePostInTheSqlDatabaseSynchronously(post);

         // the following method spawns a new thread, task,
         // queueuserworkitem, whatevever floats our boat this week,
         // and so returns immediately
         UpdateTheDocumentInTheSolrIndexAsynchronously(post);
    }
}

しかし、これが何らかの理由で爆発した場合、Solr の更新を見逃す可能性があるため、Solr に定期的に「すべてを吹き飛ばしてリフレッシュ」させるか、アウトをチェックするリーパー バックグラウンド ワーカー タイプのサービスを用意することをお勧めします。誰もが青月に一度、Solr の最新データ。

このデータを Solr からクエリする場合、いくつかの方法があります。1 つは、リポジトリのメソッドを介して Solr が完全に存在するという事実を隠すことです。Solr スキーマは、そのデータにアクセスする UI に合わせて恥知らずに調整される可能性があるため、個人的にはお勧めしません。Solr を使用して情報の簡単なファセット、並べ替え、および高速表示を提供することを既に決定しているため、Solr を最大限に使用することもできます。これは、Solr にアクセスするとき、およびキャッシュされていない最新のデータベース オブジェクトにアクセスするときに、コードで明示的にすることを意味します。

私の場合、NHibernate を使用して CRUD アクセスを行い ( ItemGroup.すでにデータベースを抽象化しています。(これは個人的な選択です。)

しかし、データをクエリするとき、それをカタログ指向の目的で使用しているか (速度クエリを気にする)、バックエンド管理アプリケーションのテーブルに表示するために使用しているか (通貨を気にする)をよく知っています。Web サイトでクエリを実行するために、私は というインターフェースを持っていますICatalogSearchQuery。いくつかのパラメーター (選択したファセット、検索用語、ページ番号、ページごとのアイテム数など) を定義するメソッドを受け取り、残りのファセットSearch()、結果の数、この結果を返すメソッドがあります。ページなどかなり退屈なもの。SearchRequestSearchResult

興味深いのは、その実装がその下の s のICatalogSearchQueryリストを使用していることです。ICatalogSearchStrategyデフォルトの戦略である はSolrCatalogSearchStrategy、単純な昔ながらの方法HttpWebRequestで XML を解析してSOLR に直接ヒットしますHttpWebResponse(これは、一部の SOLR クライアント ライブラリよりもはるかに使いやすく、私見ですが、私が最後に見たときから改善されている可能性があります)。それらは1年以上前です)。その戦略が何らかの理由で例外をスローしたり吐いたりするとDatabaseCatalogSearchStrategy、SQL データベースに直接ヒットしますが、一部のパラメーターは無視されます。SearchRequest、ファセットや高度なテキスト検索など、そこで行うのは非効率的であり、そもそも Solr を使用している理由のすべてです。アイデアは、通常、SOLR はフル機能の栄光で私の検索要求に迅速に応答するというものですが、何かが爆発して SOLR がダウンした場合でも、サイトのカタログ ページは、データベースに制限された機能セットを直接。(これが検索であることをコードで明示しているため、その戦略では、クライアントに深刻な影響を与えることを心配することなく、いくつかの検索パラメーターを無視することでいくらか自由を取ることができます。)

重要なポイント:重要なのは、古くなった可能性のあるデータ ストアに対してクエリを実行するか、信頼できるデータ ストアに対してクエリを実行するかの決定が明確にされていることですICatalogSearchQuery。挿入/更新/削除機能を備えた低速で最新のデータが必要な場合は、NHibernate の名前付きクエリ (またはこの場合はリポジトリ) を使用します。また、SQL データベースに変更を加えた場合、アウトプロセスの Worker サービスが最終的に Solr を更新し、結果的に一貫性が保たれることがわかっています。(そして、何かが本当に重要な場合は、イベントをブロードキャストするか、SOLR ストアに直接 ping を実行して、必要に応じてバックグラウンド スレッドで更新するように指示することができます。)

それがあなたにいくつかの洞察を与えることを願っています。

于 2010-09-09T01:36:56.363 に答える
8

solr を使用して、大規模な製品データベースにクエリを実行します。約100万点の商品と30店舗。

私たちが行ったことは、製品テーブルでトリガーを使用し、Sql サーバーの在庫テーブルを使用したことです。

行が変更されるたびに、再索引付けする製品にフラグが立てられます。そして、これらの製品を取得して 10 秒ごとに Solr に送信する Windows サービスがあります。(バッチごとに 100 個の製品の制限があります)。

これは非常に効率的で、ほぼリアルタイムの株式情報です。

于 2010-09-15T18:55:02.200 に答える
2

大きなテキスト フィールド (「本文」フィールド) がある場合は、バックグラウンドでインデックスを再作成します。あなたが言及した解決策(キューまたは定期的なバックグラウンドサービス)で十分です。

MVC コントローラーは、このプロセスを無視する必要があります。

リポジトリ インターフェイスに IQueryables があることに気付きました。SolrNet には現在、LINQ プロバイダーがありません。とにかく、これらの操作だけが Solr で行う場合 (つまり、ファセット処理を行わない場合) は、代わりにLINQ プロバイダーを備えたLucene.Net の使用を検討することをお勧めします。

于 2010-09-09T00:48:33.743 に答える