Повышение производительности слияния с помощью сбора и изменения данных - PullRequest
1 голос
/ 03 февраля 2020

Сегодня я пытаюсь настроить производительность базы данных аудита. У меня есть законная причина для отслеживания изменений в строках, и я реализовал набор таблиц, используя метод таблиц System Versioned в SQL Server 2016.

Мой общий процесс помещает данные «RAW» в исходный таблица из исходной системы. Отсюда у меня затем есть процесс MERGE, который берет данные из таблицы RAW и сравнивает каждый столбец в таблице RAW с тем, что существует в промежуточной таблице версионной системы с поддержкой аудита, и решает, что изменилось. Затем система контроля версий строк сообщает мне, что изменилось, а что нет.

Проблема этого подхода в том, что мои таблицы очень широки. Некоторые из них имеют 400 столбцов или более. Даже для таблиц с 450 000 записей SQL сервер занимает около 17 минут для выполнения операции MERGE. Это действительно замедляет производительность нашего решения, и кажется, что это сильно помогло бы, если бы мы могли ускорить его. В настоящее время у нас есть сотни таблиц, для которых мы должны сделать это.

В настоящий момент таблицы RAW и STAGE проиндексированы в столбце ID.

В нескольких местах мы читали может использовать функцию CHECKSUM или HASHBYTES для записи значения в экстракт RAW. (Как бы вы назвали это? GUID? UUID? Ха sh?). Затем мы сравним вычисленное значение с тем, что существует в таблице STAGE. Но вот в чем суть: во многих столбцах часто довольно много значений NULL. Предполагается, что мы приводим все типы столбцов к одинаковым (nvarchar (max))?, И значения NULL, по-видимому, приводят к тому, что все вычисления контрольной суммы не совпадают. Поэтому я также кодирую множество операторов ISNULL (, 'UNKNOWN') в свой код.

Итак, есть ли здесь более эффективные методы для улучшения производительности слияния? Я подумал, что мог бы использовать столбец с обновленной строкой в ​​качестве отдельного значения вместо контрольной суммы, но я не уверен, что это пройдет законную проверку. Legal обеспокоен тем, что строки могут редактироваться вне интерфейса, и столбец не всегда будет обновляться. Я видел подходы с разработчиками, использующими функцию сцепления (показанную ниже) для объединения многих значений столбцов вместе. Кажется, что это требует большого объема кода и слишком дорого для вычисления / приведения столбцов.

Итак, мои вопросы:

  • Учитывая ситуативную реальность, могу ли я каким-либо образом улучшить производительность MERGE?
  • Должен ли я использовать контрольную сумму, или хеш-байты, и почему?
  • Какой метод хеш-байтов здесь наиболее целесообразен? (Я сравниваю только одну строку RAW с другой строкой STAGE на основе правильного совпадения идентификатора)?
  • Я что-то упустил из-за функций, которые могли бы сделать это сравнение быстрее или проще в прочитанном мной чтении? Кажется странным, что кроме CONCAT нет лучших функций для этого в SQL Server.
  • Я написал код ниже, чтобы показать некоторые идеи, которые я рассматриваю. Есть ли что-то лучше, чем я написал ниже?

    DROP TABLE IF EXISTS MyTable;
    
    CREATE TABLE MyTable
        (C1 VARCHAR(10),
         C2 VARCHAR(10),
         C3 VARCHAR(10)
         );
    
    INSERT INTO MyTable
        (C1,C2,C3)
    VALUES
        (NULL,NULL,NULL),
        (NULL,NULL,3),
        (NULL,2,3),
        (1,2,3);
    
    
    SELECT
        HASHBYTES('SHA2_256',
        CONCAT(C1,'-',
               C2,'-',
               C3)) AS HashbytesValueCastWithNoNullCheck,
    
    
        HASHBYTES('SHA2_256',
        CONCAT(CAST(C1 as varchar(max)),'-',
               CAST(C2 as varchar(max)),'-',
               CAST(C3 as varchar(max)))) AS HashbytesValueCastWithNoNullCheck,
    
    
        HASHBYTES('SHA2_256',
        CONCAT(ISNULL(CAST(C1 as varchar(max)),'UNKNOWN'),'-',
               ISNULL(CAST(C2 as varchar(max)),'UNKNOWN'),'-',
               ISNULL(CAST(C3 as varchar(max)),'UNKNOWN'))) AS HashbytesValueWithCastWithNullCheck,
        CONCAT(ISNULL(CAST(C1 as varchar(max)),'UNKNOWN'),'-',
               ISNULL(CAST(C2 as varchar(max)),'UNKNOWN'),'-',
               ISNULL(CAST(C3 as varchar(max)),'UNKNOWN')) AS StringValue,
        CONCAT(C1,'-',C2,'-',C3) AS ConcatString,
        C1,
        C2,
        C3
    FROM
        MyTable;
    
    '''
    

1 Ответ

2 голосов
/ 03 февраля 2020

Учитывая ситуативную реальность, могу ли я каким-то образом улучшить производительность MERGE?

Вы должны протестировать, но сохраняете ха sh для каждой строки, вычисляя га sh для новых строк, и сравнение на основе (ключ, га sh) должно быть дешевле, чем сравнение каждого столбца.

Стоит ли использовать контрольную сумму или хеш-байты и почему?

HASHBYTES имеет гораздо более низкую вероятность пропустить изменение. Грубо говоря, с CHECKSUM вы, вероятно, в конечном итоге пропустите одно или два изменения, а с HASHBYTES вы, вероятно, никогда не пропустите изменение. См. Примечания здесь: BINARY_CHECKSUM .

Я что-то упустил с функциями, которые могли бы сделать это сравнение быстрее или проще в прочитанном мной чтении?

Нет. Не существует специального способа сравнения нескольких столбцов.

Есть ли что-то лучше, чем то, что я написал ниже?

Вам определенно следует заменить нули, иначе строка (1,null,'A') и (1,'A',null) получит тот же ха sh. И вы должны заменить нули и разделитель чем-то, что не будет отображаться в качестве значения в любом столбце. И если у вас есть текст Unicode, преобразование в varchar может стереть некоторые изменения, поэтому безопаснее использовать nvarchar. Например:

HASHBYTES('SHA2_256',
    CONCAT(ISNULL(CAST(C1 as nvarchar(max)),N'~'),N'|',
           ISNULL(CAST(C2 as nvarchar(max)),N'~'),N'|',
           ISNULL(CAST(C3 as nvarchar(max)),N'~'))) AS HashbytesValueWithCastWithNullCheck

JSON в SQL Сервер работает очень быстро. Таким образом, вы можете попробовать шаблон как:

select t.Id, z.RowJSON, hashbytes('SHA2_256', RowJSON) RowHash
from SomeTable t
cross apply (select t.* for json path) z(RowJSON)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...