Как улучшить скорость поиска ADO? - PullRequest
0 голосов
/ 23 февраля 2020

Я пишу приложение на C ++ через Visual Studio 2008 + ADO (не ADO. net). Который будет выполнять следующие задачи один за другим:

  1. Создать таблицу в SQL База данных сервера следующим образом:
CREATE TABLE MyTable 
(
     [S] bigint, 
     [L] bigint, 
     [T] tinyint,   
     [I1] int, 
     [I2] smallint, 
     [P] bigint, 
     [PP] bigint, 
     [NP] bigint, 
     [D] bit, 
     [U] bit
);

Вставить 5 030 242 записи через BULK INSERT

Создать индекс для таблицы:

CREATE Index [MyIndex] ON MyTable ([P]);
Запустите функцию, которая будет искать 65 000 000 раз. При каждом поиске используется следующий запрос:
SELECT [S], [L] 
FROM MyTable 
WHERE [P] = ?

Каждый раз, когда запрос либо ничего не возвращает, либо возвращает одну строку. Если получится одна строка с [S] и [L], я преобразую [S] в указатель файла и затем прочитаю данные со смещением, заданным [L].

Шаг 4 занимает много времени. Поэтому я пытаюсь профилировать его и выясняю, что поисковый запрос занимает большую часть времени. Каждый поиск занимает около 0,01458 секунды.

Я пытаюсь повысить производительность, выполняя следующие задачи:

  1. Использование параметризованного запроса ADO. См. Шаг 4

  2. Выберите только необходимые столбцы. Первоначально я использовал «Выбрать *» для шага 4, теперь я использую Select [S], [L] вместо этого. Это повышает производительность примерно на 1,5%.

  3. Пробовал как кластерный, так и некластерный индекс для [P]. Кажется, что использование некластеризованного индекса будет немного лучше.

Существуют ли другие пробелы для улучшения производительности поиска?

Примечание : [P] является уникальным в таблице.

Большое спасибо.

1 Ответ

0 голосов
/ 23 февраля 2020

Необходимо выполнить пакетную обработку и выполнить один запрос, который возвращает много строк, вместо множества запросов, каждый из которых возвращает только одну строку (и влечет за собой отдельное обратное обращение к базе данных).

Способ выполнения в SQL Сервер должен переписать запрос для использования табличного параметра (TVP) и передать все критерии поиска (обозначенные как ? в вашем вопросе) вместе в одном go.

* 1005. * Сначала нам нужно объявить тип, который будет использовать TVP:
CREATE TYPE MyTableSearch AS TABLE (
    P bigint NOT NULL
);

И тогда новый запрос будет довольно простым:

SELECT
    S,
    L
FROM
    @input I
    JOIN MyTable
        ON I.P = MyTable.P;

Основное осложнение на стороне клиента , как связать TVP с запросом. К сожалению, я не знаком с ADO - для чего это стоит, вот как это будет сделано в ADO. NET и C#:

static IEnumerable<(long S, long L)> Find(
    SqlConnection conn,
    SqlTransaction tran,
    IEnumerable<long> input
) {

    const string sql = @"
        SELECT
            S,
            L
        FROM
            @input I
            JOIN MyTable
                ON I.P = MyTable.P
    ";

    using (var cmd = new SqlCommand(sql, conn, tran)) {

        var record = new SqlDataRecord(new SqlMetaData("P", SqlDbType.BigInt));

        var param = new SqlParameter("input", SqlDbType.Structured) {
            Direction = ParameterDirection.Input,
            TypeName = "MyTableSearch",
            Value = input.Select(
                p => {
                    record.SetValue(0, p);
                    return record;
                }
            )
        };

        cmd.Parameters.Add(param);

        using (var reader = cmd.ExecuteReader())
            while (reader.Read())
                yield return (reader.GetInt64(0), reader.GetInt64(1));

    }

}

Обратите внимание, что мы повторно используем то же самое SqlDataRecord для всех входных строк, что минимизирует распределение. Это задокументированное поведение, и оно работает, потому что ADO. NET транслирует TVP.


Примечание: [P] уникально в таблице.

Тогда Вы также должны сделать индекс на P уникальным - для корректности и во избежание траты места на юниквитере.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...