1

オートコンプリートで大規模な検索が行われており、誰かがパフォーマンスを改善するためのアイデアを提供できるかどうか疑問に思っていました.

何が起こるのですか:

1) アプリケーションの起動時に、すべてのデータベース エントリをメモリに保存しています。

2) ユーザーが検索ボックスに次のように入力して、オートコンプリートを開始します。

$("#MatterCode").width(110).kendoAutoComplete({
        minLength: 3,
        delay: 10,
        dataTextField: "MatterCode",
        template: '<div class="autoCompleteResultsCode"> ${ data.ClientCode } - ${ data.MatterCode } - ${ data.ClientName } - ${ data.MatterName }</div>',
        dataSource: {
            serverFiltering: true,
            transport: {
                read: "/api/matter/AutoCompleteByCode",
                parameterMap: function() {
                    var matterCode = $("#MatterCode").val();
                    return { searchText: matterCode };
                }
            }
        }, //More Stuff here

3)それは私のコントローラークラスに行きます:

public JsonResult AutoCompleteByCode(string searchText)
{
    if (string.IsNullOrEmpty(searchText))
    {
        Response.StatusCode = 500;
        return Json(new
        {
            Error = "search string can't be empty"
        });
    }

    var results = _publishedData.GetMattersForAutoCompleteByCode(searchText).Select(
            matter => new
            {
                MatterCode = matter.Code,
                MatterName = matter.Name,
                ClientCode = matter.Client.Code,
                ClientName = matter.Client.Name
            });
    return Json(results);
}

4) DAL に入る (「_」で始まるオブジェクトはメモリ オブジェクトです)

public virtual IEnumerable<Matter> GetMattersForAutoCompleteByCode(string input)
{
    InvalidateCache();
    IEnumerable<Matter> results;
    //Searching Matter Object on all 4 given parameters by input.

    if (_lastMatters != null && input.StartsWith(_lastSearch) && _lastMatters.Any())
    {
        results = _lastMatters.Where(m => m.IsInputLike(input)).OrderBy(m => m.Code);
        _lastMatters = results;
    }
    else
    {
        results = _matters.Where(m => m.IsInputLike(input)).OrderBy(m => m.Code);
        _lastMatters = results;
    }

    _lastSearch = input;

    return results.Take(10).ToList();
}

5) isInputLike は内部 bool メソッドです

internal bool IsInputLike(string input)
{
    //Check to see if the input statement exists in any of the 4 fields
    bool check = (Code.ToLower().Contains(input.Trim().ToLower()) 
            || Name.ToLower().Contains(input.Trim().ToLower()) 
            || ClientCode.ToLower().Contains(input.Trim().ToLower()) 
            || ClientName.ToLower().Contains(input.Trim().ToLower()));

    return check;
}

今、私が処理しなければならない結果セットは、100,000 を超える可能性があります。現在、新しいクエリの最初のオートコンプリートは 400,000 レコードを検索する必要があり、機能を犠牲にすることなくパフォーマンスを向上させる方法は考えられません。

何か案は?SQL ストアド プロシージャ呼び出しは LINQ よりも高速ですか?

4

3 に答える 3

2

私はあまり asp/http 派ではありませんが、これを見ると次のようになります。

internal bool IsInputLike(string input)
{
    //Check to see if the input statement exists in any of the 4 fields
    bool check = (Code.ToLower().Contains(input.Trim().ToLower()) 
        || Name.ToLower().Contains(input.Trim().ToLower()) 
        || ClientCode.ToLower().Contains(input.Trim().ToLower()) 
        || ClientName.ToLower().Contains(input.Trim().ToLower()));

    return check;
}

たくさんの新しい文字列を作成していると思います。それには時間がかかります。これを試して、パフォーマンスが向上するかどうかを確認してください

var inp = input.Trim();
bool chk = (Code.IndexOf(inp, StringComparison.CurrentCultureIgnoreCase) > -1)
                || (Name.IndexOf(inp, StringComparison.CurrentCultureIgnoreCase) > -1)
                || (ClientCode.IndexOf(inp, StringComparison.CurrentCultureIgnoreCase) > -1)
                || (ClientName.IndexOf(inp, StringComparison.CurrentCultureIgnoreCase) > -1);

この最初の行 (inp を作成する行) は、コンパイラが繰り返し使用を最適化する必要があるため、それほど重要ではありませんが、読みやすいと思います。

IndexOf メソッドは新しい文字列を作成しません。StringComparison パラメーターを使用すると、すべての ToLower 文字列の作成を回避できます。

于 2013-10-09T02:27:00.417 に答える
2

ここでの主な問題は、最初に 400k オブジェクトをメモリに配置することだと思います。SQL はそれほど遅いわけではありません。最初は限られたデータ セットから始めることをお勧めします。

明らかな最適化の 1 つは次のとおりです。

internal bool IsInputLike(string input)
{
    string input = input.Trim().ToLower();
    //Check to see if the input statement exists in any of the 4 fields
    bool check = (Code.ToLower().Contains(input) 
            || Name.ToLower().Contains(input) 
            || ClientCode.ToLower().Contains(input) 
            || ClientName.ToLower().Contains(input));

    return check;
}

しかし、個人的には、SQLサーバーのデータが属する場所にデータを保持します(それがあなたが使用している場合)。一部のインデックス作成と適切なクエリにより、これが高速化される可能性があります。

このコードを見ると、次のように考え始めます。

public virtual IEnumerable<Matter> GetMattersForAutoCompleteByCode(string input)
{
    InvalidateCache();
    IEnumerable<Matter> results;
    //Searching Matter Object on all 4 given parameters by input.

    if (_lastMatters != null && input.StartsWith(_lastSearch) && _lastMatters.Any())
    {
        results = _lastMatters.Where(m => m.IsInputLike(input)).OrderBy(m => m.Code);
        _lastMatters = results;
    }
    else
    {
        results = _matters.Where(m => m.IsInputLike(input)).OrderBy(m => m.Code);
        _lastMatters = results;
    }

    _lastSearch = input;

    return results.Take(10).ToList();
}

なぜ注文する必要があるのですか?ドロップダウン オートコンプリートで 4 つの項目をフィルター処理する必要があるのはなぜですか? どうせ10個しか取れないなら、注文しないだけじゃないの?特に多くの結果が得られるelseステートメントで、orderbyを削除するとより良い結果が得られるかどうかを確認してください。

個人的には、LINQ to SQL にすべて参加して、SQL サーバーに検索を任せます。このテーブルのインデックス作成を最適化すると、はるかに高速になります。

于 2013-10-09T03:02:32.640 に答える
1

たとえば、すべての名前 (コード、名前、Clientcode、ClientName) を含むビューを作成して、FullName などと連結された単一の列にし、以下のように IsInputLike(..) を置き換えることをお勧めします。

internal bool IsInputLike(string input)
{
    //Check to see if the input statement exists in any of the 4 fields
    return FullName.Contains(input);

}
于 2013-10-09T05:48:21.460 に答える