私は 97 のメンバー、つまり物理シャードを持つ SQL フェデレーションを持っています。各メンバーには、1 ~ 16 個の仮想シャード、つまりアトミック ユニットがあります。このデータ層は、検索ルックアップ Web サービス (Azure Web ロール Web サーバー上) を強化します。このサービスでは、ユーザーの応答に十分な回答を得る前に、すべてのアトミック ユニットが応答する必要があります。
検索パラメーターが与えられると、Web サーバーは照会する必要があるアトミック ユニット ID を特定できますが、関連するフェデレーション メンバーは特定できません (この変換には USE フェデレーションを使用しています)。目標は、Web サーバーがすべての原子単位 (どこにあるかに関係なく) を可能な限り迅速に照会するようにすることです。
現在、これに対する最善の解決策は次のように機能します。
- 必要な原子単位のリストを生成します。
- アトミック ユニットごとに USE フェデレーションと SQL ステートメントを生成します。現在、私が見つけた最高のパフォーマンスは、USE フェデレーション ステートメントで FILTERING = OFF を指定し、SQL ステートメントでアトミック ユニットの述語を手動で指定することです (これらの述語を追加するために FILTERING = ON に依存するのではなく)。
- アトミック ユニットごとに、フェデレーション ルートへの SqlConnection を開き、USE フェデレーション ステートメントを実行してから、SQL クエリを両方とも非同期で実行します。TPL Dataflow ライブラリと async/await を使用して、すべてのアトミック ユニット クエリが終了するのを待ちます。その後、結果にビジネス ロジックを適用し (Web 要求によってはオプション)、応答を返します。
これらのクエリの各アトミック ユニットは 100 ~ 600 レコードを返しますが、2000 を超えることはありません。設計目標は、最大 200 アトミック ユニットを一度にクエリすることです => したがって、400,000 レコードが Web サーバーが適用する必要がある最大量です。ただし、そのロジックは「結果の個別の数値 ID ごとに 1 つのオブジェクトを取得する」以上のものではありませんが、そのために IEqualityComparer を実装しました。
このアプローチは、それほどうまくスケーリングされていないようです。30 ~ 40 のアトミック ユニットをクエリする場合でも、個々のアトミック ユニットの応答にかかる時間がそれぞれ 1 秒未満であることをテストして確認できますが、応答時間は著しく増加します。
私の問題の可能性が高い場所は次のとおりだと思います。
- アトミック ユニット クエリごとに個別の SqlConnection を使用する => 接続プールを効果的に活用していますか?
- ビジネス ロジックとオブジェクトのシリアライゼーション => アトミック ユニット間で異なる操作にアプローチする必要がありますか? 他のフィールドでも合計演算を実行したい場合はどうすればよいでしょうか (デフォルトの使用例ではなく、一般的な使用例です)。
誰かがこれを処理するお気に入りの方法を持っている場合は、それについて聞いてみたい. ありがとう。
現在の解決策:
#region Identify atomic units of search query from search parameters (static methods, no i/o, very fast)
List<string> AtomicUnits = AtomicUnitsOfSearch(data);
#endregion
#region Atomic unit-targeted subqueries setup
var atomicUnitQueries = AtomicUnits.Select(au =>
{
return GetItemsFromAtomicUnit(
CreateConstraintSQLQuery(data),
au,
verbosity);
});
#endregion
#region Execute async query across relevant shards; Distinct result item summary query business logic
if (Verbosity.basic.Equals(verbosity)) // return one item per conceptid
{
return (await TaskEx.WhenAll(atomicUnitQueries)).SelectMany(a => a)
.Distinct(new SimpleItemComparer()).ToList(); // Distinct() the fan-out results based on ID
}
else // assume _count verbosity => pick one item per Id, sum Categories and Item counts across atomic units (do not double-count for synonyms)
{
// create the ending schema before executing the results lookup
List<Item> atomicResults = (await TaskEx.WhenAll(atomicUnitQueries)).SelectMany(a => a).ToList();
List<Item> distinctResults = new List<Item>();
foreach (IGrouping<int, Item> g in atomicResults.GroupBy(a => a.Id)) // gets the lists of equivalent items across all atomic units queried
{
Item f = g.First();
distinctResults.Add(new Item(f.Id, f.Name, g.Sum(a => a.CategoryCount), g.Sum(a => a.ItemCount)));
}
return distinctResults;
}
#endregion