Оператор распараллеливания (Exchange)

По материалам статьи Craig Freedman «The Parallelism Operator (aka Exchange)»

Перевод Ирины Наумовой

Как я уже писал в статье Введение в распараллеливание исполнения запроса , итератор параллелизма (или обмена - Exchange operator) фактически привносит в процесс выполнения запроса возможность распараллеливания задачи. Оптимизатор помещает оператор обмена в том месте, где происходит разделение на несколько потоков, и оператор обмена перемещает строки между потоками.

Posted by gladchenko
Filed under:

SQL Server 2008 R2 для многопроцессорных систем

Вашему вниманию предлагается обзор современных возможностей SQL Server 2008 R2 по поддержке многопроцессорных серверных архитектур. Статья относится только к платформе Windows и затрагивает только те архитектурные особенности многопроцессорных систем, которые показались автору значимыми при развёртывании приложений баз данных SQL Server.
Статья адресована опытным администраторам баз данных SQL Server, знакомым с архитектурой SQLOS и современными платформами Intel и AMD.

Оглавление

Posted by gladchenko
Filed under:

Руководство по производительности загрузки данных

По материалам технической статьи, посвящённой SQL Server: The Data Loading Performance Guide
Авторы: Томас Кайзер (Thomas Kejser), Питер Карлин (Peter Carlin) и Стюарт Озер (Stuart Ozer)
Техническая рецензия и экспертиза: Sunil Agarwal, Ted Lee, David Schwartz, Chris Lee, Lindsey Allen, Hermann Daeubler, Juergen Thomas,Sanjay Mishra, Denny Lee, Peter Carlin, Lubor Kollar
Особая благодарность: Henk van der Valk (Unisys), Alexei Khalyako и Marcel van der Holst
Перевод: Александр Гладченко, Ирина Наумова, Влад Щербинин и Алексей Халяко
Дата издания: январь 2009г.
Статья относится к продуктам: SQL Server 2008 и SQL Server 2005

Резюме: Этот документ описывает стратегию массовой загрузки больших объёмов информации в базы данных SQL Server. Статья охватывает два распространённых метода, а также методологии повышения производительности и оптимизации процесса массовой загрузки данных.

Введение

Настоящая техническая статья описывает существующие стратегии массовой загрузки данных, которые применяются для быстрого внесения масштабных изменений в базах данных Microsoft ® SQL Server ®.
Прежде, чем углубиться в подробности методов массовой загрузки, давайте освежим в памяти некоторые базовые принципы минимального протоколирования, которые будут представлены в главе: "Разъяснения по минимально протоколируемым операциям".
Следующие две главы: "Методы массовой загрузки" и "Другие минимально протоколируемые операции и операции над метаданными" содержат краткий обзор двух ключевых и взаимосвязанных концепций высокопроизводительной загрузки данных, таких как массовый импорт и экспорт данных и операции только над метаданными.
После небольшого погружения в тему, мы приступим к описанию способов использования этих методов в пользовательских сценариях. Приводимые тут примеры сценариев призваны проиллюстрировать типовые подходы, которые можно найти в главе: "Решения для типовых задач массовой загрузки". Особо будут рассмотрены такие сценарии, когда загрузка данных в таблицу должна выполняться при одновременном чтении из этой же таблицы. В главе "Массовая загрузка, запросы с NOLOCK и Read Committed Snapshot Isolation" описаны методы, которые могут использоваться для достижения параллельной загрузки и чтения данных.
Эта техническая статья заканчивается главой "Оптимизация массовой загрузки данных", в которой рассказано о поиске и устранении сопутствующих загрузке данных проблем.

Разъяснения по минимально протоколируемым операциям

Для поддержки сценариев загрузки больших объёмов данных, в SQL Server реализована возможность минимального протоколирования операций. В отличие от полного протоколирования, которое интенсивно использует журнал транзакций для отслеживания всех изменений строк, минимальное протоколирование операций отслеживает только распределение экстентов и изменения метаданных. Из-за того, что в журнале транзакций отслеживается существенно меньше информации, минимально протоколируемые операция, зачастую, исполняются быстрее, чем полностью протоколируемые, т.е. само протоколирование вносит большой вклад во время исполнения операции. Кроме того, из-за уменьшения необходимости осуществлять запись в журнал транзакций, размер журнала будет заметно меньше, и требования к поддержке дисковых операций ввода-вывода для него будут существенно ниже.
Следует помнить, что операция может являться операцией массовой загрузки, но при этом выполняться без минимального протоколирования. Например, при массовой загрузке данных в кластеризованный индекс или кучу, минимальное протоколирование может не использоваться. Минимальное протоколирование обычно обеспечивает дополнительный выигрыш по производительности, но даже без минимального протоколирования, массовая загрузка потребует меньше ресурсов, чем традиционная вставка строк данных.
Вопреки мифам о SQL Server, минимально протоколируемые операции могут являться частью транзакции. Поскольку все изменения в структурах распределения запротоколированы, становится возможным откатить минимально протоколируемые операции.
Минимально протоколируемые операции доступны, только если база данных использует простую модель восстановления или модель восстановления с неполным протоколированием (bulk-logged). Для получения более подробной информации, обратитесь к статье: "Операции, для которых возможно минимальное протоколирование". Обратите внимание, что при планировании стратегии резервного копирования базы данных нужно учитывать возможность выполнения массовых операций в базе данных с неполным протоколированием. Для получения более подробной информации, обратитесь к статье: Резервное копирование с использованием модели восстановления с неполным протоколированием.

Флаг трассировки 610

В SQL Server 2008 появился флаг трассировки 610, который управляет минимальным протоколированием вставок в проиндексированные таблицы. Этот флаг трассировки может быть включен с помощью одного из следующих методов:

  1. Добавление флага к параметрам запуска SQL Server.
  2. Выполнение команды

    • В случае локального включения, флаг трассировки действует только на текущий сеанс. Такое включение флага трассировки 610 бывает полезно, если он нужен только для ограниченного набора сценариев загрузки данных на экземпляре сервера, и действует только на те инструкции Transact-SQL, которые выполняются в текущем подключении.
    • В случае глобального включения, этот флаг трассировки будет действовать на все подключения к серверу, пока он не будет выключен или произойдёт рестарт сервера. Для получения более подробной информации об использовании команды DBCC для включения и выключения флагов трассировки, прочтите статьи: "DBCC TRACEON (Transact-SQL)" и "DBCC TRACEOFF (Transact-SQL)".

Прежде чем использовать этот флаг трассировки, нужно учесть ограничения, о которых упоминалось в предыдущем разделе.
Не каждая строка, вставленная в кластеризованный индекс во время действия флага трассировки 610, будет протоколироваться минимально. Когда операция массовой загрузки приводит к необходимости распределения новой страницы, вставка всех строк, которые будут последовательно заполнять новую страницу, будет подпадать под минимальное протоколирование. Вставка же строк в страницы, которые были распределены раньше, чем началась операция массовой загрузки, будет происходить с полным протоколированием, точно так же, как и строк, которые были затронуты во время разбиения страниц в процессе загрузки. Это означает, что для некоторых таблиц частично может использоваться полное протоколирование вставки.
Если включение флага трассировки 610 приводит к использованию сервером минимального протоколирования, это в общем случае может привести к повышению производительности. Но, как и в случаях с другими флагами трассировки, обязательно нужно убедиться, что подобное воздействие на производительность будет воспроизводиться и в промышленной среде под реальной рабочей нагрузкой.
Давайте рассмотрим следующие два примера:

Пример 1: Имеется таблица, кластеризованая по целочисленному ключу, содержащему четные числа от 0 до 14. Таблица имеет четыре страницы листового уровня, и эти страницы не заполнены полностью и могут содержать еще по две строки на каждой странице.
Выполняется массовая загрузка ещё восьми новых строк, со значениями ключа, представляющими собой возрастающую последовательность нечётных чисел от 1 до 15. Новые строки будут размещаться в существующих страницах. На представленной ниже иллюстрации показано, как таблица будет выглядеть до и после операции загрузки.


Рисунок 1: Полностью протоколируемая вставка при включённом флаге трассировки 610

В этом примере не происходит распределения новых страниц, и включение флага трассировки 610 не приведёт к повышению производительности вставки за счёт минимального протоколирования.

Пример 2: Рассмотрим альтернативный сценарий: теперь таблица первоначально занимает две страницы, обе заполнены полностью и содержат записи со значениями ключа от 0 до 7. Далее выполняется массовая загрузка строк со значениями ключа от 8 до 15.


Рисунок 2: Минимально протоколируемая вставка при включённом флаге трассировки 610

Этот пример демонстрирует, что происходит при включённом флаге трассировки 610. В данном случае, вставка записей со значениями ключа 8-15 на выделенные для этого новые страницы (на рисунке выше это светло-синие прямоугольники) будет проходить с минимальным протоколированием.
Один из способов проверки того, сколько новых страниц было распределено для размещения записей таблицы, состоит в выполнении запроса к sys.dm_db_partition_stats. Следующий сценарий Transact-SQL вернёт число страниц каждого индекса и таблицы:

    SELECT OBJECT_NAME(p.object_id) AS object_name
           , i.name AS index_name
           , ps.in_row_used_page_count
    FROM sys.dm_db_partition_stats ps
    JOIN sys.partitions p
           ON ps.partition_id = p.partition_id
    JOIN sys.indexes i
           ON p.index_id = i.index_id
           AND p.object_id = i.object_id

Сравнивая возвращаемые этим сценарием результаты до и после выполнения операции массовой загрузки, можно увидеть, сколько было распределено новых страниц.
Если во время массовой заливки в В-дерево используется флаг трассировки 610, рекомендуется указывать наибольшее из возможных значений параметра BATCHSIZE. В таком случае, SQL Server сможет распределить в заполняемой секции большое число новых страниц для каждого блока загрузки и секции. В некоторых случаях, такое распределение с включённым флагом трассировки 610, даже если страницы потом будут возвращены обратно, может привести к увеличению ввода-вывода, по сравнению с аналогичной нагрузкой без включения этого флага трассировки. Однако, при загрузке небольшого количества секций и использовании большого размера пакета, включение флага трассировки 610 может привести к существенному повышению производительности по сравнению с полным протоколированием вставок. Для получения более подробной информации о поведении минимального протоколирования при включении флага трассировки 610, ознакомьтесь со статьёй: "New update on minimal logging for SQL Server 2008" в блоге SQL Server Storage Engine.

Влияние на ввод-вывод минимального протоколирования с включённым флагом 610

Когда фиксируется транзакция массовой загрузки, которая должна протоколироваться минимально, все загруженные страницы должны быть сброшены на диск ещё до того, как транзакция будет завершена. Сбрасываемые на диск страницы, которые не попали на диск во время исполнения предыдущих операций контрольной точки, могут создать довольно большое количество операций ввода-вывода, носящих случайный характер. В противоположность этому, при полном протоколировании операций образуются последовательные запросы ввода-вывода для записи в файл журнала транзакций, и не требуется сброс загруженных страниц на диск во время фиксирования транзакции.
Если предполагаемый сценарий загрузки подразумевает небольшие операции вставки в В-деревья, и эти операции не пересекаются со временем прохождения контрольных точек, а также при условии использования медленной подсистемы ввода-вывода, использование минимального протоколирования фактически может негативно повлиять на скорость вставки.

Сводка по возможным условиям минимального протоколирования

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

Индексы
таблицы
Число строк
в таблице
Подсказка
оптимизатору
Без флага
610
С флагом
610
Возможность
конкурентного доступа
Куча Любое TABLOCK Минимальное Минимальное ДА
Куча Любое НЕТ Полное Полное ДА
Куча + Индекс Любое TABLOCK Полное Выбор (3) НЕТ
Кластерный Пустая TABLOCK, ORDER (1) Минимальное Минимальное НЕТ
Кластерный Пустая НЕТ Полное Минимальное ДА (2)
Кластерный Любое НЕТ Полное Минимальное ДА (2)
Кластерный Любое TABLOCK Полное Минимальное НЕТ
Кластерный + Индекс Любое НЕТ Полное Выбор (3) ДА (2)
Кластерный + Индекс Любое TABLOCK Полное Выбор (3) НЕТ

Таблица 1: Сводка по возможным условиям минимального протоколирования

(1) Если используется метод INSERT | SELECT, можно не использовать подсказку ORDER, однако вставляемые строки должны быть отсортированы в том же порядке, что и кластеризованный индекс. При использовании BULK INSERT необходимо использовать подсказку ORDER.
(2) Параллельная загрузка возможна только в некоторых случаях. См. главу "Массовая загрузка при наличии индексов". Кроме того, минимальное протоколирование будет использовано только для тех строк, которые попали на вновь распределённые страницы.
(3) Вставка в таблицу с некластеризованным индексом может быть полностью или минимально протоколирована в зависимости от выбранного оптимизатором плана исполнения запроса.

Методы массовой загрузки

Для обеспечения задач быстрого исполнения операций вставки данных, SQL Server поставляется с несколькими базовыми методами массовой загрузки. Этот раздел посвящён подробному описанию следующих методов:

  • Импорт данных средствами Integration Services
  • BCP
  • BULK INSERT
  • INSERT…SELECT
  • SELECT INTO

В этой статье, термин "массовая загрузка" относится к использованию любого из представленных в текущем разделе методов. Термин "BULK INSERT" (в верхнем регистре) относится к инструкциям Transact-SQL, которые базируются на тех методах массовой загрузки, которые описаны в статье "BULK INSERT (Transact-SQL)".
Использование этих методов требует понимания их возможностей и ограничений. В этой главе будут кратко описаны те варианты, которые возможны для массовой загрузки данных из внешних по отношению к Ядру SQL Server источников, а также из источников, обслуживаемых тем же самым Ядром.
Кроме того, доступные нам методы массовой загрузки данных имеют в SQL Server свои программные интерфейсы, в ADO.NET это класс SQLBulkCopy, в OLE DB это IRowsetFastload, а в ODBC это библиотека SQL Server Native Client. Несмотря на то, что тематика программных интерфейсов выходит за рамки данной статьи, многие рекомендации, лучшие практики, параметры настройки и т.п., которые рассматриваются в статье, пригодны и для случаев использования программных интерфейсов массовой загрузки.

Импорт данных средствами Integration Services

Служба SQL Server Integration Services предоставляет самые гибкие средства для массовой загрузки данных в SQL Server. Данные могут читаться из любого источника, который совместимым с Integration Services, с преобразованием и конвертацией данных в памяти, и массовой загрузкой напрямую в SQL Server, без промежуточной материализации данных на диске. Поскольку Integration Services является самостоятельным процессом, он потенциально может быть запущен на другом компьютере, что позволяет существенно разгрузить процессоры SQL Server, сняв с них нагрузку на преобразование данных. Это довольно простой способ масштабирования массовых операций, когда необходимо добиться повышения производительности.
Существует две возможности загрузки данных, которые обеспечивают минимальное протоколирование и доступ к функциональности массовой загрузки SQL Server: это компоненты загрузки с именами: "Назначение SQL Server" и "Назначение OLE DB" (SQL Server Destination и OLE DB Destination).

Назначение SQL Server

Загрузка в Назначение SQL Server является самым быстрым способом массовой загрузки из потока данных SQL Server Integration Services. Эта возможность поддерживает все варианты параметров массовой загрузки SQL Server, кроме ROWS_PER_BATCH.
Следует помнить, что использование этой возможности может потребовать подключения к SQL Server через Общую память, а это означает, что необходимо запускать службу Integration Services на том же компьютере, что и службы Ядра SQL Server.

Назначение OLE DB

Загрузка в Назначение OLE DB поддерживает все варианты массовой загрузки, которые доступны для загрузки в SQL Server. Однако, для поддержки упорядоченной массовой загрузки, требуется некоторая дополнительная настройка. Для получения более подробной информации, ознакомьтесь со статьями: "Управление порядком сортировки во время массового импортирования данных" и "Выполнение операций массового копирования". В этих статьях описано, как использовать параметры команд и утилит, или интерфейсы OLE DB для загрузки отсортированных данных (при использовании интерфейсов массовой загрузки, нужно включить режим "fast load").
Загрузка в OLE DB для подключения к SQL Server использует протокол TCP/IP или именованные каналы. Это означает, что Назначение OLE DB, в отличие от Назначения SQL Server, может работать на другом компьютере, не на том, на который идёт массовая загрузка. Поскольку пакеты службы Integration Services, которые используют OLE DB, не обязаны работать непосредственно на том компьютере, на котором запущен SQL Server, становится возможным масштабирование ETL потоков с помощью множества недорогих серверов.

BCP

BCP (Bulk Copy Program) - утилита командной строки, используемая для извлечения и импорта данных в SQL Server. Программа разработана с использованием интерфейсов массового копирования и позволяет быстро вставлять данные из текстовых файлов напрямую в SQL Server. Кроме того, она позволяет экспортировать данные из таблиц SQL Server или из запросов в текстовые файлы.
BCP может читать файлы данных в формате таблиц баз данных (собственный формат SQL Server), а так же и другие форматы. Использование собственного формата работает наиболее быстро, за счёт минимального синтаксического анализа входного текстового файла.

BULK INSERT

Команда BULK INSERT представляет собой внутрипроцессный метод для переноса данных из текстовых файлов в SQL Server. Поскольку всё это выполняется в процессе Sqlservr.exe, загрузка файлов данных в SQL Server происходит очень быстро.
BULK INSERT не может использоваться для задач экспорта данных, только для импорта. Кроме этого ограничения, команда наделена всеми возможностями, присущими BCP. Команда BULK INSERT вызывается в сценариях, написанных на Transact-SQL, что делает её идеальной для использования в хранимых процедурах, основанных на Transact-SQL задачах ETL, и заданиях по расписанию службы SQL Server Agent.

SELECT INTO

Инструкция SELECT INTO создаёт новую таблицу, основанную на результирующем наборе инструкции SELECT. Вставка новых строк протоколируется минимально, что делает такой способ загрузки данных очень быстрым. К сожалению, для этой инструкции не существует способов управления BATCHSIZE или ROWS_PER_BATCH.
Ещё одним ограничением инструкции SELECT INTO является то, что результирующая таблица должна находиться в файловой группе по умолчанию. Хотя можно временно изменить значение по умолчанию для файловых групп, перед тем, как выполнить инструкция SELECT INTO, широкое использование такого сценария маловероятно.

INSERT…SELECT

В SQL Server 2008 появился новый способ, который позволяет добиться минимального протоколирования для операций вставки. Речь идёт об инструкции INSERT (Transact-SQL), которая при некоторых условиях может быть минимально протоколируемой.
У этой возможности есть несколько ограничений. Прежде всего, нужно отметить, что с этим методом не могут использоваться такие параметры массовой загрузки, как указание размера фиксации (commit size), управление проверкой ограничений и отключение триггеров. Во-вторых, таблица, куда идёт заливка, должна быть заблокирована с помощью монопольной (X) блокировки, в то время как другие методы массовой загрузки (Integration Services, BCP и BULK INSERT), описанные в этой статье, используют блокировку массового обновления (BU). Поскольку монопольная (X) блокировка не совместима с другими монопольными (X) блокировками, единовременно может выполниться только одна вставка. Для получения более подробной информации о блокировках массового обновления (BU) и монопольных (X) блокировках, ознакомьтесь со статьёй в SQL Server Books Online: "Режимы блокировки".
Несмотря на то, что операция вставки будет однопоточной, инструкция INSERT…SELECT может быть распараллелена. Распараллеливаться сможет та часть инструкции INSERT…SELECT, в которой Ядро SQL Server исполняет инструкцию SELECT. Ввиду того, что этот метод может преобразовать данные во время транзита, высокая скорость преобразования данных, которое основано на инструкции Transact-SQL, достигается за счёт того, что запрос оптимизируется средствами SQL Server. Для таких операций, которые основаны на инструкции SELECT, именно это определяет скорость работы.
Будет ли минимально протоколироваться операция INSERT…SELECT в кластеризованный индекс, зависит от того, включён ли флаг трассировки 610. Операция INSERT…SELECT в кучу может минимально протоколироваться даже без включения флага трассировки 610.

Куча

Инструкция INSERT, которая получает строки от операции SELECT и вставляет их в кучу, будет протоколироваться минимально, когда для таблицы назначения используется подсказка WITH (TABLOCK).
Для того чтобы получить минимальное протоколирование операций вставки в кучу, используйте следующий синтаксис Transact-SQL:

    INSERT INTO <ТаблицаНазначения> (<Колонки>) WITH (TABLOCK)
    SELECT <Колонки> FROM <НекоторыеУтверждения>

Обратите внимание, что куча, куда направлена вставка, не должна быть пуста. Наряду с тем фактом, что таблица назначения может находиться в любой файловой группе (или даже использовать схему секционирования), эта методика предлагает больше гибкости, чем вариант с SELECT INTO.

Кластеризованные индексы

Если операция INSERT…SELECT направлена в кластеризованный индекс, эта операция будет протоколироваться минимально, при условии, что будет активизирован флаг трассировки 610. Обратите внимание, что минимальное протоколирование используется даже без подсказки TABLOCK. Это означает, что может существовать несколько параллельных инструкций INSERT…SELECT, которые вставляют в таблицу данные в одно и то же время и в режиме минимального протоколирования. У такой возможности тоже есть несколько ограничений. Для получения более подробной информации, прочтите главу: "Массовая загрузка при наличии индексов".
Кроме этого, операция массовой загрузки будет минимально протоколироваться без флага трассировки, когда истинны оба представленные ниже условия:

  • Для таблицы, куда направлена вставка, определена подсказка WITH (TABLOCK)
  • Таблица, куда направлена вставка, пуста

В упомянутом выше случае, на получающую данные таблицу будет наложена блокировка схемы (Sch-M), что будет препятствовать любому параллелизму. В этом случае вставка будет выполняться упорядоченно. Для получения более подробной информации, прочтите главу "Вставка упорядоченных данных".

INSERT…SELECT с чтением из текстовых файлов или данных OLE DB источников

Команду INSERT…SELECT в SQL Server можно использовать для чтения из любого OLE DB источника данных. Для реализации такой возможности используется команда OPENROWSET в качестве исходной инструкции SELECT.
Использование OPENROWSET позволяет добиться точно такого же поведения, как у BULK INSERT и INSERT…SELECT, кроме того, становится возможным использовать соединения или фильтровать входной набор данных до их вставки.
Следует помнить, что BULK INSERT позволяет направлять несколько минимально протоколируемых потоков в кучу, а INSERT…SELECT может использовать для вставки только один поток.
OPENROWSET также позволяет настраивать несколько параметров пакетной обработки вставки. При использовании OPENROWSET в качестве источника INSERT…SELECT, доступны для настройки следующие параметры:

  • ORDER
  • ROWS_PER_BATCH или BATCH_SIZE
  • IGNORE_CONSTRAINTS и IGNORE_TRIGGERS
  • FIRSTROW и LASTROW
  • KEEPDEFAULTS и KEEPIDENTITY
  • /p>

Для получения более подробной информации, прочтите статью SQL Server Books Online OPENROWSET (Transact-SQL).

Возможности записи для собственного ПО

"ODBC", "OLE DB", ".NET" и "DB-library for SQL Server" позволяют реализовать свои собственные методы массовой загрузки данных. Полное описание программных интерфейсов (API) массовой загрузки и их возможности выходят за рамки настоящей статьи.
Для получения информации о разработке собственных методов массовой загрузки, ознакомьтесь со следующими материалами:

Варианты выбора средств массовой загрузки

В представленной ниже таблице приведен краткий обзор разных методов массовой загрузки, доступных в Integration Services и SQL Server.

Функциональность Integration Services BULK INSERT BCP INSERT…SELECT
Получатель SQL Server Получатель OLE DB
Протокол Общая память (Shared Memory) TCP/IP, Named Pipes В памяти TCP/IP, Shared Memory, Named Pipes В памяти
Скорость Быстро / Быстрейший(4) Быстро / Быстрейший (1) Быстрейший Быстро Медленно / Быстрейший (2)
Источник данных Любой Любой Только файл с данными Только файл с данными Любой OLE DB источник
Поддержка массовых интерфейсов Нет поддержки собственного формата Нет ORDER, Нет поддержки собственного формата Все Все Нет подсказок
Блокировка с подсказкой TABLOCK для кучи BU BU BU BU X
Преобразование при транзите Да Да Нет Нет Да
Размер ввода-вывода читаемого блока Зависит от источника (3) Зависит от источника (3) 64Кб 64Кб До 512Kб
Версии SQL Server 2005 и 2008 2005 и 2008 7.0, 2000, 2005 и 2008 6.0, 7.0, 2000, 2005 и 2008 2008
Вызывается из: DTEXEC / BIDS DTEXEC / BIDS Transact-SQL Командная строка Transact-SQL

Таблица 2: Варианты выбора средств массовой загрузки

(1) Если DTEXEC выполняется не на том же сервере, на котором запущена служба SQL Server, Integration Services может работать очень быстро, за счет освобождения Ядра базы данных от операций преобразования данных.
(2) Обратите внимание, что INSERT…SELECT не позволяет выполнять вставки в одну и ту же таблицу параллельно. Если вставка идёт в одну таблицу, скорее всего наиболее быстрым вариантом будет Integration Services, потому что это позволяет выполнять несколько потоков параллельно.
(3) Размер блока чтения зависит от источника. Для текстовых файлов, используются размеры блока 128Кб.
(4) Если назначением является SQL Server, то процессорных циклов будет затрачено больше, чем у BULK INSERT, и это будет ограничением максимальной скорости. Но поскольку Ядро базы данных освобождается от операций преобразования данных, производительность однопоточной вставки будет быстрее, чем у BULK INSERT.

Немного о BATCHSIZE и ROWS_PER_BATCH

Эти два параметра массовой загрузки нуждаются в некоторых разъяснениях. Первый из них - BATCHSIZE, он предписывает, сколько строк будет передано единовременно во время массовой операции. Если оставить это значение по умолчанию, вся массовая операция будет выполнена в одной большой транзакции. Однако, если параметру задано не нулевое значение, для передачи указанного серверу количества строк будет создана и завершена новая транзакция. Обычно параметр BATCHSIZE используется для поэтапной передачи всех строк, чтобы в случае сбоя не передавать весь пакет целиком. Однако нужно понимать, что автоматическая "докачка" строк не предусмотрена. Отслеживание того, сколько строк было уже вставлено, является задачей разработчика приложения. Обратите внимание на то, что слишком маленькие значения для параметра BATCHSIZE могут привести к снижению производительности, из-за неоправданного увеличения затрат ресурсов на обслуживание транзакций. На Рисунке 3 можно увидеть, как значение этого параметра влияло на массовую загрузку 17Гб в таблицу одним потоком. Обратите внимание, что для значений больше 10000 наблюдается лишь небольшое увеличение производительности.
Ненулевые значения BATCHSIZE наиболее полезны для вставки в индексированные таблицы. Если не указывать упорядочивание данных, операция SORT будет выполнена внутренними механизмами для каждого индекса при вставке каждого пакета. Т.е. достаточно маленькие значения BATCHSIZE могут позволить выполнить сортировку в памяти, и обойтись без промежуточной материализации на диске. Однако это может стать причиной роста фрагментации индексов, которая также может привести к падению производительности запросов, которым свойственны упорядоченные просмотры диапазонов ключа. Фрагментации из-за вставки в кластеризованный индекс можно избежать, если вставлять данные в уже отсортированном виде, как это описано в главе "Вставка упорядоченных данных".
Стоит проверить несколько вариантов настройки параметра BATCHSIZE для свойственных вашим приложениям нагрузок, что позволит увидеть влияние разных значений на производительность загрузки и итоговую фрагментацию. Кроме того, ненулевое значение параметра BATCHSIZE может оказаться полезным, если не используется подсказка TABLOCK, и нужно избежать эскалации блокировок. Типичной для этого ситуацией является наличие запросов на чтение, параллельных загрузке, когда нежелательно их блокировать. Даже если для предотвращения эскалации блокировок используются другие методы (например, используется ALTER TABLE), ненулевой BATCHSIZE будет необходим для того, чтобы предотвратить утилизацию слишком большого объёма памяти, которая используется блокировками при большой нагрузке.


Рисунок 3: Зависимость минимального протоколирования от размера пакета в BCP

Вторым параметром является ROWS_PER_BATCH, он задаёт приблизительное число строк во всём пакете операции массовой загрузки данных. Этот параметр имеет смысл, когда параметр BATCHSIZE не задан. Кроме того, если в массовой загрузке используется BATCHSIZE больше нуля, параметру ROW_PER_BATCH нельзя присваивать значения, необходимо оставить его пустым или нулевым.
С помощью ROWS_PER_BATCH можно подсказать оптимизатору запросов объём загрузки, а тот, в свою очередь, постарается оптимизировать ход выполнения массовой операции. Если известно примерное число строк, оптимизатор сможет лучше распределить ресурсы и необходимый объем памяти для обслуживания пакета.
Поскольку информация о параметрах BATCHSIZE и ROWS_PER_BATCH может вас запутать, так как у этих параметров в разных инструментах массовой загрузки разные имена, мы свели эти два разных метода в одну таблицу, которая показывает способы их использования:

Метод массовой операции BATCHSIZE ROWS_PER_BATCH
Integration Services
Назначение OLE DB
См. "Размеры пакетов в Integration Services" Rows per Batch
Integration Services
Назначение SQL Server
См. "Размеры пакетов в Integration Services" Не применимо
BCP -b <X> (1) -h "ROWS_PER_BATCH = <X>"
BULK INSERT BATCHSIZE = X ROWS_PER_BATCH = <X>
INSERT…SELECT (2) N/A ROWS_PER_BATCH (3)

Таблица 3: BATCHSIZE и ROWS_PER_BATCH для разных массовых методов

(1) Если BATCH_SIZE не задан, BCP будет использовать в качестве значения по умолчанию 1000.
(2) Для INSERT…SELECT нельзя управлять параметрами BATCHSIZE и ROWS_PER_BATCH.
(3) В качестве источника нужно использовать OPENROWSET(BULK…).

Размеры пакетов в Integration Services

В Integration Services размеры пакетов используются по-другому по сравнению с методами массовой загрузки.
По умолчанию Integration Services создаёт один пакет на конвейерный буфер. После того, как буфер сбрасывается на диск, пакет (имеется в виду пакет вставки, а не пакет SSIS) фиксируется. Можно изменить такое поведение, задав значение параметру "Maximum Insert Commit Size" (Максимальный фиксируемый размер вставок) для получателя данных.
В представленной ниже таблице показаны возможные варианты использования "Maximum Insert Commit Size" (MICS).

Максимальный фиксируемый
размер вставок
Эффект
MICS > размер буфера Такая установка игнорируются. Одна фиксация выполняется для каждого буфера.
MICS = 0 Весь пакет фиксируется целиком, как это было бы при BATCHSIZE = 0.
MICS < размер буфера Фиксация будет происходить каждый раз при посылке указанного в MICS числа строк.
Кроме того, фиксация будет выполняться и после обработки каждого буфера.

Таблица 4: Максимальный фиксируемый размер вставок и его особенности

Обратите внимание, что единственным способом получения пакетов большего размера, чем размер буфера состоит в том, чтобы изменить размер буфера в свойствах потока данных.

Сжатие данных и массовая загрузка

Когда массовая загрузка данных выполняется в сжатую таблицу или секцию, сжатие уровня страницы и уровня строки обычно происходит прямо во время массовой загрузки.
Однако следует помнить об одном исключении: когда массовая загрузка направлена в кучу со сжатием страниц, нужно использовать подсказку TABLOCK, чтобы получилось именно сжатие страниц. Если подсказка TABLOCK не используется, то для кучи будет выполнено сжатие уровня строк.
Использование сжатия уровня страниц на получателе массовой загрузки приведёт к естественному снижению производительности загрузки, особенно, если система ввода-вывода способна набрать достаточную скорость, позволяющую полностью загрузить процессоры.
На представленной ниже иллюстрации продемонстрирован эффект от сжатия таблицы с 50 миллионом строк, при этом система не испытывает проблем с большим вводом-выводом:


Рисунок 4: BULK INSERT и компрессия данных

Красная линия графика показывает размер таблицы, а синие полосы показывают затраченное время.

Другие минимально протоколируемые операции и операции над метаданными

В дополнение к изложенному материалу стоит упомянуть, что кроме операций вставки, существует ещё ряд операций, которые тоже протоколируются минимально. Если такие операции выполняются вместе с инструкциями вставки данных, во многих случаях загрузка в хранилища данных может полностью осуществляться как минимально протоколируемая операция.
Хотя операции массовой загрузки протоколируются минимально довольно часто, совершенно не обязательно, что так будет происходить всегда. Например, массовая загрузка данных в таблицу, которая уже содержит данные и имеет индексы, не будет протоколироваться минимально даже при том, что будет по-прежнему считаться быстрой массовой загрузкой. В этой статье мы будем рассматривать все типы массовой загрузки, и те, которые протоколируются минимально, и те, которые протоколируются полностью.
В этом разделе мы опишем операции, которые затрагивают только метаданные. Операции, подобные SWITCH и TRUNCATE, могут затронуть большие объёмы данных, хотя выполняют совсем небольшие изменения в файлах данных и журнале транзакций.

DROP TABLE

Операция уничтожения таблицы выполняется только над метаданными. Такое удаление таблицы позволяет очень быстро удалить большое количество строк, без затрат на протоколирование, как в случае инструкции DELETE.

MERGE

Команда MERGE является новой инструкцией Transact-SQL, и тоже позволяет вставлять строки с минимальным протоколированием. Это справедливо, когда вставка посредством MERGE направлена в кучу, в других случаях для минимального протоколирования вставок необходимо активировать флаг трассировки 610.

TRUNCATE TABLE

Точно так же как DROP TABLE, инструкция TRUNCATE TABLE является операцией, выполняемой только над метаданными. Следует помнить, что относительно TRUNCATE TABLE действуют некоторые ограничения. Вот они:

  • Вы не сможете выполнить усечение таблицы, на которую ссылается ограничение внешнего ключа.
  • Вы можете выполнить усечение строк только всей таблицы, но не для одной секции (но есть другие возможности удаления секции, о которых можно узнать из этой главы: "Удаление всех строк из секции или таблицы").

CREATE INDEX, DROP INDEX и REBUILD INDEX

Операции записи, выполняемые при создании, удалении или пересоздании индекса, являются минимально протоколируемыми. Несмотря на то, что в файлы данных записывается много информации, в журнале транзакций этому будет соответствовать совсем мало записей.
В корпоративных редакциях SQL Server, создание индексов распараллеливается. Однако нужно помнить, что параллелизм при создании индексов может стать причиной их фрагментации.

Partition SWITCH (переключение секции)

В SQL Server 2005 появилось секционирование таблиц и переключение секций. Переключение секций физически не перемещает данные; эта операция выполняется только над метаданными, что позволяет загружать параллельно много однотипных потоков данных в несколько промежуточных таблиц. После того как загрузка будет закончена, можно выполнить команду SWITCH для создания одной большой таблицы.
Изменения значений строк в таблице в режиме минимального протоколирования невозможны, как и удаления из хранимого подмножества строк. Однако, как будет показано в главе "Решения для типовых задач массовой загрузки", команда SWITCH может использоваться для эмуляции поведения минимально протоколируемых инструкций.
Создание промежуточной таблицы, которая будет переключена взамен секции главной таблицы, может оказаться довольно сложной задачей. Для того чтобы использовать SWITCH, необходимо, чтобы метаданные и распределение страниц промежуточной таблицы были точно такими же, как и у основной таблицы. Для этого нужно следовать представленному ниже алгоритму, который позволит выполнять манипуляции над метаданными согласовано:

  1. Создание точной копии схемы главной таблицы.

    Наиболее очевидным способом добиться этого результата является использование инструкции CREATE TABLE, которая должна соответствовать исходной схеме таблицы.
    Также можно использовать показанный ниже простой метод, позволяющий быстро создать идентичную по схеме таблицу:

      SELECT TOP(0) *
      INTO <TargetTable>
      FROM <SourceTable>

    Однако при таком подходе новая таблица будет создана в файловой группе по умолчанию. Переместить таблицу в нужную файловую группу можно создав для неё кластеризованный индекс в нужном месте и удалив его.

      CREATE CLUSTERED INDEX IX_temp
      ON <<argetTable> () ON []
      DROP INDEX <TargetTable>.IX_Temp

  2. Соответствие индексов главной и промежуточной таблиц.

    После копирования схемы исходной таблицы, нужно добиться соответствия всех индексов промежуточной и главной таблиц. Согласованными должны быть следующие параметры индексов:

    • Настройки сжатия
    • Привязка к файловой группе
  3. Создание ограничений для промежуточной таблицы.

    После приведения в порядок схемы столбцов и индексов таблиц, нужно создать необходимые ограничения для промежуточной таблицы. Эти ограничения должны соответствовать требованиям функции секционирования промежуточной таблицы. Тут нужно быть очень внимательным, особенно к граничным значениям.
    Для первой секции таблицы (с самыми маленькими значениями), используйте следующий шаблон:

      ALTER TABLE <TargetTable>
      ADD CONSTRAINT CK_<X>
      CHECK (<PartCol> < <LeftValue>)

    Для секций между первой и последней будет следующий формат ограничения:

      ALTER TABLE <argetTable>
      ADD CONSTRAINT CK_<X>
      CHECK (<PartCol> IS NOT NULL
      AND <PartCol> >= <LeftValue>
      AND <PartCol> < <RightValue>)

    Обратите внимание на ограничение NOT NULL. Значения NULL должны всегда попадать в первую секцию, потому что значения NULL не сравнимы друг с другом, нужно явно указывать это ограничение, что позволит исключить попадание в секции неопределённых значений.
    Наконец, для последней секции нужно использовать следующий шаблон.

      ALTER TABLE <TargetTable>
      ADD CONSTRAINT CK_<X>
      CHECK (<PartCol> IS NOT NULL
      AND <PartCol> >= <LeftValue>)

    Все представленные выше шаблоны предполагают, что функция секционирования использует RANGE LEFT.

  4. Исполнение инструкции SWITCH.

    После всех необходимых подготовительных действий, можно исполнить команду SWITCH:

      ALTER TABLE <SourceTable>
      SWITCH PARTITION <X> TO <TargetTable>

    Для того чтобы облегчить понимание всех тонкостей управления секциями, воспользуйтесь бесплатным инструментом, который доступен для скачивания на сайте CodePlex: SQL Server Partition Management

Решения для типовых задач массовой загрузки

В этой главе будут рассмотрены некоторые наиболее типичные сценарии загрузки данных и представлены шаблоны для их реализации. В примерах будет использоваться отвлечённая таблица фактов Sales. Будут рассмотрены следующие сценарии:

В каждом из указанных сценариев основное внимание будет уделено тому, что нужно сделать для повышения скорости выполняемых операций.
В целях иллюстрации, в приводимых ниже примерах будем использовать метод BULK INSERT. Однако, эти инструкции применимы и для других методов массовой загрузки.

Массовая загрузка в пустую, несекционированную таблицу

Поскольку загрузка данных в несекционированную таблицу является довольно простой операцией, оптимизирована она может быть несколькими способами.
Перед тем, как начать загрузку данных в пустую таблицу, считается хорошей практикой отключать у этой таблицы все индексы. После загрузки индексы нужно пересоздать.
Если исходные данные извлекаются из таблицы в SQL Server, и для этого используются SELECT INTO или INSERT…SELECT, загрузка пройдёт быстро и просто. Если исходные данные не хранятся в SQL Server, можно использовать то, что подойдёт лучше: BCP, BULKINSERT, INSERT…SELECT или Integration Services.
Выполнить много параллельных операций вставки в кучу можно в том случае, когда выбран такой сценарий массовой загрузки, который приведёт к выполнению операции c установкой на таблице блокировки массового обновления (BU). BU - блокировки совместимы, и следовательно две массовые операции могут выполняться одновременно.
Если ваша дисковая подсистема достаточно быстра, есть смысл рассмотреть возможность параллельного исполнения нескольких операций BULK INSERT. Одиночная массовая операция будет утилизировать ресурсы только одного процессорного ядра. Как мы увидим и в других сценариях, использование нескольких параллельных потоков массовых операций является ключом к масштабированию массовой загрузки данных.
В этом сценарии будет нецелесообразно использование инструкций INSERT…SELECT и SELECT INTO. Обе они приведут к исключительной (X) блокировке на уровне всей таблицы, куда идёт загрузка. А это означает, что единовременно может выполняться только одна операция массовой загрузки, что является ограничением для масштабируемости. Однако, такие средства, как: BCP, BULK INSERT и Integration Services способны обойтись BU - блокировкой, если указать оптимизатору подсказку TABLOCK.
Исполняется несколько операций массовой загрузки, типа:

BULKINSERT Sales FROM'Sales<X>.csv'
WITH (
FIELDTERMINATOR=';'
,ROWTERMINATOR='\n'
, TABLOCK
)

Выполнение параллельной массовой загрузки в одну таблицу-кучу масштабируется довольно хорошо. Наши тесты производительности, описанные в этой статье: "ETL World Record", показали следующую зависимость:


Рисунок 5: Массовая вставка в кучу (одна таблица) и её кривая масштабируемости

16 параллельных массовых операций масштабируются практически линейно, правда, всё это зависит от размера данных. После этого, проявляется особенность работы в SQL Server распределения внутренних структур данных, что ограничивает производительность вставки. Наличие подобного узкого места сигнализируется сервером в виде появления в sys.dm_os_latch_stats ожидания ALLOC_FREESPACE_CACHE. Обратите внимание, что представленные на графике значения меняются в зависимости от размера вставляемых строк.
Если необходимо добиться линейного масштабирования вышеуказанного ограничения, то более высокой производительности можно добиться с помощью секционирования таблицы. Используя секционирование можно загружать несколько таблиц одновременно и потом с помощью переключения секций объединять данные, как это показано в главе "Массовая загрузка в секционированную таблицу".

Вставка упорядоченных данных

Это особый случай, который нуждается в отдельном рассмотрении. Если у получающей данные таблицы имеется кластеризованный индекс, и исходные данные упорядочены точно так же, как задано ключом кластеризованного индекса, можно задействовать подсказку ORDER и не отключать кластеризованный индекс у таблицы на время массовой загрузки. Использование подсказки ORDER может значительно ускорить массовую загрузку, потому что это приводит к устранению в плане исполнения загрузки промежуточного шага сортировки по ключу кластеризованного индекса. Однако так поступать следует только в том случае, если данные поступают из источника уже упорядоченными; предварительная же сортировка перед вставкой не приведёт к увеличению скорости загрузки.
Если вначале таблица пуста, операция будет протоколироваться минимально. Однако если в ней уже есть данные, и, если не включен флаг трассировки 610, операция будет протоколироваться полностью.
Подсказки ORDER и TABLOCK можно указывать одновременно. Это не позволит одновременно направлять несколько потоков в одну таблицу (накладывается блокировка стабильности схемы Sch-M). Однако это будет наиболее быстрым решением для одного потока, сопоставимое по скорости с однопоточной массовой загрузкой в кучу, и при этом устранятся необходимость в дополнительном после загрузки шаге CREATE INDEX (использующего дорогую операцию сортировки).
Для одного потока, представленный ниже сценарий операции массовой вставки был близок по производительности и при вставке в кучу, и при вставке в кластеризованный индекс с ключом по OrderDate.

    BULK INSERT Sales FROM 'C:\temp\Sales200401'
    WITH (
    FIELDTERMINATOR = ';'
    , ROWTERMINATOR = '\n'
    , TABLOCK
    , ORDER(OrderDate)
    )

Высокая производительность получалась при вставке в кластеризованный индекс и кучу. Обратите внимание, что, если не включить флаг трассировки 610, то такая операция будет минимально протоколирована только в том случае, когда получающая данные таблица будет пуста, и используется параметр BATCHSIZE = 0. Для случая с ненулевым BATCHSIZE, минимально протоколироваться будет только первый пакет (первая фиксация).
Вставка в кластеризованный индекс отсортированных входных данных приводит к снижению фрагментации. Однако сортировка данных может потребовать большой объём памяти. Если нужно снизить фрагментацию и одновременно снизить утилизацию памяти сортировкой, можно воспользоваться следующей уловкой:

  1. Создайте временную таблицу с такой же схемой как у таблицы, получающей данные.
  2. Убедитесь, что у временной таблицы создан такой же кластеризованный индекс, как у получающей таблицы.
  3. Выполните массовую загрузку данных в промежуточную таблицу. Если это окажется полезно для производительности, включите флаг трассировки 610.
    • Данные массовой загрузки окажутся в промежуточной таблице, они уже будут отсортированы, но могут оказаться фрагментированными.
  4. Используйте метод массовой загрузки INSERT…SELECT для перемещения данных из промежуточной таблицы в таблицу, куда изначально нужно было загрузить данные.
    • Поскольку промежуточная таблица уже отсортирована, данные будут вставляться в таблицу назначения в отсортированном порядке.
    • Этот подход обеспечит минимальную фрагментацию вставляемых данных в таблице назначения.

Описанная уловка потребует больше операций ввода-вывода, чем загрузка данных напрямую в таблицу назначения. Однако минимизация фрагментации может повысить скорость чтения для больших рабочих нагрузок при просмотре всей таблицы или диапазонов ключей.
Обратите внимание, что если при массовой загрузке в кластеризованный индекс используются маленькие пакеты, которые потенциально могут поместиться в памяти, SQL Server оценивает необходимый для сортировки объём памяти, предполагая, что в среднем столбцы переменной длины будут заполнены наполовину. Если для какого-либо пакета размер данных в памяти окажется большим, чем эта оценка, сортировка будет сопровождаться сбросом страниц на диск (протечкой), что серьёзно ухудшит производительность. Вы можете обнаружить события протечки сортировки (sort spill) используя SQL Server Profiler, отслеживая класс событий "Sort Warning".
Если, вместо половинной наполненности столбцы varchar имеют тенденцию к полному заполнению, можно помочь SQL Server предотвратить протечку сортировки, используя следующий обходной манёвр:

    При создании временной таблицы, о которой говорилось выше при описании шага 1, увеличьте втрое размер столбцов переменной длины. При этом настройки длины столбцов таблицы постоянной длины изменять не нужно. Это увеличит оценки памяти, распределяемой на шаге сортировки до более чем достаточных значений. Протечек не будет.

Настройка Назначения OLE DB для вставки упорядоченных данных в Integration Services

Несмотря на то, что средствами графического пользовательского интерфейса настройка сортировки входного потока данных делается не совсем очевидным способом, такая возможность для Назначения OLE DB существует и настроить её в Integration Services можно в расширенных свойствах компонента.


Рисунок 6: Настройка сортировки входного потока для получателя OLE DB

Достаточно добавить в свойстве FastLoadOptions подсказку ORDER, как это показано на рисунке выше.

Массовая загрузка в несекционированную таблицу, в которой уже имеются данные

Если таблица, в которую выполняется загрузка, не содержит индексов, нужно руководствоваться советами из предыдущей главы. Напомним, что в уже имеющуюся таблицу не стоит загружать данные методом SELECT INTO.
Когда загрузка выполняется в таблицу, которая уже имеет индексы, это повод для принятия особых мер. Сначала, прочтите статью SQL Server Books Online: "Рекомендации по оптимизации массового импорта данных".
В этой статье представлены рекомендации по отключению индексов перед массовой загрузкой данных в таблицу. Однако стоит помнить, что рекомендации в SQL Server Books Online применимы только для однопоточных операций массовой загрузки. По мере увеличения параллелизма и числа ядер, удаление индексов, одновременная загрузка в нескольких потоках, а затем восстановление индексов может выполниться быстрее, чем вставка данных в существующие индексы. Также, стоит помнить, что массовая загрузка в кластеризованный индекс будет протоколироваться минимально, только если таблица пуста или если включён флаг трассировки 610. Это означает, что протоколирование нескольких параллельных массовых загрузок не будет минимальным.
Стоит протестировать оба сценария: удаление/создание индексов и загрузка данных в таблицу с индексами. Если выбор падёт на параллельную загрузку всей таблицы, руководствуйтесь советами предыдущей главы.

Массовая загрузка при наличии индексов

Массовая загрузка данных в таблицу, которая уже имеет индексы, сильно отличается от массовой загрузки в кучу без индексов.
Если массовая загрузка осуществляется в таблицу с одним или несколькими индексами, нужно учесть представленные ниже особенности организации такой загрузки.

Индексированные таблицы не поддерживают блокировки Bulk Update (BU)

Таблицы с индексами не поддерживают BU-блокировку, вместо неё будет исключительная блокировка (X-блокировка). X-блокировки, в отличие от BU-блокировок, не совместимы с другими блокировками такого же типа. Массовая загрузка в таблицу с индексами потребует наложения X-блокировки уровня строки. В принципе, нет запретов на использование подсказки TABLOCK для выполнения массовой загрузки в индексированную таблицу, но это приведёт к тому, что X-блокировки поднимутся до уровня таблицы, что заблокирует другие потоки массовой загрузки в ту же самую таблицу. Для обеспечения параллелизма массовой загрузки в индексированную таблицу, не стоит использовать подсказку TABLOCK.

Определите оптимальное значение для BATCHSIZE

Эмпирическим путём подберите такое значение BATCHSIZE, при котором производительность массовых операций будет наивысшей. Нулевой размер BATCHSIZE полезен для вставки в кучу без индексов, и неприменим для вставки в таблицы, у которых индексы имеются. Если вставляемые данные предварительно не отсортированы (и указана подсказка ORDER), оптимизатор выполнит операцию сортировки вставляемых данных перед тем, как вставить данные пакета. Если такую сортировку не получается выполнить в памяти, будет выполнена промежуточная материализация сортируемых данных в tempdb, операции ввода-вывода в которой приведут к замедлению скорости вставки. Небольшой размер BATCHSIZE гарантирует, что сортировка будет происходить в памяти, но недостатком такого подхода является расщепление страниц, и фрагментация индексов, если вставляемые данные не отсортированы. Также, от размера BATCHSIZE будет зависеть возможность одновременной загрузки данных множеством запросов к одной и той же таблице.

Избегайте укрупнения блокировок

Укрупнение блокировок до уровня таблицы может начаться, если число X-блокировок уровня строки превысит пороговое значение. Если такое случается, параллелизм деградирует до однопоточной вставки. Методика борьбы с подобным сценарием укрупнения блокировок зависит от версии SQL Server:

  • Для SQL Server 2005, укрупнение блокировок происходит при достижении установки 5000 блокировок. Для борьбы с этим применяют такие способы:

    • Установка настолько маленького BATCHSIZE, чтобы не происходило укрупнение блокировок.
    • Другой способ радикальный, можно использовать флаг трассировки 1211, который полностью отключает укрупнение блокировок для всего сервера.
  • Для SQL Server 2008, также возможно полностью отключить укрупнение блокировок до уровня таблицы.

Использование непересекающихся входных потоков

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

Использование флага трассировки 610

Если включить флаг трассировки 610, вставка в таблицу с индексами может стать минимально протоколируемой операцией. Таблицы с кластеризованными индексами поддерживают одновременную массовую загрузку в несколько потоков вставки, пока эти потоки содержат непересекающиеся данные. Если будет обнаружено пересечение данных, потоки будут блокировать друг друга (но это не будут взаимоблокировки).
Ниже показан пример, где на основе таблицы Sales в несколько шагов проиллюстрировано, как можно добиться максимума производительности при многопоточном исполнении BULK INSERT в таблицу с кластеризованным индексом:

  1. Отключаем для таблицы эскалацию блокировок. ALTER TABLE Sales SET (LOCK_ESCALATION = DISABLE) В SQL Server 2005 можно отключить эскалацию блокировок, присвоив BATCHSIZE значение 5000. Иначе, этого можно было достичь включением флага трассировки 1211 (который полностью отключает эскалацию блокировок на сервере). Для получения более подробной информации о флаге трассировки 1211, см. Lock Escalation in SQL2005.

  2. Запустите несколько команд BULK INSERT с непересекающимися диапазонами.

    Секционирование данных на непересекающиеся диапазоны по столбцу Date:

      BULKINSERT Sales
      FROM'200101.csv'
      WITH (
      FIELDTERMINATOR=';'
      ,ROWTERMINATOR='\n'
      )
      BULKINSERT Sales
      FROM'200102.csv'
      WITH (
      FIELDTERMINATOR=';'
      ,ROWTERMINATOR='\n'
      )

    … и т.д. …

    Не используйте подсказку TABLOCK для отдельных потоков.

    Поэкспериментируйте с разными значениями BATCHSIZE, чтобы понять, для каких значений достигается оптимальная производительность, с учётом особенностей вашей подсистемы ввода-вывода и ресурсов памяти.

  3. Проверка блокировок и параллелизма.
    Вы должны наблюдать несколько незаблокированных потоков массовой загрузки. Каждый из этих потоков должен удерживать монопольную X-блокировку уровня строки. Для проверки можно использовать запрос, подобный показанному ниже:

      SELECT lck.resource_type, lck.request_mode
             , lck.request_status , lck.request_session_id
             , COUNT(*) number_locks
      FROM sys.dm_tran_locks lck
      INNER JOIN sys.partitions par
             ;ON lck.resource_associated_entity_id = par.hobt_id
      INNER JOIN sys.objects obj
             ;ON par.object_id = obj.object_id
      WHERE obj.name = 'Sales'
      GROUP BY lck.resource_type, lck.request_mode
             , lck.request_status, lck.request_session_id

    Он должен возвращать нечто подобное:


    Рисунок 7: Листинг блокировок, удерживаемых операциями массовой загрузки

    Вы можете видеть X-блокировки строк (представленные значением KEY в колонке resource_type) и блокировки с намерением монопольного доступа (IX) на только что распределенных страницах данных.

Массовая загрузка в секционированную таблицу

Массовая загрузка данных в секционированные таблицы предоставляет возможность загрузить данные быстрее всех других способов. Такая загрузка может использовать множество одновременных операций массовой загрузки, которые будут выполняться параллельно и это не добавит существенной дополнительной нагрузки. Переключение секций после загрузки данных будет представлять собой операцию, которая выполняется только над метаданными.
Если массовая загрузка направлена в секционированную таблицу, лучше всего вывести загружаемую секцию из таблицы, перед тем, как начать массовую операцию. Такое выключение секции имеет несколько преимуществ:

  • Появится возможность наложить массовую BU-блокировку на изъятую из таблицы секцию, что позволит в одно и то же время осуществлять массовые операции в несколько потоков.
  • Перед переключением, можно отключить и потом воссоздать индексы для изъятой из таблицы секции, что может поднять скорость массовой загрузки.
  • Массовая загрузка прямиком в секционированную таблицу потребует исполнения операции сортировки, даже если у таблицы нет индексов, или загружаемые данные заранее упорядочены, например, с помощью подсказки ORDERED. Операция сортировки инициируется оптимизатором запросов для устранения издержек, вызванных частым открытием и закрытием новых секций при одновременной большой нагрузке операций вставки данных в таблицу.

После того, как секция изъята, она ведет себя точно так же, как обычная таблица, и можно применять все рекомендации из предыдущих глав. Однако на практике не следует забывать добавлять ограничения, согласующие добавляемые секции (и включить CHECK CONSTRAINTS в опциях загрузки) перед началом операции массовой загрузки. Можно добавить проверочное ограничение и после загрузки, но это потребует исполнения полного просмотра таблицы. Если же не добавить ограничение, не получится переключить секцию обратно в таблицу.
Однако есть особый случай, который нужно учитывать. Если планируется одновременная вставка данных в несколько секций, возможна дальнейшая оптимизация этого процесса. В таком случае, нужно переключить каждую секцию в отдельную таблицу. С каждой из этих таблиц можно параллельно исполнять несколько операций массовой загрузки. Этот метод проиллюстрирован ниже:

  1. Создание временных таблиц для каскадирования

    Создаются временные таблицы, в которые будут переключаться секции, как это было описано выше, в главе "Partition SWITCH (переключение секции)".
    Переключение данных во временные таблицы:

      ALTER TABLE Sales_P
      SWITCH PARTITION<X>
      TO Sales_200<X>

  2. Оптимизация массовой загрузки с разделением на множество независимых таблиц

      BULK INSERT Sales_200
      FROM'Sales200.csv'
      WITH (
      FIELDTERMINATOR=';'
      ,ROWTERMINATOR='\n'
      )

    …и т.д.…

  3. Переключите промежуточных таблиц назад в главную таблицу.

    Возврат промежуточных таблиц назад, в виде секций:

      ALTER TABLE Sales_200<X>
      SWITCH TO Sales_P
      PARTITION <X>

  4. Очистка промежуточных таблиц.

    Удаление временных таблиц, используемых для распараллеливания загрузки::

      DROP TABLE Sales_200<X>

  5. Выбор окончательного сценария загрузки.

    Представленная ниже блок-схема поможет сделать правильный выбор сценария массовой загрузки.


    Рисунок 8: Блок-схема решений для выбора методов массовой загрузки.

Удаление всех строк из секции или таблицы

Этот сценарий, несмотря на его простоту, часто упускают из вида. Самым быстрым способом удаления всех данных из таблицы, будет выполнение инструкции:

    TRUNCATE TABLE Sales

Удаление данных из секции немного сложнее:

  1. Создайте пустую временную таблицу, для помещения туда данных секции.

    Создаём временную таблицу, в которую будем переключать секцию, используя для этого описанный в главе "Partition SWITCH (переключение секции)" метод.

    Если исходная таблица имеет индексы, нужно воссоздать индексы и для временной таблицы.

  2. Выполняем инструкцию SWITCH для той секции, данные которой нужно перенести во временную таблицу.

      ALTERTABLE Sales_P
      SWITCH PARTITION 1
      TO Sales_Temp

    Исходная секция после этого опустеет.

  3. DROP или TRUNCATE для временной таблицы.

    Если временная таблица больше не нужна, удаляем её:

      DROP TABLE Sales_Temp

    Иначе, можно очистить таблицу, после чего её можно будет использовать повторно, например, для удаления других секций:

      TRUNCATE TABLE Sales_Temp

Удаление большого числа строк из секции или таблицы

До сих пор мы рассматривали только способы вставки и удаления данных с минимальным протоколированием и/или с массовой загрузкой.
Бывают ситуации, когда нежелательно использовать инструкцию TRUNCATE для всех секций, но нужно удалить большое число строк из этой таблицы. Инструкция DELETE протоколируется полностью. Однако, существует возможность получить эффект, который похож на DELETE с массовой операцией. Эта возможность представлена ниже, в виде пошагового алгоритма.

  1. Создайте копию основной таблицы.

    Нужно создать временную таблицу, которая будет использоваться для переключения секции. Требования к этой таблице аналогичны тем, которые уже были описаны выше, в главе "Partition SWITCH (переключение секции)".

  2. Массовая загрузка строк в промежуточное хранилище секции.

    Чтобы заработала массовая вставка, можно использовать один из двух методов. Метод INSERT…SELECT:

      INSERT INTO Sales_Temp WITH (TABLOCK)
      SELECT*FROM Sales
      WHERE OrderDate >='20010101'
      AND OrderDate <'20020101'
      AND <Keep Criteria>

    Второй способ заключается в использовании возможностей Integration Services, позволяющих создать несколько параллельных, потоков в таблицу Sales_Temp.

  3. Переключение секции и усечение старых данных

    Если нужна транзакционность операции, запустите транзакцию с помощью:

      BEGIN TRAN

    Чтобы удалить ненужные строки из главной таблицы, используйте методику, описанную в предыдущей главе: "Удаление всех строк из секции или таблицы".

  4. Воссоздание индексов и использование SWITCH.

    Восстановить все индексы и ограничения таблицы Sales_Temp, чтобы они соответствовали тем, что присутствуют в главной таблице. Инструкция по выполнению операций над секциями была представлена выше, в главе "Partition SWITCH (переключение секции)". Она поможет с задачей переключения таблицы Sales_Temp с пустой секцией:

      ALTER TABLE Sales_Temp
      TO Sales
      SWITCH PARTITION 1

  5. Удалите временную таблицу.

    Удалите временную таблицу:

      DROP TABLE Sales_Temp

    Если Вы использовали транзакцию, самое время её завершить:

      COMMIT TRAN

Обновление большой части данных секции или таблицы

До сих пор мы рассматривали способы вставки и, до некоторой степени, удаления данных в режиме минимального протоколирования.
Ни DELETE, ни UPDATE не могут протоколироваться минимально для операций над строками таблицы или секции. Однако, иногда можно создать такие сценарии с инструкциями UPDATE и DELETE, что позволит задействовать массовые операции.
Допустим, нужно удалить и/или изменить большое число строк таблицы или секции. Объём изменений оценивается так, что общее число строк, измененных этой операцией превышает 10 - 20 % от полного объёма таблицы. В этом случае, может быть полезна представленная ниже методика.

  1. Создайте во временной таблице журнал изменений.

    Создайте временную таблицу, которая будет содержать строки, представляющие собой изменённые данные в существующей секции главной таблицы. Подобным образом создавалась таблица, в главе "Partition SWITCH (переключение секции)".
    Выполните массовую загрузку изменяемых строк в таблицу Sales_Delta. Впишите новые значения, которые должны быть после изменений.

  2. Если таблица секционирована, переключите секцию, в которую попадают подлежащие изменению данные.

    Создайте временную таблицу, в которую будут переключены данные изменяемой секции. Для этого можно воспользоваться методом, который был описан выше, в главе "Partition SWITCH (переключение секции)".
    Переключить секцию, данные которой будут изменяться, можно так:

      ALTER TABLE Sales_P
      SWITCH PARTITION 1
      TO Sales_Old

    После этого у вас будут две таблицы: Sales_Delta и Sales_Old.

  3. Если таблица не секционирована, пропустите этот шаг и используйте далее основную таблицу вместо таблицы Sales_New, в качестве одного из источников следующего шага.

  4. Слияние (merge) журнала изменений и изначальной таблицы, и массовая загрузка во временную таблицу.

    Выполнение слияния:

    Создайте временную таблицу, куда будет выполнено переключение секции, как это было описано выше, в главе "Partition SWITCH (переключение секции)".
    Выполните слияние старых записей в Sales_Old и разницы из Sales_Delta. Это легко реализуется с помощью Integration Services, если использовать такие компоненты, как merge или lookup. Иначе, слияние можно выполнить с помощью инструкции Transact-SQL, подобной этой:

      SELECT o.PrimaryKey
        , COALESCE(d.Col1, o.Col1)
        , COALESCE(d.Col2, o.Col3)

    ...и т.д....

      FROM Sales_Old o
      LEFT JOIN Sales_Delta d

    После успешного выполнения этого шага, таблица Sales_New должна быть заполнена и содержать результаты правильного слияния строк, показанных на рисунке промежуточных таблиц.
    Представленный на этом шаге сценарий позволяет добиться высокой степени параллелизма, если одновременно запускать несколько параллельных операций слияния со вставкой в таблицу Sales_New. Каждая операция должна исполняться самостоятельно, и опираться на собственное подмножество данных таблицы Sales_Delta.

  5. Удаление журнала изменений и первоначальных данных.

    Если необходимо выполнить изменение в транзакции, тогда этот шаг нужно начинать с инструкции:

      BEGIN TRAN

    Удалите промежуточную таблицу журнала изменений, созданную на шаге 1:

      DROP TABLE Sales_Delta

    Удалите промежуточную таблицу с оригинальными данными, которая появилась на шаге 2:

      DROP TABLE Sales_Old

    Если таблица не секционирована, выполните усечение Sales вместо удаления таблицы Sales_Old.

  6. Переключение (SWITCH) результатов слияния

    Использовать команду SWITCH, чтобы переместить новую версию данных таблицы взамен прежних данных:

      ALTER TABLE Sales_New
      SWITCH TO Sales_P PARTITION 1

    Удалите таблицу Sales_New, чтобы избавиться от промежуточных результатов:

      DROP TABLE Sales_New

    Если для завершающих шагов использовалась транзакция, её нужно завершить командой:

      COMMIT TRAN

Выполнив последовательно все представленные выше шаги, Вы сможет выполнить большое изменение данных, преимущественно опираясь на операции с минимальным протоколированием.

Массовая загрузка, запросы с NOLOCK и Read Committed Snapshot Isolation

Очень часто, когда нужно читать данные из таблицы, которая потенциально подвержена множественным блокировкам, запрос исполняется с указанием подсказки оптимизатору NOLOCK. Такое чтение известно как "грязное чтение", а сами запросы с NOLOCK выполняются с уровнем изоляции Read Uncommitted и чреваты риском получения неполных или противоречивых результирующих наборов. Однако, поскольку с этой подсказкой не используется совмещаемая блокировка, такие запросы не будут заблокированы операциями записи.
Read committed snapshot isolation (RCSI) появился в SQL Server 2005 в качестве новой возможности чтения данных и наложение на это время блокировок, не мешающих работать другим процессам, в то время, как существуют другие запросы, изменяющие данные в той же таблице. Это хорошая альтернатива NOLOCK, потому что при использовании такого уровня изоляции гарантируется целостное, непротиворечивое с точки зрения транзакций представление данных, для которого не нужно использовать специальные подсказки оптимизатору. Изначально RCSI предназначался для характерных OLTP нагрузок, но этот уровень изоляции может стать мощным подспорьем в нагрузках, свойственных информационным хранилищам или для сценариев, в которых велика доля операций массовой вставки.
RCSI включается для базы данных. При включении этого уровня изоляции, запросы на чтение не накладывают разделяемую блокировку на строки, страницы или таблицу, и поэтому они не блокируются эксклюзивными X-блокировками или BU-блокировками. Вместо блокировок, новые или изменённые в таблице строки получают 17-байтный идентификатор версии, и ещё до того, как образ таких строк будет изменён в рамках транзакции (изменения или удаления) они будут скопированы в системную базу данных tempdb, которая в этом случае исполняет роль хранилища версий строк для всего экземпляра SQL Server. Когда исполняется запрос на чтение, извлекаются только те строки, которые существовали с самого начала исполнения запроса, при этом, игнорируя позднейшие номера версии строк, а для изменений извлекаются ссылки в tempdb для соответствующих предыдущих версий строк.
Транзакционная семантика для RCSI идентична обычному уровню изоляции Read Committed, с той разницей, что запросы на чтение всегда возвращают данные на момент начала запроса, вместо того, чтобы блокироваться до окончания конкурирующих запросов на запись. В результате этого, те приложения, которые требуют, чтобы не было никаких изменений во время выполнения запросов с уровнем изоляции RCSI, сохраняют точно такое же поведение, как и в предыдущих версиях. Транзакции, которые устанавливают уровень изоляции REPEATABLE READ, или когда инструкция SELECT выполняется с HOLDLOCK, будут по-прежнему запрашивать блокировки и потенциально подпадать под блокировки одновременных запросов на запись. Кроме того, не стоит путать RCSI с другим новшеством SQL Server 2005, речь идёт об ещё одном уровне изоляции: Snapshot Isolation (уровень изоляции моментального снимка). Этот уровень изоляции тоже использует версионность строк.
Для тех баз данных, которые подвержены большому числу операций UPDATE или DELETE, уровень изоляции RCSI может породить дополнительную нагрузку и повысить требования к пропускной способности tempdb. Однако, при изоляции RCSI, операции INSERT (включая BULK INSERT), не будут создавать нагрузки на tempdb и не станут приводить к дополнительным затратам ресурсов кроме добавления 17 байтов к каждой строке (обратите внимание на то, что в довершение ко всему эти 17 байт несжимаемы).
Хотя при RCSI чтение не страдает от X-блокировок, существует два случая при массовой загрузке данных, при которых в SQL Server 2008 запросы с уровнем RCSI будут блокироваться (так же как запросы с NOLOCK):

     

  • При загрузке кучи с подсказкой TABLOCK, когда используются операции BULK INSERT, INSERT…SELECT или другие операции массовой загрузки. Это происходит, потому что при загрузке накладывается BULK OPERATION блокировка с намерением монопольного доступа (IX-блокировка), а чтение с NOLOCK или RCSI накладывают разделяемую BULK OPERATION блокировку (S-блокировка). Для массовой загрузки в кучу с параллельными операциями чтения, несмотря на использование RCSI или NOLOCK, нужно убрать подсказку TABLOCK, что приведёт к отказу от возможности минимального протоколирования этих операций.
  • В начале загрузки в пустую таблицу с кластеризованным индексом, с подсказкой TABLOCK, и с использованием операций BULK INSERT, INSERT…SELECT или других операций массовой загрузки. Это происходит, потому что загрузка выполняется с блокировкой стабильности схемы (Sch-M блокировка), а чтение выполняется с блокировкой изменения схемы (Sch-S блокировка). В качестве альтернативы можно включить флаг трассировки 610, что обеспечит минимальное протоколирование и отказ от использования подсказки TABLOCK.

Кроме этих исключений при уровне изоляции RCSI операции BULK INSERT (включая INSERT…SELECT) могут использоваться с подсказкой TABLOCK, или они могут использовать нормальные правила эскалации блокировок, и во всех этих случаях чтение не будет блокироваться. Производительность будет аналогична исполнению запросов с подсказкой NOLOCK, с тем преимуществом, что в отличие от способа с NOLOCK, чтение будет оперировать транзакционно целостным представлением данных таблицы. Кроме того, даже если одновременно будет существовать множество продолжительных запросов на чтение данных таблицы, вставка новых данных с помощью операции INSERT не будет блокироваться чтением. Чтобы включить для базы данных уровень изоляции RCSI, нужно выполнить представленную ниже команду, которая должна выполняться в отсутствие других подключений к изменяемой базе данных.

    ALTER DATABASE SET READ_COMMITTED_SNAPSHOT ON

Определить, включён ли RCSI для существующей базы данных, можно с помощью следующего сценария.

    SELECT name, is_read_committed_snapshot_on
    FROM sys.databases
    WHERE name = '<dbname>'

Перед принятием решения об использовании RCSI для ваших рабочих нагрузок стоит убедиться, что у вас имеется полное понимание того, насколько часто выполняются операций UPDATE и DELETE, и нужно обеспечить такую конфигурацию tempdb, которая позволила бы без проблем справиться с той частью рабочей нагрузки, которая обеспечивает изменения и удаления данных. Также нужно учитывать, что уменьшение блокировок между операциями чтения и записи может привести к увеличению конкурентного ввода-вывода, порождаемого этими запросами, и нужно обеспечить необходимую пропускную способность для возросшего числа запросов ввода-вывода к используемой дисковой подсистеме.

Оптимизация массовой загрузки данных

В этой главе мы углубимся в те методы, которые обычно используются для оптимизации процесса массовой загрузки. SQL Server способен обеспечивать очень высокую производительность массовой загрузки. Но для этого и все остальные ресурсы должны обеспечивать должную поддержку и производительность.
Существует несколько направлений оптимизации массовой загрузки. Прежде всего, это параллелизм, который является ключевым фактором оптимизации для массовой загрузки, и который позволяет линейно масштабировать процесс по числу процессорных ядер.
Во время поиска решений по повышению производительности, статистика ожиданий и счетчики производительности являются основными источниками информации, необходимой для анализа первопричин возможных проблем. Наиболее критичные параметры производительности рассмотрены ниже, в главах: "Отслеживаемые типы ожиданий" и "Отслеживаемые счетчики производительности".
Некоторые моменты настройки производительности и устранения критических проблем требуют более глубокого вмешательства. К таким моментам относится глава "Отслеживаемые счётчики производительности" с углублённым описанием оптимизации сетевого взаимодействия, главы: "Конкуренция не планировщике", "Конкуренция PFS". Последним направлением, но не последним по значимости, мы рассмотрим небольшой набор рекомендаций в главе: "Оптимизация ввода-вывода и размещения файлов".

Параллелизм и секционирование загрузки

Основным фактором оптимизации производительности во время массовых операций является параллелизм. Лучше всего параллелизм достигается выполнением нескольких команд массовой загрузки в одно и то же время. Поскольку каждая массовая операция выполняется в собственном потоке, понадобится выполнить несколько таких операций, чтобы загрузить все процессорные ядра. Каждая команда массовой операции потенциально может утилизировать свой процессор на 100%. Следовательно, оптимальная производительность при загрузке всех ядер на 100% может быть достигнута в том случае, когда число параллельно выполняемых команд массовых операций будет таким же, как общее число процессорных ядер, выделенных в вашей системе для массовых операций.
При проектировании параллельных операций, также нужно учитывать то, какое влияние на процесс оказывает очерёдность исполнения операций, когда одна операция заканчивается перед началом другой. В идеале, получаемые данные должны секционироваться на части равного размера. Это будет гарантией того, что все параллельные задачи завершатся приблизительно в одно и то же время.
На иллюстрации ниже показано, как перекос при распределении размера секций может привести к увеличению времени исполнения.


Рисунок 9: Балансировка размера секций

 

Отслеживаемые типы ожиданий

Когда нужно повысить производительность массовых операций, вначале нужно проанализировать данные из системного административного представления sys.dm_os_wait_stats. Оно поможет определить, блокируются ли какие-либо процессорные ресурсы. Сначала внимание стоит сфокусировать на значениях с наиболее продолжительным временем ожидания и постепенно оптимизировать наиболее "узкие" места. По ссылке Вы найдёте сценарий с сайта TechNet, который может оказаться полезным для этих целей: Retrieve Waitstat Snapshots.
Также, будет полезен сбор данных из sys.dm_os_latch_stats, особенно если планируется реализовать много потоков массовой загрузки в одну таблицу.
Ниже представлена таблица, в которой перечислены некоторые наиболее распространённые при массовой загрузке типы ожиданий, указаны их возможные причины, и те действия, которое необходимо предпринять для исследования и устранения проблемы.

Тип ожидания Типичная причина появления Определение/устранение
LCK_<X> Один процесс блокирует другой Вы используете непересекающиеся потоки загрузки?
Убедитесь в корректности использования TABLOCK?
Найдите основную причину блокировок.
PAGEIOLATCH_<X> Дисковая подсистема слишком слаба Увеличьте число дисков или оптимизируйте ввод-вывод. См. "Оптимизация ввода-вывода и размещения файлов".
IMPROV_IO Данные из текстового файла поступают слишком медленно Оптимизируйте дисковый ввод-вывод, для используемых файлов с загружаемыми данными.
PAGELATCH_UP Конкуренция за страницы PFS Убедитесь, что дисковая подсистема достаточно быстра.
См. "Конкуренция PFS"
Добавьте в параметры запуска флаг -E.
ASYNC_NETWORK_IO Сеть не может обеспечить надлежащую пропускную способность См. в главе "Отслеживаемые счётчики производительности" информацию о настройках сети
WRITELOG Журнал транзакций недостаточно производителен Убедитесь, что используется минимальное протоколирование операций.
Убедитесь, что журнал транзакций находится на быстром диске.
OLEDB Данные на вход поступают слишком медленно Оптимизируйте производительность источника данных, поставляющего на вход информацию.
SOS_SCHEDULER_YIELD Конкуренция за планировщик См. "Конкуренция на планировщике".
ALLOC_FREESPACE_CACHE Конкуренция за распределение памяти в куче (выводятся только в sys.dm_os_latch_stats) Слишком много потоков одновременно вставляет данные в кучу. Рассмотрите возможность секционирования таблицы таким образом, чтобы за счёт увеличения числа куч снизить конкуренцию вставки.
PREEMPTY_COM_<X> Ничего Эти ожидания вполне нормальное и ожидаемое явление. Игнорируйте их.

Таблица 5: Типичные ожидания для массовых операций

Отслеживаемые счетчики производительности

Представленные в таблице счетчики производительности полезны для наблюдения за массовой загрузкой.

Объект производительности Счётчик Пояснения
Логический диск
Logical Disk
Скорость записи на диск (байт/сек)
Disk Write Bytes Bytes / sec
Показывает эффективную скорость записи на диски.
Логический диск
Logical Disk
Скорость чтения с диска (байт/сек)
Disk Read Bytes / sec
Показывает, как быстро данные читаются из источника.
Логический диск
Logical Disk
Средний размер одной записи на диск (байт)
Avg Disk Bytes / Write
Показывает, насколько большой размер блока логического диска. См. "Оптимизация ввода-вывода и размещения файлов".
Процессор
Processor
% загруженности процессора
% Processor time - Total
Можно ожидать 100% утилизации процессоров, которые обслуживают задачи массовой загрузки, если для таких задач нет узких мест.
MSSQL::Databases Массовое копирование строк/с
Bulk copy rows / sec
Показывает число строк, заливающихся в базу данных. Оптимизация заливки приводит к увеличению этих значений.
Сетевой интерфейс
Network Interface
Всего байт/сек
Bytes Total / sec
Показывает пропускную способность сети, которая подключена через сетевые адаптеры сервера.
SQL:Databases Количество байтов журнала, сбрасываемых на диск/с
Log bytes Flushed / Sec
Показывает производительность журнала транзакций.

Если для массовой загрузки данных по сети используется Integration Services или BCP, правильная настройка производительности становится критически важным фактором.
Взаимодействуя со своим отделом локальных и глобальных сетей, выясните инфраструктуру и конфигурацию сети. Стоит также удостовериться в том, что выбранная конфигурация обеспечивает минимально возможное время ожидания. Ниже перечислены несколько параметров, которые для этого можно оптимизировать:

  • Используйте высокопроизводительные сетевые платы и коммутаторы.
  • Установите последние версии сертифицированных драйверов используемых сетевых плат.
  • Включите полный дуплекс.
  • Включите поддержку "Гигантских" фреймов (Jumbo frames).
  • Используйте режим "TCP Chimney Offload".
  • Используйте режим Receive Side Scaling (RSS).

Ниже перечислены ссылки на документы, где можно найти краткий обзор ключевых атрибутов сетевых интерфейсов и того, как они взаимодействуют с операционной системой Windows®:

Оптимизировать производительность сети непосредственно для клиента массовой загрузки можно, изменяя параметр размера сетевого пакета для подключения к SQL Server. В Integration Services размер пакета настраивается в свойствах менеджера подключения, как это проиллюстрировано ниже.


Рисунок 10: Настройка размера сетевого пакета

Если используется BCP, размер пакета позволяет задать ключ -a. Увеличение размера сетевого пакета сокращает число операций чтения и записи, потребляемых сетевым стеком SQL Server. Поскольку подобные изменения в настройках приведут к тому, что каждая операция будет чуть больше утилизировать процессоры, сокращение ввода-вывода может поднять производительность. В таблице, приведенной ниже, представлены несколько способов оптимизации одного потока, которые применялись для достижения мирового рекорда загрузки данных в ETL системе.

Размер сетевого пакета Число сетевых чтений SQL Server Число дисковых записей SQL Server Число дисковых чтений Integration Services Число сетевых записей Integration Services Время исполнения
4096 (по умолча-нию) 465 тыс. 8 тыс. (256 Кб) 14 тыс. (128 Кб) 465 тыс. 2 мин. 56 сек.
32 Кб 58 тыс. 8 тыс. (256 Кб) 14 тыс. (128 Кб) 58 тыс. 2 мин. 36 сек.

Таблица 6: Эффект от размера сетевого пакета

Если для достижения требуемого уровня пропускной способности по сети используется несколько сетевых плат, можно извлечь выгоду из привязки прерываний сетевых интерфейсов Windows к указанным процессорным ядрам. Для получения подробной информации о такой привязке ознакомьтесь со статьёй: Scaling Heavy Network Traffic with Windows.
Используя гигабитную сетевую плату можно достичь пропускной способности приблизительно в 70-100 мегабайт в секунду в зависимости от передаваемых при массовой загрузке данных. Для измерения производительности сети используйте счетчики из раздела Сетевой интерфейс (Network Interface).


Рисунок 11: Пример контроля производительности сети

Конкуренция на планировщике (Scheduler Contention)

Конкуренция на планировщике возникает тогда, когда две параллельные операции массовой загрузки исполняются на одном и том же планировщике SQLOS, за который они конкурируют, т.е. конкурируют за циклы одного и того же процессорного ядра. Обычно такое может случиться на компьютерах, у которых имеется много процессорных ядер. Наличие конкуренции на планировщике можно обнаружить по ожиданию SOS_SCHEDULER_YIELD, которое может появляться даже в тех случаях, когда число процессов загрузки данных не превышает число процессорных ядер.
Одним из способов решения подобной проблемы является программная настройка поддержки экземпляром SQL Server неоднородного доступа к памяти (Soft-NUMA). Каждый программно настроенный узел Soft-NUMA должен содержать по одному процессорному ядру. После этого, каждому Soft-NUMA узлу назначается индивидуальный порт TCP/IP. Это позволит выбирать явно, на какой процессор направляется нагрузка, указав необходимый порт в качестве части строки подключения к SQL Server. Обратите внимание, что изменение параметров настройки Soft-NUMA требует рестарта службы SQL Server. На рисунке ниже проиллюстрирован этот принцип.


Рисунок 12: Soft-NUMA и подключения массовых операций

Для получения дополнительной информации о настройке Soft-NUMA для подобных конфигураций, см. "Как настроить сервер SQL Server на использование программной архитектуры NUMA" и "Как сопоставить порты TCP/IP узлам NUMA".
Когда настроена привязка портов к узлам, в журнале ошибок SQL Server эти порты TCP будут представлены так, как это показано на рисунке ниже.


Рисунок 13: Пример вывода конфигурации Soft-NUMA в журнале SQL Server

Также можно настроить отдельный порт, который будет отображаться на все процессорные ядра, и его можно будет использовать для обычных подключений, которые не задействованы в синхронизированной рабочей нагрузке массовой загрузки данных.
Можно предотвратить конкуренцию на планировщике с помощью других методик, например, с помощью методики, получившей название: "Отключись и подключись заново". Эта методика описана в этой технической статье: "Resolving scheduler contention for concurrent BULK INSERT".

Конкуренция PFS (Page Free Space)

Несмотря на то, что массовая загрузка с минимальным протоколированием сопровождается малыми объёмами записи в журнал транзакций, возможны трудности с распределением новых страниц для целей массовой загрузки. При таких распределениях, SQL Server использует специальный тип страниц - Page Free Space (PFS), с помощью которых отслеживается использование страниц в файле данных.
Поскольку для массовых операций новые страницы должны распределяться очень быстро, обращение к страницам PFS для выделения места под загружаемые данные может стать критически важным шагом. Если такие обращения будут проблематичными, сервер об этом будет сигнализировать ожиданием PAGELATCH_UP. С помощью запроса к sys.dm_os_buffer_descriptors можно удостовериться, что ожидается доступ к страницам PFS. Избавиться от ожиданий PAGELATCH_UP можно путём увеличения числа файлов данных в оперируемой файловой группе. Обратите внимание, что наличие нескольких файлов данных в файловой группе может сделать дисковый ввод-вывод менее эффективным, как это описано в идущей далее главе "Получатель массовой загрузки SQL Server".
Для получения более подробной информации о страницах PFS и о том, как они могут влиять на производительность, обратитесь к техническому документу "How many files should a database have? -Part 1: OLAP workloads" и статье в SQL Server Books Online "Управление размещением экстента и свободным местом".

Оптимизация ввода-вывода и размещения файлов

Когда выполняется параллельная загрузка в несколько потоков, нужно тщательно продумать конфигурацию используемой подсистемы ввода-вывода. Поскольку нужно добиться увеличения скорости чтения и записи, при оптимизации следует фокусироваться на времени ожидания диска.
Процесс массовой загрузки должен одновременно читать данные из источника и выполнять их массовую загрузку средствами ядра СУБД. Это означает, что нагрузка ввода-вывода имеет преимущественно последовательный характер доступа. Существенный выигрыш в производительности могут дать такие приёмы, которые, например, использовались при установлении Мирового Рекорда в ETL, и заключались в усовершенствовании вторичных факторов производительности ввода-вывода массовой загрузки, например, таких как размещение файлов по дискам.
Часто, может оказаться выгодным размещать исходные данные на других дисках, не на тех, на которые идёт загрузка. Изоляции друг от друга нагрузок чтения и записи можно достичь путём разделения двух разных по характеру нагрузки последовательных потоков по разным шпинделям.
Если учитывать особенности операционной системы Windows, то нужно убедиться, что используемые разделы дисков отформатированы с размером кластера NTFS равным 64 Кб, и кластеры правильно выровнены по границам сектора относительно блоков дисковых массивов.
Источник и получатель данных массовой загрузки оптимизируются по-разному, что будет рассмотрено в следующих главах.

Источник данных

Для достижения максимальной производительности массовой загрузки, нужно организовать несколько параллельных потоков чтения из источника данных, по одному и более для каждой операции массовой загрузки.
Если источник секционирован, и чтение выполняется последовательно, можно оптимизировать источник, помещая каждую исходную секцию данных на собственный, выделенный для этой секции набор шпинделей. Однако если вы имеете много исходных секций, большое количество потоков может быстро стать неуправляемым.
В качестве наглядного примера сложности управления большим числом потоков можно привести гипотетический случай с использованием текстовых файлов в качестве исходных данных и выполнение 64 параллельных команд BULK INSERT для этих текстовых файлов.
Давайте рассмотрим две крайности в конфигурации дисковых массивов:

  • Just a Bunch Of Disks (JBOD) - это конфигурация с отдельными дисками (или возможно RAID1 или RAID 10 LUN), которые распределены в сегментированный массив.
  • Stripe And Mirror Everything (SAME) - это конфигурация с распределением дисков в единственный большой LUN.

Just a Bunch Of Disks (JBOD)

С точки зрения производительности ввода-вывода, размещение JBOD обычно самое быстрое. В этом случае, каждый текстовый файл выкладывается на свой диск. Эта конфигурация показана ниже.


Рисунок 14: Размещение дисков для JBOD

Поскольку BULK INSERT будет последовательно читать файл, для диска нагрузка ввода-вывода будет последовательной с очень маленьким временем задержки на операциях дискового поиска. Современный жесткий диск способен легко превысить скорость последовательного чтения в 100 Мб/сек, что более чем достаточно для нужд BULK INSERT. Однако следует учитывать повышение сложности управления подобным размещением файлов. В данном случае, понадобилось бы организовать в операционной системе 32 LUN, и это только для поддержки файлов исходных данных. Каждый из этих LUN должен быть соответствующим образом настроен на SAN или на встроенном SCSI контроллере. Кроме этого, добавится работы по секционированию, управлению файловой системой и резервному копированию, что может существенно усложнить поддержку и обслуживание такого решения. Если операции по обслуживанию и установке не будут выполняться автоматизированной системой, в отсутствии опытного персонала, работа с подобным сложным размещением файлов и большим числом аппаратных элементов повышает риски ошибок и проблем, вызванных человеческим фактором.

Stripe And Mirror Everything (SAME)

Следующий крайний случай наоборот предоставляет очень управляемую конфигурацию. В ней используется один большой LUN, набранный из большого числа дисков в один из возможных уровней RAID, и на этом логическом диске помещаются все файлы данных.
Такой сценарий обычно используется, когда важна простота установки и обслуживания, а также необходимо обеспечить возможность быстрого размещения большого количества файлов с исходными данными на сервере.


Рисунок 15: Размещение дисков для SAME

Однако несколько параллельных последовательных потоков чтения с одних и тех же шпинделей могут породить эффект, который известен под именем "ввод-вывод внахлёст" (I/O weaving). Даже притом, что рабочая нагрузка будет носить последовательный характер (с точки зрения ядра хранилища SQL Server), для диска такая нагрузка будет носить случайный характер. Поскольку ввод-вывод в реальности случайный, возрастает влияние времени поиска диска, что вынуждает вас увеличивать число шпинделей, чтобы обеспечить заданные требования к производительности.
Некоторые системы SAN и контроллеры SCSI способны использовать собственный кэш, чтобы преобразовать случайный ввод-вывод, и собрать несколько потоков в последовательный ввод-вывод. Стоит проконсультироваться у своего поставщика систем хранения, чтобы выбрать верные характеристики дисковой подсистемы для вашего характера ввода-вывода.
Наиболее удачная конфигурация для вашей системы может оказаться где-нибудь посередине между двумя описанными тут стратегиями SAME и JBOD. Вы должны добиться баланса между производительностью JBOD и управляемостью SAME. Также, обратите внимание, что стратегия JBOD опирается на тот факт, что массовая загрузка будет носить преимущественно последовательный характер. Если нагрузка носит смешанный характер, стратегия SAME может обеспечить наилучшую производительность системы в целом.

Получатель массовой загрузки SQL Server

Точно так же, как и в описанном выше случае с источниками данных, таблица, получающая данные массовой загрузки, также может выиграть от оптимизации ввода-вывода. Когда выполняется массовая загрузка в таблицу, характер ввода-вывода операций записи носит последовательный характер. Это можно использовать в своих интересах.
Сначала, разберём, как SQL Server пишет данные в файлы. Рассмотрим файловую группу с двумя файлами.


Рисунок 16: Файловые группы и файлы

Когда SQL Server выполняет массовую загрузку в таблицу, новые экстенты распределяются между файлами файловой группы поочерёдно. В конфигурации по умолчанию, SQL Server распределяет один экстент для файла и переходит к следующему в цепочке файлу. Можно увеличить число распределяемых за раз экстентов в 64 раза, для этого необходимо включить параметр запуска сервера -E.

Файл Штатное распределение
экстента
С указанием в стартовых
параметрах флага -E
Файл 0 Экстент 1 Экстенты 1-64
Файл 1 Экстент 2 Экстенты 65-128
Файл 0 Экстент 3 Экстенты 129-192
Файл 1 Экстент 4 Экстенты 192-256
…и.т.д… …и.т.д… …и.т.д…

Таблица 7: Эффект от использования флага -E

Поскольку каждый экстент занимает 64 Кб, это будет минимальный размер записи в файл во время массовой загрузки. На первый взгляд, можно было бы ожидать последовательный поток блоков по 64 Кб, записываемых на каждый LUN. Однако, реальное положение вещей несколько лучше этого предположения.
SQL Server использует штатную оптимизацию ввода-вывода Windows, которая склеивает (scatter/gather) блоки. Такая оптимизация используется при обслуживании нескольких запросов ввода-вывода, которые могут группироваться, чтобы сформировать один, большой блок запросов. Например, четыре запроса ввода-вывода по 64 Кб могут быть собраны в один запрос на 256 Кб. Поскольку исполнение одного большого запроса ввода-вывода происходит быстрее, чем нескольких маленьких, использование scatter/gather повышает производительность операций записи. При больших нагрузках массовых операций это способствует повышению производительности, и если такие возможности удастся задействовать, то средства мониторинга будут показывать использование запросов ввода-вывода размером до 256 Кб.
Чем больше файлов в файловой группе, тем больше времени понадобится SQL Server чтобы пробежаться по всем файлам и "возвратиться" к первому файлу для записи. Из-за этого, операция scatter/gather может решить сбросить на диск буфер ввода-вывода ещё до того, как SQL Server заполнит этот буфер до его лимита в 256 Кб. Наблюдая за счетчиком производительности "Средний размер одной записи на диск (байт)" (Logical Disk / Avg. Disk Bytes / Write), можно определить, как хорошо scatter/gather заполняет буфер записи.
Когда в файловой группе немного файлов, вероятность того, что scatter/gather соберёт большие запросы ввода-вывода, повышается. Запуск SQL Server с флагом -E также будет способствовать укрупнению размеров блока записи.
Для достижения мирового рекорда ETL, число файлов в файловой группе было небольшим, и выбиралось из соображений получения в результате работы scatter/gather и -E практически равных 256 Кб блоков ввода-вывода.


Рисунок 17: Измерение размеров блока

Однако не следует забывать, что слишком маленькое число файлов в файловой группе может привести к конкуренции PFS, которая была описана выше.

Оптимизация раскладки файлов по логическим дискам

Во время массовой загрузки, каждый файл базы данных получает последовательный поток запросов ввода-вывода на запись, с размерами блока между 64 Кб и 256 Кб.
Как было показано в главе "Источник данных", оптимизация подсистемы ввода-вывода для получения максимально последовательной рабочей нагрузки может дать существенный выигрыш в производительности массовой загрузки. Тут мы снова должны рассмотреть уже описанные выше две крайности: JBOD против SAME. Что лучше: иметь один большой LUN или несколько, логических дисков меньшего размера?
Как правило, если положить один файл базы данных на один LUN, это обеспечивает самую высокую производительность операций ввода-вывода. Каждый такой LUN может быть сформирован из нескольких дисков, собранных в RAID. В отличие от оптимизации чтения, где каждый файл с данными для массовой загрузки нуждается в собственном LUN, есть смысл положиться на SQL Server, который направит поток данных массовой загрузки в несколько получающих данные таблиц, преобразовав его в последовательный вид для записи в один и тот же файл. Поскольку SQL Server во время массовой загрузки, пишет в файл последовательно, нет необходимости закреплять по одному LUN на каждый поток массовой операции и оптимизировать этим её производительность. Это означает, что можно создать небольшое число больших LUN на специализированных для этого шпинделях. Каждый LUN должен обслуживать один файл базы данных - то есть нужно обеспечить соотношение LUN к файлам базы данных близкое 1:1. Конечно, если SAN или кэш контроллера могут сгладить негативные последствия пересечения ввода-вывода, можно создавать более одного файла на одном LUN. До развертывания системы стоит проверить, сколько последовательных потоков записи будет обращено к одному и тому же LUN в используемой для этих целей подсистеме ввода-вывода.
Представленные выше соображения являются аргументами в пользу конфигурации SAME - один большой LUN для каждой файловой группы базы данных. Однако, как было упомянуто ранее в главе о конкуренции PFS, слишком маленькое число файлов (а значит и число LUN, потому что нужно сохранить соотношение 1:1) может вызвать проблемы с распределением страниц PFS. Поэтому, для файловой группы можете потребоваться использовать больше одного файла на каждом LUN. Например, в упомянутом уже проекте по мировому рекорду ETL, мы использовали по 4 файла базы данных на одном LUN компьютера, куда была направлена загрузка.

Описание проекта: Мировой Рекорд ETL

В рамках проекта по Мировому Рекорду ETL, 56 задач BULK INSERT исполнялись параллельно со средней скоростью 600 Мб в секунду и с пиками до 850 Мб в секунду.
Сервер базы данных SQL Server 2008:

  • Unisys ES7000/one Enterprise Server with 32 Dual core 3Ghz Intel 7140M CPUs
    • Всего 64 ядра
  • 8 x 4Gbit Emulex HBAs
  • 8 x 1Gbit Intel Pro /1000MT Network cards
  • 256 GB RAM (для операций BULK INSERT использовалось только 30Гб)

Оптимальное размещение файла SQL Server в этих условиях было таким:

  • Файлы данных SQL Server были расположены на EMC Clariion CX3-80 SAN с общим числом шпинделей 165.
    • Были "нарезаны" 16 Мета-LUN. Каждый Мета-LUN был собран из 8 шпинделей как показано на рисунке 19


      Рисунок 18: Мета-LUN
  • 133Гб/4Гбит шпиндели с числом оборотов в минуту 15К.
  • 56 файлов данных, по 4 файла данных на каждом LUN.
    • Журнал SQL Server: single4x (1+1R1) Мета-LUN

Размещение файлов для чтения:

  • 2 x EMC Clariion CX600 SAN
    • 10 LUN в RAID5, по 5 на каждом CX600
    • 7 x 36Гб шпинделей с числом оборотов в минуту 15К на один LUN
    • На каждом LUN лежали 5 или 6 текстовых файлов.

Для получения более подробной информации, см. следующие материалы:

Дополнительные ссылки на статьи о вводе-выводе

Для получения более подробной информации о вводе-выводе, изучите эти материалы:

Заключение

Если время исполнения задачи пакетной загрузки данных выходит за отведённые рамки, или если нужно достичь максимальной производительности загрузки большого объёма данных, эта статья предоставляет руководство по налаживанию мониторинга и оптимизации производительности подобных задач.
В статье представлена информация о том, как выбрать метод массовой загрузки, чтобы этот метод лучше всего подходил для вашей задачи. Массовая загрузка лежит в основе некоторых распространённых задач. Описанные здесь решения помогают выбрать правильный сценарий, и помочь вам подступиться к решению и поддержке задачи массовой загрузки данных.
Для рабочих нагрузок, которые характеризуются высокой нагрузкой, исключение "узких мест" является ключом для обеспечения наилучшей производительности. В главе "Оптимизация массовой загрузки данных" были представлены рекомендации по оптимизации конфигурации SQL Server для повышения производительности операций массовой загрузки. Там же можно найти соответствующие ссылки на дополнительные материалы по этой теме.
Совместное использование SQL Server и Integration Services позволяет организовать высокопроизводительную загрузку данных. Во время работ по проекту Мирового Рекорда ETL использовались руководства из настоящей статьи. После выполнения описанных здесь мер оптимизации, были втрое превышены показатели "коробочного" варианта установки. Кроме того, была достигнута близкая к линейной масштабируемости в пределах от 1 до 64 параллельных операций массовой загрузки на 64-х ядерной системе Unisys ES7000/one, которой управляла операционная система Windows Server ® 2008 Datacenter Edition.
Для получения более подробной информации:

Эта статья помогла Вам? Пожалуйста, сообщите нам о ваших впечатлениях. Оцените по пятибалльной системе качество этой статьи, и поясните причины, побудившие поставить выбранную вами оценку? Например, причины могут быть такие:

    Вы поставили отличную оценку из-за наличия хороших примеров, качественных снимков экрана, понятного изложения, или у вас были иные причины?
    Ваша оценка оказалась низкой из-за плохих примеров, неразборчивых снимков с экрана, или путаного изложения материала?

Обратная связь с читателем поможет нам повысить качество технических статей, которые мы предлагаем для вашего ознакомления.

Обратная связь

Posted by gladchenko
Filed under:

Пример настройки Soft-NUMA

Сегодня получили широкое распространение многоядерные системы. Персональные компьютеры с четырьмя ядрами уже не редкость. Т.о. счастливые обладатели подобных многоядерных систем могут на практическом примере апробировать Soft-NUMA и как можно привязать к Soft-NUMA узлу порт сетевого протокола TCP/IP.
Для этого будем использовать компьютер х86 с четырьмя логическими процессорами. Процессоры в системе нумеруются с нуля до трёх. Воспользовавшись программой SQL Server Management Studio, в свойствах сервера, на закладке Processors, отключается привязка к первому процессору (CPU0) для обоих типов привязки: Affinity и I/O Affinity. Это делается для того, чтобы оставить один процессор операционной системе для привязки к нему других приложений (неравён час, прожорливость SQL Server помешает вам насладиться представленным тут примером). Как вы наверняка знаете, другие процессы можно привязывать к процессорам с помощью Диспетчера Задач Windows. Таким образом, можно снизить влияние на тестируемые процессоры нагрузки от активного во время тестирования программного окружения. Для двух следующих процессоров (CPU1 и CPU2) отменим I/O Affinity, оставив для них только привязку Affinity. Это необходимо, чтобы не совмещать обслуживание процессорами системных дисковых операций с обслуживанием сетевых запросов. И последний процессор (CPU3) мы наоборот замаскируем для Affinity и оставим ему привязку только для I/O Affinity. Этому процессору будет отведена роль обслуживания системного процесса отложенной записи. Нет особых причин, которые меня побудили включить эту настройку в систему, просто хотелось, чтобы вы пощупали и этот параметр глобальной конфигурации сервера.

    Внимание! Этот пример не является примером хорошей практике или какой-либо рекомендацией к подражанию в промышленных системах. Целью этого примера является только очень упрощённая демонстрация возможностей Soft-NUMA, которую можно воспроизвести в домашних условиях.

Привязку процессоров и отложенной записи можно выполнить и с помощью системной хранимой процедуры sp_configure, как это показано ниже:

    USE master;
    GO
    EXEC sp_configure 'show advanced option', '1';
    RECONFIGURE;
    EXEC sp_configure 'affinity mask', 6;
    EXEC sp_configure 'affinity I/O mask', 8;
    RECONFIGURE WITH OVERRIDE;
    GO
    EXEC sp_configure 'show advanced option', '0';
    RECONFIGURE;
    GO

После этого, поскольку было выполнено изменение параметра глобальной конфигурации affinity I/O mask, чтобы все изменения вступили в силу, потребуется перезапуск службы SQL Server.
В данном тестовом примере создаётся два программных узла, каждый должен содержать по одному логическому процессору, первый - CPU1, а второй - CPU2. Каждому из программных узлов будет присвоен свой порт TCP, первому - 20001, а второму - 20002. Для чистоты эксперимента, отключим все используемые SQL Server протоколы, кроме TCP/IP. Это можно сделать, воспользовавшись поставляемой в дистрибутиве утилитой SQL Server Configuration Manager, в разделе SQL Server 200х Network Configuration, если перейти на узел "Protocols for MSSQLSERVER". В правой части появившегося окна будет представлен список выбранных для SQL Server протоколов, и по правой кнопке для них доступна команда отключения или подключения каждого из протоколов.

    ВНИМАНИЕ! Для того чтобы привязать порты к процессорам, необходимо вручную выполнить изменения системного реестра операционной системы. Вам необходимо помнить, что ошибочные действия с реестром могут привести к возникновению проблем в работе сервера или его компонент. Прежде, чем приступать к изменениям в реестре, удостоверьтесь, что Вы имеете актуальную копию состояния системы. Для обеспечения возможности быстрого отката неверных или неудачных изменений, сохраните изменяемые ключи или целые ветви реестра в экспортируемых файлах. Все операции с системным реестром Вы делает на свой страх и риск, и не рекомендуются вносить изменения вручную, особенно на промышленных системах без глубокого предварительного тестирования. Кроме того, следует учитывать, что системы x86 и x64 используют разные местоположения веток реестра и пути размещения файлов приложений и служб.

Первым шагом для достижения этой цели является создание Soft-NUMA узлов. Для этого, нужно в системном реестре добавить следующую ветку ключей:

    Windows Registry Editor Version 5.00
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\90\NodeConfiguration]
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\90\NodeConfiguration\Node0]
    "CPUMask"=dword:00000002
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\90\NodeConfiguration\Node1]
    "CPUMask"=dword:00000004

Обратите внимание, что потребуется создать новый подраздел системного реестра NodeConfiguration, в котором описываются программные узлы. Node0 - это подраздел первого узла, в котором указывается ключ CPUMask с шестнадцатеричным значением, соответствующим маске физических процессоров, задействованных для этого узла. В нашем примере заданное значение dword:00000002 соответствует выбору второго физического процессора - CPU1. Подраздел Node1 содержит маску процессоров для второго программного узла. Эта маска соответствует выбору третьего процессора - CPU2.
Таким образом, мы определили для SQL Server два программных узла, и в каждый входит по одному процессору. Задаваемый маской программного узла набор объединяемых процессоров может включать несколько процессоров в произвольном, определяемом маской порядке (с учётом изложенных выше ограничений). Если бы нам потребовалось включить в состав первого программного узла ещё и первый физический процессор, тогда маска должна была бы получить значение dword:00000003, и нам бы ещё потребовалось добавить привязку Affinity для CPU0, от которой мы отказались на подготовительном шаге нашего примера.
Планирование обслуживания сетевой нагрузки процессорами в рамках такого программного узла осуществляется циклическим выбором входящих в программный узел процессоров.
Следующим шагом, является назначение портов программным узлам. Для этого, необходимо откорректировать значение параметров TCP Port, доступные на закладке "IP Addresses" свойств протокола TCP/IP, в узле "Protocols for MSSQLSERVER" утилиты "SQL Server Configuration Manager". В нашем примере, необходимые изменения вносятся непосредственно в системный реестр, путём редактирования тех ключей, в которых SQL Server хранит параметры сетевых протоколов. Ниже представлено содержимое REG - файла, в котором установлены все необходимые для примера параметры протокола TCP/IP.

    Windows Registry Editor Version 5.00
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL.1\MSSQLServer\SuperSocketNetLib\Tcp]
    "Enabled"=dword:00000001
    "ListenOnAllIPs"=dword:00000001
    "NoDelay"=dword:00000000
    "KeepAlive"=dword:00007530
    "DisplayName"="TCP/IP"
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL.1\MSSQLServer\SuperSocketNetLib\Tcp\IPAll]
    "TcpPort"="20001[0x1], 20002[0x2]"
    "TcpDynamicPorts"=""
    "DisplayName"="Any IP Address"

Здесь представлена ветвь системного реестра для подраздела TCP сетевой библиотеки. Как видно, протокол TCP разрешён, и прослушивание будет вестись по всем IP адресам. Во вложенном подразделе IPAll заданы значения портов, используемых экземпляром SQL Server. Эти порты прописаны в строковом ключе TcpPort.
Вы, наверное, уже обратили внимание, что справа от каждого из номеров портов в квадратных скобках указана маска, которая соответствует номеру (начинающиеся не с нуля, а с единицы) Soft-NUMA узла, определённого нами на предыдущем шаге. Маску можно указывать в десятичном или шестнадцатеричном формате, в нашем примере, формат шестнадцатеричный. Таким образом, поступающие на порт 20001 сетевые запросы пользователей будут исполняться на первом процессорном узле (у нас это процессор CPU1), а запросы к порту 20002 будут обслуживаться на втором программном узле (CPU2).
Чтобы наши изменения в системном реестре были подхвачены сервером, необходимо перезапустить службу SQL Server.
Дополнительную информацию о привязке портов сетевого интерфейса к узлам Soft-NUMA можно получить в электронной документации Microsoft SQL Server Books Online: "Как сопоставить порты TCP/IP порт с узлами NUMA".
Для того чтобы убедиться в том, что всё так и будет происходить, несложно создать сравнительно большую рабочую нагрузку на оба порта. В этом примере, мы будем исполнять на каждом из портов довольно простую, но ресурсоёмкую команду по переиндексации всех таблиц тестовой базы данных AdventureWorks, которую можно взять по этой ссылке: http://msftdbprodsamples.codeplex.com. Ниже показан текст запроса, содержащего эту команду.

    USE AdventureWorks;
    GO
    EXEC sp_MSforeachtable "DBCC DBREINDEX ('?')";
    GO

Для того чтобы направить этот запрос на заданный порт, создаём два псевдонима нашего SQL Server, которые будут отличаться только портами TCP. Псевдонимы также можно создавать с помощью утилиты SQL Server Configuration Manager, перейдя к разделу псевдонимов в узле SQL Native Client Configuration. Поскольку псевдонимы также хранятся в системном реестре, давайте посмотрим, как они там могут выглядеть:

    Windows Registry Editor Version 5.00
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSSQLServer\Client\ConnectTo]
    "Node1"="DBMSSOCN,SQLRU,20001"
    "Node2"="DBMSSOCN,SQLRU,20002"

Из представленных выше ключей реестра видно, что созданы два псевдонима, каждый из которых подключается по имени к серверу SQLRU, но для каждого из них для протокола TCP задан свой порт. Для псевдонима Node1 задан порт TCP 20001. А для псевдонима Node2 задан порт TCP 20002.
Теперь запустим программу SQL Server Management Studio, зарегистрируем там оба наших серверных псевдонима и, создав новое окно T-SQL запроса с подключением к псевдониму сервера Node1, введём в нём сценарий представленной выше команды. Чтобы наблюдать утилизацию процессоров во время исполнения запроса, в нашем случае удобно воспользоваться системной программой из комплекта поставки операционной системы - Диспетчер Задач.
Запустив исполнение запроса на переиндексацию всех таблиц тестовой базы данных AdventureWorks, и обращаясь к серверу через псевдоним Node1, автор наблюдал следующую картину:


Рис. 1. Подача нагрузки на первый программный узел

Как видно на Рисунке 1, основная доля рабочей нагрузки попала на второй процессор, чего мы и добивались. Теперь, давайте запустим эту же рабочую нагрузку через псевдоним Node2. В этом случае, закладка Быстродействие в оснастке Диспетчер Задач во время исполнения запроса приобретёт вид, который представлен на следующем рисунке:


Рис. 2. Подача нагрузки на второй программный узел

Из рисунка 2 видно, что рабочая нагрузка переместилась на третий процессор как мы и планировали. В итоге, мы получили такую схему привязки, что в зависимости от избираемого клиентским подключением порта, создаваемая им рабочая нагрузка может посылаться на один из двух процессоров, доступных в системе для клиентских подключений.
В случае NUMA, привязка портов сетевых интерфейсов осуществляется так же, как и для SMP серверов, только может понадобиться добавить ключи реестра, определяющие протоколы сетевых интерфейсов для нескольких сетевых адаптеров. В нашем примере, в варианте использования одного адаптера, понадобились бы следующие ключи системного реестра Windows:

    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL.1\MSSQLServer\SuperSocketNetLib\Tcp\IP1]
    "Enabled"=dword:00000001
    "Active"=dword:00000001
    "TcpPort"="20001[0x1]"
    "TcpDynamicPorts"=""
    "DisplayName"="Specific IP Address"
    "IpAddress"="127.0.0.1"

    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL.1\MSSQLServer\SuperSocketNetLib\Tcp\IP2]
    "Enabled"=dword:00000001
    "Active"=dword:00000001
    "TcpPort"="20002[0x2]"
    "TcpDynamicPorts"=""
    "DisplayName"="Specific IP Address"
    "IpAddress"="127.0.0.1"

На представленных выше рисунках 1 и 2, ваш интерес, возможно, привлекла некоторая активность, которая наблюдается в виде загрузки узла, на который по идее не должна была поступать никакая нагрузка. Давайте попробуем разобраться в её природе.
Первое предположение, которое приходит в голову, что процесс повторного создания индексов может быть распараллелен и нагрузка распределяется на нескольких процессоров. Другая гипотеза, это выполнение ресурсоёмких сортировок с промежуточной материализацией в tempdb, которая выполняется в другом потоке.
Системная процедура sp_MSforeachtable пересоздаёт индексы таблиц базы данных последовательно и трудно предугадать, какие из этих индексов будут пересоздаваться с использованием распараллеливания или потребуют промежуточной материализации. Поскольку мы наблюдаем активность на программном узле, который не нагружался запросом, можно предположить, что с помощью программных узлов можно указать только те группы процессоров, которые должны быть нагружены в первую очередь. Получается, что если для сервера будет существовать возможность создания такого числа потоков, которое превышает число процессоров выбранного для запроса программного узла, то нагрузка будет размещена и на процессоры, которые не входят в число процессоров выбранного программного узла. В общем случае, вывод такой, что наш тестовый сценарий не очень "чисто" демонстрирует возможности Soft-NUMA.
Из документации следует, что механизмы планирования задач операционной системы и SQL Server устроены таким образом, что планирование потоков не привязывается жёстко к схеме процессорных узлов. Если в системе есть свободные процессоры, и не наложено никаких ограничений на число потоков, обслуживающих запрос, то нет препятствий задействовать под подаваемую на программный или NUMA-узел нагрузку столько процессоров, сколько доступно для экземпляра SQL Server.
Имея в виду вышесказанное, давайте немного изменим условия нашего предыдущего примера, преследуя цель более наглядно и приближённо к реальным задачам продемонстрировать работу Soft-NUMA. Мы будем посылать определённым для примера псевдонимам сервера такую нагрузку, которая больше похожа на реальную работу пользователей. Как вы помните, используемые в примерах псевдонимы отличаются только прослушиваемыми сервером баз данных портами, которые привязаны к заданным программным узлам. Нам потребуется нагрузка, которая лучше поддаётся распараллеливанию, чем использованная нами ранее переиндексация таблиц.
В следующей демонстрации, что бы получить распараллеливаемую нагрузку, на тестовом компьютере был развернут стандартный набор инструментов и шаблонов эталонного теста TPC-H. Для простоты, размер исходной информации базы данных был выбран в один гигабайт. Спецификацию и подробное описание теста TPC-H, базы данных и запросов можно найти на сайте tpc.org. Была создана и проверена тестовая база и необходимый набор запросов. Наиболее удобным для демонстрации представляется запрос Q13, исполнение которого не занимало на сервере много времени, хорошо утилизировало процессоры и требовало распараллеливания задач. Все большие таблицы и индексы базы данных были размещены в отдельных файловых группах, каждая из которых состояла из нескольких файлов.
Поскольку, в нашем примере для каждого программного узла выделялось по одному процессору, то чтобы не было перехлёста рабочей нагрузки на другие узлы, в глобальных параметрах конфигурации SQL Server были выполнены следующие изменения:

    USE master;
    GO
    EXEC sp_configure 'show advanced option', '1';
    RECONFIGURE;
    EXEC sp_configure 'max degree of parallelism', '1';
    RECONFIGURE WITH OVERRIDE;
    GO
    EXEC sp_configure 'show advanced option', '0';
    RECONFIGURE;
    GO<

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


Рис. 3. Подача нагрузки на первый программный узел

Как видно из рисунка 3, уже не наблюдается активности на других процессорах, как это было в варианте запроса на переиндексацию таблиц. Используя абстракцию процессорного узла и ограничение MAXDOP = 1, мы смогли локализовать нагрузку только на заданном по условиям теста процессоре. В реальных условиях уровень максимальный уровень параллелизма, который исходит из требований бизнеса, может стать одним из определяющих факторов выбора числа ядер в одном узле.
Если запрос Q13 направить посредством соответствующего псевдонима на второй программный узел, мы увидим ожидаемую в этом случае картину, которая представлена на рисунке 4.


Рис. 4. Подача нагрузки на второй программный узел

Если же немного изменить условия теста и установить MAXDOP = 2, то при отправке запроса на порт TCP/IP:20001, мы увидим, что нагрузка распараллелится на всех процессорах, выделенных для используемого в тесте экземпляра SQL Server. Т.к. всего у нас два логических процессора, загружены будут оба Soft-NUMA узла, как это показано на рисунке 5.


Рис. 5. Влияние MAXDOP = 2

Правила, по которым нужно настраивать параметры глобальной конфигурации для SMP сервера с программными узлами фактически такие же, как для неадаптированных к NUMA приложений. Если целью является исключение передачи части рабочей нагрузки одного Soft-NUMA узла на другие узлы, то SQL Server должен иметь такую конфигурацию, которая не позволяла бы создавать больше потоков, чем количество процессоров, которое определено для одного программного узла.
Одним из полезных свойств описанного в последнем примере способа управления планированием нагрузки является то, что можно разнести на разные процессоры одного экземпляра SQL Server запросы от разных клиентов в сети. Это может дать заметный выигрыш в производительности экземпляра, если из-за негативного влияния нагрузок клиентов друг на друга нежелательно обслуживать их на одних и тех же ресурсах. Негативное влияние может проявляться в виде очень долгого использования процессора одним из потоков или конкуренцией исполняемых на одном узле потоков за локальные ресурсы узла. SQL Server предоставляет в распоряжение администратора баз данных средства управления планированием потоков. С помощью этих средств администратор может существенно снизить подобное негативное влияние потоков друг на друга.
Кроме того, продемонстрированные в последних тестах результаты позволяют рекомендовать подобное управление планированием потоков для приложений, код которых недоступен для модификации собственными силами. Этот подход применим, если требуется балансировка порождаемой приложениями нагрузки в рамках одного экземпляра SQL Server, а возможности реализовать это на стороне клиента нет. Все изменения, которые потребуется внести администратору - это определить в системном реестре Soft-NUMA узлы, привязать к ним порты сетевых интерфейсов, создать необходимые псевдонимы, а потом прописать соответствующие строки подключения в конфигурации приложения. В итоге, используя новые возможности планирования потоков, можно выделить разные группы процессоров для клиентов, которые посылают серверу "тяжёлые" аналитические запросы, и для тех клиентов, которые посылают серверу короткие транзакции или выборки. Выполнив необходимые для этого настройки, можно практически свести к минимуму возможное негативное влияние таких разнотипных запросов к одной и той же базе данных. Дополнительные возможности регулирования нагрузки может дать комбинирование средств и возможностей Soft-NUMA с возможностями регулятора ресурсов SQL Server 2008.
Сама возможность балансировки нагрузки между процессорами одного экземпляра SQL Server позволяет экономить лицензии и не приобретать дополнительные сервера только для того, чтобы на одном сервере балансировать нагрузку между процессорами, т.е. фактически выделяя и закрепляя ресурсы за приложениями или группами приложений. В предыдущих версиях SQL Server это достигалось только за счёт установки дополнительных именованных экземпляров сервера баз данных, а потом, процессоры распределялись между установленными экземплярами (что можно было делать динамически или можно было задать в глобальной конфигурации экземпляров жёсткую привязку процессоров экземплярам).
Другие сценарии привязки портов к процессорным узлам можно найти в электронной документации Microsoft SQL Server Books Online: "Сценарии NUMA".

Posted by gladchenko | 1 comment(s)
Filed under:

Обновите свои новостные каналы лентами из SQL Server BLOGROLL 2010

Продолжаю традицию в начале года верстать очередной BlogROLL. За основу этого списка новостных лент берётся мой актуализированный OPML, т.е. счастливым обладателям IE7 и выше достаточно просто импортировать его каналы. Как это сделать уже было подробно и наглядно описано в моём блоге. Тех же, кто предпочитает список блогов, я пригашаю перейти по этой ссылке на страницу, где я сгруппировал блоги по нескольким тематическим направлениям: SQL Server BLOGROLL 2010

Вашему вниманию предлагается актуализированный список блогов (блоги, которые не пополнялись больше года, из него исключены). Кроме того, в списке появилось очень много совершенно новых авторов, которые публикуют исключительно интересные материалы и исследования, так либо иначе связанные с SQL Server. Прошлогодняя лента блогов также доступна, напоминаю ссылку на её страницу: SQL Server BLOGROLL 2009

SQL Server BLOGROLL 2010

Analysis Services/BI AdamJorgensen: Most Recent blogs An Uncommon Approach: Business Intelligence Andrew Calvett Andrew Fryer's Blog Arcane Code » SQL Server BIpartisan biresort.net Blog Colin White Boyan Penev on Microsoft BI Business Intelligence...

Высокая доступность в репликации SQL Server 2008 с зеркалированием и доставкой журналов

Под общей редакцией А. Гладченко и В. Щербинина.

Москва. ЭКОМ Паблишерз, 2008Г. 288 страниц. ISBN: 978-5-9790-0086-2. Книга уже в продаже.

Эта книга - сборник статей, которые посвящены ключевым для понимания репликации SQL Server моментам. Кроме переводов наиболее интересных зарубежных авторов, являющимисяхся признанными во всём мире специалистами, в книге вы найдёте ряд статей участников сообщества SQL.RU, которые многие годы помогаю решать разнообразные проблемы на технических форумах этого замечательного интернет - ресурса. Если во время прочтения книги у вас возникнут сомнения или вопросы по поводу изложенных тут материалов, смело обращайтесь к авторам статей на форумах SQL.RU, они с удовольствием вам помогут.
В сборник включены переводы и статьи следующих участников сообщества SQL.RU: Дмитрий Артёмов, Александр Волок, Александр Гладченко, Ильдар Даутов, Григорий Кoрнилов, Алексей Ковалёв, Наталья Кривонос, Ян Либерман, Ирина Наумова и Владислав Щербинин. Причём, на момент написания книги, Александр Гладченко, Ян Либерман и Ирина Наумова являлись SQL Server MVP.
Книга в первую очередь ориентирована на администраторов баз данных, которые собираются углубить свои познания в репликации SQL Server. Назначение этой книги состоит в том, чтобы предоставить читателям набор апробированных в течение нескольких лет рецептов по использованию и настройке репликации в SQL Server.

ЗАКАЗАТЬ

Обсудить книгу можно тут: http://www.sql.ru/forum/actualthread.aspx?tid=643700

Пример статьи из сборника:

Высокая доступность в репликации SQL Server 2008 с зеркалированием и доставкой журналов

Автор Александр Юрьевич Гладченко

Статья написана по мотивам технического документа Майкрософт: "SQL Server Replication: Providing High Availability using Database Mirroring" и описания в электронной документации SQL Server 2008 Books Online (далее BOL): "Репликация и зеркальное отображение базы данных".
В этой статье мы рассмотрим новые возможности обеспечения высокой доступности тиражируемых данных, используя для этого Репликацию транзакций, Доставку журналов и зеркальные копии баз данных.
Основанная на Репликации транзакций распределённая система хранения данных может обеспечить высокую устойчивость к отказам серверов баз данных. Подобные решения позволяют достичь высокой степени доступности, за счёт поддержки избыточных копий данных. Кроме Репликации, избыточность на уровне баз данных способны обеспечить несколько механизмов SQL Server 2008. Это такие возможности, как резервное копирование с последующим восстановлением, Доставка журналов и Зеркальное отображение базы данных. Причём, Зеркальное отображение является единственным механизмом, который поддерживает точную копию защищаемой базы данных практически в реальном масштабе времени, и гарантирует отсутствие потерь данных.
В этой статье на примерах мы посмотрим, как можно использовать Зеркальное отображение реплицируемой базы данных для повышения её доступности. Мы рассмотрим как Репликация и Зеркальное отображение влияют друг на друга, а также, как Зеркальное отображение совместимо с Доставкой журналов и как Доставка журналов совместима с Репликацией. Кроме того, в этой статье мы коснёмся возможностей использования для первоначальной синхронизации баз данных механизмов Доставки журналов, и вкратце рассмотрим принципы работы инициализации подписчика, основанной на логических номерах виртуальных журналов (LSN), которая позволяет сократить время восстановления после отказа при наличии зеркальной копии базы данных Подписчика.

Совместимость Доставки журналов с Зеркальным отображением и Репликацией

В настоящей статье мы не ставим целью полностью раскрыть тему совмещения Доставки журналов с Зеркальным отображением баз данных и Репликацией. Эта тема достаточно подробно раскрыта в BOL. Здесь мы рассмотрим только те аспекты Доставки журналов, которые нам пригодятся для первоначальной синхронизации основной базы данных Зеркального отображения и баз данных Подписчиков в топологии репликации. Коротко будет сказано и о том, как можно использовать Доставку журналов в целях резервного копирования участвующих в Репликации баз данных.
Целью Доставки журналов SQL Server 2008 является обеспечение автоматической синхронизации баз данных, которая осуществляется путём резервного копирования журналов транзакций в базе данных Источника, и последующей доставке и восстановление копий журнала транзакций в базе данных Получателя. Сервер, который обслуживает базу данных Получателя, выступает в роли резервного сервера или сервера отчётов. Сервер отчётов предоставляет возможность обработки запросов пользователей на чтение данных. Одна база данных Источника может синхронизироваться с одной или с несколькими базами данных Получателей. Доставка журналов применима к тем базам данных, которые используют полную модель восстановления или модель восстановления с неполным протоколированием.
Давайте договоримся, что сервер, первоначально обслуживающий базу данных Источника, будем называть сервером источника (термин "основной сервер" был бы удобнее, но он используется в Зеркальном отображении, и мы его не станем использовать, чтобы не было путаницы), а сервер, первоначально обслуживающий базу данных Получателя, называть резервным сервером. После настройки, сервер источника обслуживает базу данных Источника, но может сменить роль и обслуживать базу данных Получателя. Изменение роли приводит к одновременному изменению настроек на всех участвующих в Доставке журналов серверах, поскольку сервер источника может быть только один.
В Доставке журналов предусмотрены средства мониторинга и оповещения обо всех штатных и нештатных состояниях участников процесса Доставки журналов. Начиная с SQL Server 2008, поддерживается сжатие резервных копий, и такие копии можно использовать для Доставки журналов. Копии журналов создаются и доставляются с помощью заданий автоматизации службы SQL Server Agent, всего таких заданий четыре: задание резервного копирования Источника, задание копирования файлов Получателю, задание восстановления копий на Получателе и задание рассылки предупреждений.
Формат хранения данных на диске для SQL Server x64 и x86 одинаков, т.е. в Доставке журналов могут участвовать сервера баз данных под управлением 32-х и 64-х разрядных операционных систем. Доставка журналов поддерживается следующими редакциями SQL Server 2005: Enterprise Edition, Standard Edition и Workgroup Edition. Серверы, задействованные в доставке журналов, должны иметь одинаковые параметры сортировки, а базы данных источника и получателей могут использовать только модель полного восстановления (Full) или модель восстановления с неполным протоколированием (Bulk Logged).
Доставка журналов может быть средством тиражирования данных, однако, в этой статье мы рассмотрим возможность использования Доставки журналов для первоначальной синхронизации баз данных в Зеркальном отображении и Репликации.
Из BOL мы знаем, что Доставку журналов допустимо совмещать с Зеркальным отображением. В таком случае база данных Источника должна являться основной базой данных Зеркального отображения. Причём, Зеркальное отображение может быть настроено в любом из трёх режимов его работы. Базы данных Получателей не должны находиться на том же экземпляре сервера, что и база источника или на экземпляре Зеркального отображения базы Источника. Поскольку Зеркальное отображение позволяет делать резервные копии только базы данных основного сервера, использование в качестве базы данных Источника зеркальной копии основной базы невозможно. Настраивать Доставку журналов и Зеркальное отображение на основном сервере можно в любом порядке.
Допустимо совмещать Доставку журналов с Репликацией, и это справедливо для издаваемой базы данных, баз данных Подписок, и для базы данных Распространителя. Следует учитывать, что процесс репликации будет прерван в случае перехода на резервный сервер, поскольку агенты репликации не умеют правильно реагировать на изменение ролей серверов в Доставке журналов, и это приведёт к тому, что транзакции перестанут тиражироваться подписчикам. После того, как роль базы данных Источника вернётся в первоначальное состояние, репликация возобновляется, и все те транзакции, которые были скопированы Доставкой журналов с резервного сервера на сервер источник, реплицируются подписчикам. В случае безвозвратной потери базы данных Источника, достаточно переименовать Получателя, чтобы возобновить процесс репликации.
Первоначальная инициализация базы данных Получателя в процессе настройки Доставки журналов осуществляется путём восстановления полной резервной копии базы данных источника с параметрами NORECOVERY или STANDBY. Для организации доставки журналов необходимо создать сетевой ресурс, в котором будут создаваться резервные копии журналов транзакций и который будет доступен всем участвующим в Доставке журналов серверам. В нашем примере в качестве такого ресурса будет использоваться каталог сервера источника C:\MSSQL\LogShip\.
Существует возможность синхронизировать процесс доставки журналов с тиражированием данных в репликации транзакций. Такая синхронизация устанавливается с помощью системной хранимой процедуры sp_replicationdboption, которая позволяет для публикуемой базы данных и базы данных Распространителя установить опцию "sync with backup". Когда эта опция установлена для базы данных Распространителя, это гарантирует, что транзакции в журнале публикуемой базы данных не будут усечены до тех пор, пока не будет создана их резервная копия в базе данных Распространителя. Усечение журнала транзакций публикуемой базы данных откладывается до завершения резервного копирования усекаемых транзакций в базе данных Распространителя. Установка этой опции позволяет управлять точкой усечения базы данных Публикации. Новое значение вступает в силу после очередного запуска Агента чтения журнала или по истечении заданного параметром агента -MessageInterval интервала времени, если Агент чтения журнала работает в непрерывном режиме. За счёт того, что в базе данных распространителя не будет транзакций, которых нет в резервной копии публикуемой базы данных, можно не боятся рассогласованности издаваемой базы данных и базы данных Распространителя в случае восстановления издаваемой базы данных из резервной копии. Однако, следует помнить, что такой способ синхронизации добавляет задержку в процесс тиражирования транзакций и не гарантирует отсутствия потерь данных, в случае повреждения журнала транзакций. Кроме того, для Доставки журналов опция важна только для издаваемой базы данных.
В рамках этой статьи мы не будем рассматривать совместную работу Доставки журналов и Репликации. Доставка журналов нами будет использоваться только для целей первоначальной синхронизации баз данных подписчиков и Зеркального отображения базы данных Издателя. Поэтому сценарии включения Доставки журналов будут использоваться в примерах с демонстрацией включения Зеркального отображения и Репликации.
Дополнительную информацию о Доставке журналов можно получить в следующих статьях BOL:

  • Развертывание доставки журналов
  • Репликация и доставка журналов
  • Зеркальное отображение баз данных и доставка журналов
  • Резервное копирование и восстановление из копий реплицируемых баз данных
  • Стратегии резервного копирования и восстановления из копии репликации моментальных снимков и репликации транзакций
  • Совместимость Зеркального отображения и Репликации

Не все базы данных в топологии Одноранговой репликации транзакций допускают Зеркальное отображение (зеркалирование). Например, это невозможно для базы данных Распространителя. В Репликации транзакций зеркалирование базы данных подписчика требует учёта некоторых ограничений, которых нет для издаваемой базы или в одноранговой топологии. Возможность использования в репликации зеркалирования зависит от того, насколько будут способны задействованные агенты репликации отрабатывать состояние отказа основной базы данных и автоматически переключаться на зеркальную копию. Так, например, ни один из соответствующих агентов репликации не может правильно отреагировать на отказ базы данных Распространителя и переключиться на работу с зеркальной копией этой базы данных. Для обеспечения высокой доступности Распространителя его следует вынести на выделенный компьютер и отдать его под управление отказоустойчивого кластера. Однако, те агенты репликации, которые соединяются с публикуемой базой данных, умеют переключаться на зеркальную копию, и в случае отказа способны подключиться автоматически, обеспечив непрерывность процесса репликации.
С подписчиками всё не так просто, как с издателем. К сожалению, ни один из работающих с базами на подписчике агентов репликации не предназначен для автоматического переключения в случае отказа. Самым естественным путём переключения потока данных репликации является удаление подписки и создание её заново.
Дополнительные сведения можно получить в следующей статье BOL: "Репликация и зеркальное отображение базы данных".

Влияние зеркалирования на работу Агента чтения журнала

Зеркалирование публикуемой базы данных влияет на поведение Агента чтения журнала, его состояние становится зависимым от состояния зеркалирования. Агент чтения журнала зеркалируемой базы данных будет копировать из журнала вначале записи тех транзакций, которые были до этого скопированы и завершены в журнале регистрации транзакций зеркальной базы данных (процесс, с помощью которого пишутся записи в журнал транзакций зеркальной базы данных, в документации называют hardening - закрепление). Т.е. реплицироваться будут только записи с таким LSN, который больше LSN последней закреплённой в журнале транзакций зеркала.
Это позволяет выставить из топологии основной сервер (зеркало доступно, но существуют такие записи в журнале транзакций, которые еще не были закреплены на зеркале) или изолировать его (когда зеркало недоступно). В обоих случаях, пока основной сервер работоспособен и его база данных доступна, любые изменения в его базе данных не будут реплицированы, пока соответствующие записи журнала транзакций не будут закреплены на зеркале.
Такое поведение добавляет задержки в поток репликации, и если произойдёт отказ зеркалирования, будет гарантировано, что записи в журнале Подписчика не обгонят фиксацию в основной базе данных.
Ели Агент чтения журнала вынужден ждать закрепления записей в журнале транзакций зеркальной базы данных, в хронологии работы Агента чтения журнала будут появляться уведомления следующего вида: "Replicated transactions are waiting for next Log backup or for mirroring partner to catch up".

Изменение поведения Агента чтения журнала при установке флага трассировки 1448

В редких случаях, эффект задержек из-за зеркалирования для логики работы использующих репликацию приложений может оказаться не приемлем. Для решения этой проблемы добавлен новый флаг трассировки 1448, который предписывает репликации продолжаться даже в тех случаях, когда основная база данных выставлена или изолирована. Обычно, Агента чтения журнала ждет, пока не будут закреплены записи в журнале регистрации транзакций зеркальной базы данных, после чего он копирует их в базу данных Распространителя. Когда сервер запущен с флагом трассировки 1448, это ожидание исключается, и Агента чтения журнала может сразу копировать изменения, независимо от состояния зеркалирования.
Этот флаг трассировки можно применять в SQL Server 2008, а для SQL Server 2005 он появился в составе Cumulative update package 2 для SQL Server 2005 Service Pack 2. Подробности о флаге трассировки №1448 можно узнать в статье базы знаний Майкрософт №937041.
Обратите внимание на потенциальную опасность исключения задержек тиражирования в репликации. Для приложений отсутствие задержек может иметь решающее значение, и это может послужить причиной установки флага трассировки №1448 при использовании зеркалирования с репликацией. Надо понимать, что существует вероятность того, что использование этого флага приведёт к проблемам в случае отказа Зеркального отображения. Эти проблемы будут рассмотрены ниже.

 

Влияние отказа зеркалирования на работу Агента чтения журнала

Когда происходит автоматическая или ручная отработка отказа Зеркального отображения, Агент чтения журнала должен автоматически соединиться с новым основным сервером и продолжить копировать транзакций (если параметр -PublisherFailoverPartner был установлен правильно, как это будет описано ниже). Есть два случая, когда этого может не произойти. Первый, это когда отказ не может быть отработан правильно, потому что зеркальный сервер по каким либо причинам не может стать основным сервером. Второй случай может произойти только тогда, когда включён флаг трассировки №1448, о котором только что упоминалось выше.
Когда Зеркальное отображение не работает в режиме высокой доступности или основной сервер был выставлен или изолирован, может оказаться, что на основном сервере есть завершённые транзакции, которых еще не были закреплены на зеркале. Если основная база данных станет недоступной, и произойдёт передача её роли зеркалу, может случиться потеря тех транзакций, которые были завершены на основном сервере, но еще не были закреплены на зеркале. Если к тому же включён флаг трассировки №1448, некоторые из завершённых транзакций основного сервера могут быть скопированы, но ещё не закреплены на зеркале. Это приведёт к тому, что состояние метаданных о тиражируемых транзакциях на Распространителе будет опережать реальное состояние транзакций на принявшем роль основной базы данных зеркале. Т.е. Агент чтения журнала уже передавал Распространителю некоторое количество транзакций. После обнаружения таких расхождений, Агент распространителя выдаст ошибку: "The process could not execute 'sp_repldone/sp_replcounters' on ' GLADCHENKO-TEST'". Кроме этого сообщения об ошибке, будут и детализирующие ситуацию сообщения, из которых можно узнать, какой номер виртуального журнала пытался считать Агент чтения журнала. Исправить положение можно запустив с помощью системной хранимой процедуры sp_replrestart принудительную синхронизацию метаданных Издателя и Распространителя. Однако, велика вероятность того, что из-за расхождений в метаданных часть не синхронизированных в результате отказа основного сервера транзакций уже может попасть в базы данных Подписчиков. Это приведёт к тому, что Агент распространителя тоже прекратит свою работу, выдав сообщение об ошибке синхронизации данных. Поскольку в результате сбоя синхронизации Распространитель просто повторит изменения, передаваемые не прошедшими синхронизацию транзакциями, для преодоления ошибки работы Агента распространителя достаточно сменить его профиль на стандартный профиль с именем: "Continue on data consistency errors".

Как влияет нарушение зеркалирования на работу Агента чтения журнала

Как уже отмечалось, в случае отказа Зеркального отображения Агент чтения журнала станет работать с журналом транзакций зеркала основной базы данных. Возможность переключения на сменивший свою роль новый основной сервер обеспечивается параметром запуска агента -PublisherFailoverPartner. Пока между обоими участниками Зеркального отображения действует партнёрство, т.е. они продолжают работать по схеме основная база - зеркало основной базы, в случае отказа основной базы данных Агент чтения журнала подключится к принявшей роль основной, зеркальной базе данных.
Если основной сервер будет недоступен очень долго, лучше всего удалить зеркалирование, что позволит избежать проблем с ростом журнала транзакций зеркальной базы данных. Если отказ приведёт к тому, что будет нарушено партнёрство основной и зеркальной баз данных, Агент чтения журнала будет продолжать работать с зеркалом, пока ситуация с партнёрством не восстановится или зеркальное отображение не будет удалено. Если потом агента перезапустить, он попробует соединяться с основной базой данных, которая больше не является издаваемой базой данных, что приведёт к сбою репликации. В хронологии работы Агента чтения журнала появятся сообщения такого типа: "User-specified agent parameter values: -Publisher GLADCHENKO-TEST'". Также будут более детальные сообщения об отказе подключения. Для того чтобы решить подобную проблему, и заставить Агент чтения журнала продолжить свою работу, создайте на Распространителе псевдоним действующего сервера издателя.

Настройка периода хранения Распространителя

По умолчанию на Распространителе значение периода хранения транзакций установлено в ноль. Это означает, что транзакции очищаются сразу же, как только они будут доставлены всем Подписчикам. При использовании Зеркального отображения, в момент, когда из-за отказа Агент распространителя переключается на работу с зеркальной копией базы данных, нужно чтобы в базе данных распространителя ещё оставались транзакции, которые уже доставлены всем Подписчикам. Такое случается, когда зеркалирование работает в высокопроизводительном режиме, т.е. передача транзакций зеркалу идёт асинхронно и состояние зеркала может сильно отставать от состояния основной базы данных. Тогда, при необходимости добавления нового Подписчика, которого нежелательно инициализировать, у Агента распространителя должна существовать возможность догрузить новому Подписчику те транзакции, которые на момент добавления подписчика ещё не успели закрепиться в зеркале. Другой такой случай может произойти при отказе сервера Издателя, когда данные в базе данных публикации больше не доступны, и это не позволяет корректно инициализировать новую базу данных подписчика. В таком случае, при отсутствии транзакций на Распространителе, может произойти потеря данных.
Чтобы избегать потери данных или длительного простоя в системе с зеркалированием базы данных подписчика, рекомендуется установить адекватный период задержки транзакций у распространителя.
В наших примерах мы будем использовать следующий вариант настройки параметра задержки распространения:

    USE distribution
    EXEC sp_changedistributiondb N'distribution', N'min_distretention', 3
    EXEC sp_helpdistributiondb;
    GO

Типы ручной синхронизации Подписчика

Репликация не поддерживает автоматический переход на зеркальное отображение подписанной на публикацию базы данных, т.е. хотя зеркалирование и возможно, Агент распространителя не сможет переключиться на зеркало после отказа основной базы данных Зеркального отображения. Работа Агента распространителя закончится ошибкой сразу, как только он поймёт, что не может подключиться к базе данных, являющейся основной в партнёрстве Зеркального отображения. Зеркалирование Подписчика можно настраивать как до, так и после подписки. Однако, если планируется инициализация Подписчика моментальным снимком Публикации, эффективнее будет настраивать Зеркальное отображение после инициализации базы данных Подписчика.
В случае отказа Зеркального отображения базы данных Подписчика, необходимо перенаправить Репликацию на зеркальный сервер, туда, где теперь располагается новая база данных Подписчика, на новом основном сервере. Хотя этот метод требует заново оформить подписку, полной её инициализации не потребуется, и это благодаря новому типу синхронизации SQL Server 2008.
Все методы ручной инициализации Подписчика основаны на том, что база данных приводится в согласованное с Издателем состояние, и это становится отправной точкой тиражирования последовавших за этим состоянием транзакций Издателя. После создания Подписки с ручной синхронизацией, Агент распространителя должен иметь информацию о том, какие транзакции нужно тиражировать, не потеряв при этом новые данные или изменения в имеющихся данных. В качестве такой отправной точки используется тот номер LSN, который применим к соответствующему методу инициализации. Например, в методе с указанием параметра "replication support only" определение LSN происходит автоматически. Когда же инициализация осуществляется восстановлением из резервной копии, LSN берётся из заголовка файла резервной копии. В случае инициализации по LSN, необходимый номер виртуального журнала задаётся пользователем. Этот номер нужно указывать аккуратно, т.к. относящиеся к нему транзакции должны присутствовать на этот момент в базе данных Распространителя. Вот почему так важна задержка транзакций у Распространителя, это очень помогает в случае отказа.

Примеры

Для демонстрации новых возможностей обеспечения доступности Репликации SQL Server 2008 нам потребуется сервер Распространитель, который в представленном ниже сценарии создания Распространителя называется GLADCHENKO-VHD:

    -- Сценарий создания Распространителя
    use master
    exec sp_adddistributor @distributor = N'GLADCHENKO-VHD'
    , @password = N'' -- безопасность репликации не тема этой статьи,
                      -- оставляем уязвимой
    GO
    exec sp_adddistributiondb
      @database = N'distribution'
    , @data_folder = N'C:\MSSQL\DATA'
    , @log_folder = N'C:\LOG\DATA'
    , @log_file_size = 2
    , @min_distretention = 0
    , @max_distretention = 72
    , @history_retention = 48
    , @security_mode = 1
    GO

    use [distribution]
    if (not exists (select * from sysobjects where name = 'UIProperties'
        and type = 'U '))
        create table UIProperties(id int)
    if (exists (select * from ::fn_listextendedproperty('SnapshotFolder'
        , 'user', 'dbo', 'table', 'UIProperties', null, null)))
        EXEC sp_updateextendedproperty
      N'SnapshotFolder'
        , N'C:\MSSQL\ReplData'
        , 'user'
        , dbo
        , 'table'
        , 'UIProperties'
    else
        EXEC sp_addextendedproperty
      N'SnapshotFolder'
        , N'C:\MSSQL\ReplData'
        , 'user'
        , dbo
        , 'table'
        , 'UIProperties'
    GO

    exec sp_adddistpublisher
      @publisher = N'GLADCHENKO-VHD'
    , @distribution_db = N'distribution'
    , @security_mode = 1
    , @working_directory = N'C:\MSSQL\ReplData'
    , @trusted = N'false'
    , @thirdparty_flag = 0
    , @publisher_type = N'MSSQLSERVER'
    GO

Зеркальное отображение издаваемой базы данных

Мы будем использовать сервер GLADCHENKO-TEST в качестве основного сервера в зеркале и сервера издателя в Репликации. Публиковаться будет база данных MIR, и она же будет иметь своё Зеркальное отображение. Публикация тоже будет называться MIR. Сервер, на котором будет размещаться зеркало, называется GLADCHENKO-VHD и этот же сервер является в нашей тестовой топологии репликации Распространителем. В качестве сервера Подписчика будет выступать экземпляр GLADCHENKO-A\SUB, на котором база данных подписки будет называться MIR. Хотя репликацию можно настроить с помощью SQL Server Management Studio, мы будем её настраивать с помощью системных процедур Transact-SQL.
Для простоты демонстрации, все учётные записи, от имени которых будут запускаться службы, и имена входа, в контексте безопасности которых будут выполняться работы на серверах, будут сведены к одной, доменной учётной записи пользователя AG@troika.ru. Этот пользователь является локальным администратором на всех серверах, и включён в серверную роль sysadmins всех указанных выше серверов баз данных.

Шаг 1. Создание Публикации

Публикуемая база данных MIR состоит из четырёх таблиц: PerformanceCounter, PerformanceSignature, PerformanceSignatureData и PerformanceSignatureHistory. На самом деле, эти таблицы тиражируются средствами репликации транзакций с сервера, на котором размещена база данных System Center Operations Manager 2007 SP1. Сделано это для того, чтобы эмулировать работу "живого" приложения, поскольку данные мониторинга инфраструктуры серверов поступают в эти таблицы постоянно и без длительных перерывов. Именно этот поток реплицируемой информации мы и будет реплицировать с сервера GLADCHENKO-TEST на сервер GLADCHENKO-A\SUB.
Для получения более подробной информации о настройке Распространителя, смотрите следующие темы в SQL Server 2008 Books Online:

Ниже представлен сценарий создания Публикации.

Вначале, нужно разрешить на Распространителе ещё одного Издателя. Для этого на сервере GLADCHENKO-VHD выполните следующий сценарий:

    exec sp_adddistpublisher
    @publisher = N'GLADCHENKO-TEST'
    , @distribution_db = N'distribution'
    , @security_mode = 1
    , @working_directory = N'C:\MSSQL\ReplData'
    , @trusted = N'false'
    , @thirdparty_flag = 0
    , @publisher_type = N'MSSQLSERVER'
    GO

После этого можно приступить к созданию Публикации, для чего на сервере GLADCHENKO-TEST нужно выполнить следующий сценарий:

    use master
    -- Указываем Распространителя
    exec sp_adddistributor @distributor = N'GLADCHENKO-VHD'
    , @password = N'*********' -- тут нужно указать заданный
                               -- на Распространителе пароль

    -- В ответ должны получить подтверждение:
    -- "You have updated the Publisher property 'active' successfully"
    GO

    use [MIR]
    exec sp_replicationdboption @dbname = N'MIR'
    , @optname = N'publish'
    , @value = N'true'
    GO

    -- Добавляет Публикацию
    exec sp_addpublication
      @publication = N'MIR'
    , @description = N'Публикация БД MIR в репликации
    транзакций, на сервере GLADCHENKO-TEST'

    , @sync_method = N'native'
    , @retention = 0
    , @allow_push = N'true'
    , @allow_pull = N'true'
    , @allow_anonymous = N'false'
    , @enabled_for_internet = N'false'
    , @snapshot_in_defaultfolder = N'true'
    , @compress_snapshot = N'false'
    , @ftp_port = 21
    , @ftp_login = N'anonymous'
    , @allow_subscription_copy = N'false'
    , @add_to_active_directory = N'false'
    , @repl_freq = N'continuous'
    , @status = N'active'
    , @independent_agent = N'true'
    , @immediate_sync = N'true'
    , @allow_sync_tran = N'false'
    , @autogen_sync_procs = N'false'
    , @allow_queued_tran = N'false'
    , @allow_dts = N'false'
    , @replicate_ddl = 1
    , @allow_initialize_from_backup = N'true'
    , @enabled_for_p2p = N'false'
    , @enabled_for_het_sub = N'false'
    , @p2p_conflictdetection = N'false'
    , @p2p_originator_id = 1
    -- В ответ должны получить сообщение:

    -- Job 'GLADCHENKO-TEST-MIR-1' started successfully.

    -- "Warning: The logreader agent job has been implicitly created
    -- and will run under the SQL Server Agent Service Account"

    GO

    -- Создаём инициализирующий моментальный снимок
    exec sp_addpublication_snapshot @publication = N'MIR'
    , @frequency_type = 1
    , @frequency_interval = 1
    , @frequency_relative_interval = 1
    , @frequency_recurrence_factor = 0
    , @frequency_subday = 8
    , @frequency_subday_interval = 1
    , @active_start_time_of_day = 0
    , @active_end_time_of_day = 235959
    , @active_start_date = 0
    , @active_end_date = 0
    , @job_login = null
    , @job_password = null
    , @publisher_security_mode = 1
    GO
    -- Добавляем наши таблицы в виде статей публикации
    exec sp_addarticle @publication = N'MIR'
    , @article = N'PerformanceCounter'
    , @source_owner = N'dbo'
    , @source_object = N'PerformanceCounter'
    , @type = N'logbased'
    , @description = null
    , @creation_script = null
    , @pre_creation_cmd = N'drop'
    , @schema_option = 0x000000000803509F
    , @identityrangemanagementoption = N'manual'
    , @destination_table = N'PerformanceCounter'
    , @destination_owner = N'dbo'
    , @vertical_partition = N'false'
    , @ins_cmd = N'CALL sp_MSins_dboPerformanceCounter'
    , @del_cmd = N'CALL sp_MSdel_dboPerformanceCounter'
    , @upd_cmd = N'SCALL sp_MSupd_dboPerformanceCounter'
    GO
    exec sp_addarticle @publication = N'MIR'
    , @article = N'PerformanceSignature'
    , @source_owner = N'dbo'
    , @source_object = N'PerformanceSignature'
    , @type = N'logbased'
    , @description = null
    , @creation_script = null
    , @pre_creation_cmd = N'drop'
    , @schema_option = 0x000000000803509F
    , @identityrangemanagementoption = N'manual'
    , @destination_table = N'PerformanceSignature'
    , @destination_owner = N'dbo'
    , @vertical_partition = N'false'
    , @ins_cmd = N'CALL sp_MSins_dboPerformanceSignature'
    , @del_cmd = N'CALL sp_MSdel_dboPerformanceSignature'
    , @upd_cmd = N'SCALL sp_MSupd_dboPerformanceSignature'
    GO
    exec sp_addarticle @publication = N'MIR'
    , @article = N'PerformanceSignatureData'
    , @source_owner = N'dbo'
    , @source_object = N'PerformanceSignatureData'
    , @type = N'logbased'
    , @description = null
    , @creation_script = null
    , @pre_creation_cmd = N'drop'
    , @schema_option = 0x000000000803509F
    , @identityrangemanagementoption = N'manual'
    , @destination_table = N'PerformanceSignatureData'
    , @destination_owner = N'dbo'
    , @vertical_partition = N'false'
    , @ins_cmd = N'CALL sp_MSins_dboPerformanceSignatureData'
    , @del_cmd = N'CALL sp_MSdel_dboPerformanceSignatureData'
    , @upd_cmd = N'SCALL sp_MSupd_dboPerformanceSignatureData'
    GO
    exec sp_addarticle @publication = N'MIR'
    , @article = N'PerformanceSignatureHistory'
    , @source_owner = N'dbo'
    , @source_object = N'PerformanceSignatureHistory'
    , @type = N'logbased'
    , @description = null
    , @creation_script = null
    , @pre_creation_cmd = N'drop'
    , @schema_option = 0x000000000803509F
    , @identityrangemanagementoption = N'manual'
    , @destination_table = N'PerformanceSignatureHistory'
    , @destination_owner = N'dbo'
    , @vertical_partition = N'false'
    , @ins_cmd = N'CALL sp_MSins_dboPerformanceSignatureHistory'
    , @del_cmd = N'CALL sp_MSdel_dboPerformanceSignatureHistory'
    , @upd_cmd = N'SCALL sp_MSupd_dboPerformanceSignatureHistory'
    GO
    -- настраиваем и запускаем Агента чтения журнала
    exec [MIR].sys.sp_addlogreader_agent
      @job_login = null
    , @job_password = null
    , @publisher_security_mode = 1
    GO
    exec [MIR].sys.sp_addqreader_agent
    @job_login = null
    , @job_password = null
    , @frompublisher = 1
    GO
    -- Формируем лист доступа к публикации (PAL)
    -- Сюда нужно даобавить все задействованные агентами логины
    exec sp_grant_publication_access
      @publication = N'MIR'
    , @login = N'sa'
    GO
    exec sp_grant_publication_access
      @publication = N'MIR'
    , @login = N'NT AUTHORITY\SYSTEM'
    GO

Шаг 2. Настройка Доставки журналов для инициализации Подписчика

На этом шаге мы должны подготовить базу данных на сервере Подписчика, для того, чтобы она могла быть инициализирована из резервной копии. Поскольку поток транзакций в базе данных нашего Издателя не прерывается 24 часа в сутки, 7 дней в неделю и 365 дней в году, нам будет удобно задействовать для этого механизм Доставки журналов.
Для включения Доставки журналов нужно инициализировать базу данных Получателя путем восстановления полной резервной копии базы данных Источника. В нашем случае, резервным сервером, а заодно и сервером Подписчика будет компьютер GLADCHENKO-A\SUB.
Делаем полную копию издаваемой базы данных, для чего, на сервере GLADCHENKO-TEST выполняем следующий сценарий:

    BACKUP DATABASE [MIR]
    TO DISK = N'C:\MSSQL\BACKUP\MIR.bak'
    WITH NOFORMAT, INIT
    , NAME = N'Полная копия БД MIR'
    , SKIP, NOREWIND, NOUNLOAD, STATS = 10
    GO

Дожидаемся получения подтверждения успешности этой операции, в окне результатов появится приблизительно такой текст:

    10 percent processed.
    20 percent processed.
    30 percent processed.
    40 percent processed.
    50 percent processed.
    60 percent processed.
    70 percent processed.
    80 percent processed.
    90 percent processed.
    Processed 18104 pages for database 'MIR', file 'MIR' on file 1.
    100 percent processed.
    Processed 3 pages for database 'MIR', file 'MIR_log' on file 1.
    BACKUP DATABASE successfully processed 18107 pages in 4.394 seconds (32.193 MB/sec).

После того, как копия успешно создана, нужно её скопировать на сервер Источника. В нашем случае, нужно взять файл с компьютера GLADCHENKO-TEST из папки C:\MSSQL\BACKUP\MIR.bak и скопировать его на компьютер GLADCHENKO-A, в папку: C:\MSSQL\BACKUP\MIR.bak.
После того, как файл резервной копии окажется на сервере Получателя, нужно восстановить базу данных Получателя, что мы и сделаем на экземпляре GLADCHENKO-A\SUB, используя следующий сценарий:

    RESTORE DATABASE [MIR]
    FROM DISK = N'D:\MSSQL\BACKUP\MIR.bak'
    WITH FILE = 1
    , MOVE N'MIR' TO N'C:\MSSQL\DATA\MIR.mdf'
    , MOVE N'MIR_log' TO N'D:\MSSQL\LOG\MIR_1.ldf'
    , NOUNLOAD, REPLACE, STATS = 10, NORECOVERY
    GO

После успешного восстановления базы данных, приступаем к настройке Доставки журналов между публикуемой базой данных на Издателе, и только что восстановленной базой на Подписчике. Подробное описание настройки Доставки журналов можно найти в статье электронной документации: "Как включить доставку журналов (Transact-SQL)". Создадим папку для обмена резервными копиями журналов транзакций: C:\MSSQL\LogShip. На эту папку у учётных записей служб должен быть полный доступ.
Далее, настраивает базу данных Источника вместе с заданием резервного копирования, а также записями локального и удаленного мониторов:

    DECLARE @LS_BackupJobId AS uniqueidentifier
    DECLARE @LS_PrimaryId AS uniqueidentifier

    EXEC master.dbo.sp_add_log_shipping_primary_database
     @database = N'MIR'
    ,@backup_directory = N'C:\MSSQL\LogShip'
    ,@backup_share = N'\\GLADCHENKO-TEST\lsbackup'
    ,@backup_job_name = N'LSBackup_MIR'
    ,@backup_retention_period = 1440
    ,@backup_compression = 0
    ,@monitor_server = N'GLADCHENKO-A'
    ,@monitor_server_security_mode = 1
    ,@backup_threshold = 60
    ,@threshold_alert_enabled = 1
    ,@history_retention_period = 1440
    ,@backup_job_id = @LS_BackupJobId OUTPUT
    ,@primary_id = @LS_PrimaryId OUTPUT
    ,@overwrite = 1
    GO

Значения идентификаторов получились соответственно следующие:

    @LS_BackupJobId = DDCD7E77-F045-4649-A9F1-CF8FE843C711
    @LS_PrimaryId = 3346F94D-3DB6-4A56-BE99-DF2E60E0A25F

Если процедура отработала без ошибок, должно появиться новое задание, предусматривающее один шаг, в котором выполняется следующая команда:

c:\Program Files\Microsoft SQL Server\100\Tools\Binn\sqllogship.exe" -Backup 3346F94D-3DB6-4A56-BE99-DF2E60E0A25F -server GLADCHENKO-TEST'

Создаём расписание для задания резервного копирования журналов транзакций базы данных MIR:

    DECLARE @schedule_id int
    EXEC msdb.dbo.sp_add_jobschedule
     @job_id=N'DDCD7E77-F045-4649-A9F1-CF8FE843C711'
    ,@name=N'1'
    ,@enabled=1
    ,@freq_type=4
    ,@freq_interval=1
    ,@freq_subday_type=4
    ,@freq_subday_interval=10
    ,@freq_relative_interval=0
    ,@freq_recurrence_factor=1
    ,@active_start_date=20081202
    ,@active_end_date=99991231
    ,@active_start_time=0
    ,@active_end_time=235959
    ,@schedule_id = @schedule_id OUTPUT
    SELECT @schedule_id
    GO

Это расписание предусматривает выполнение задания каждые 10 минут в течение суток.
Мы не планируем создавать полное решение Доставки журналов, наша задача только облегчить первоначальную инициализацию Подписчика. Однако, давайте полностью следовать упомянутой выше инструкции BOL по включению Доставки журналов.
Далее, нам нужно на сервере Источника задать базу данных Получателя:

    EXEC master.dbo.sp_add_log_shipping_primary_secondary
     @primary_database = N'MIR'
    ,@secondary_server = N'GLADCHENKO-A\SUB'
    ,@secondary_database = N'MIR'
    ,@overwrite = 1
    GO

После этого, на сервере Получателя (Подписчик, в нашем примере это компьютер GLADCHENKO-A\SUB) нужно запустить процедуру, которая создаст все необходимые задания по применению резервных копий на базе данных Получателя.

    DECLARE @LS_Secondary__CopyJobId AS uniqueidentifier
    DECLARE @LS_Secondary__RestoreJobId AS uniqueidentifier
    DECLARE @LS_Secondary__SecondaryId AS uniqueidentifier

    EXEC master.dbo.sp_add_log_shipping_secondary_primary
     @primary_server = N'GLADCHENKO-TEST'
    ,@primary_database = N'MIR'
    ,@backup_source_directory = N'\\GLADCHENKO-TEST\lsbackup'
    ,@backup_destination_directory = N'\\GLADCHENKO-A\D$\MSSQL\LogShip'
    ,@copy_job_name = N'LSCopy_GLADCHENKO-TEST_MIR'
    ,@restore_job_name = N'LSRestore_GLADCHENKO-TEST_MIR'
    ,@file_retention_period = 4320
    ,@monitor_server = N'GLADCHENKO-A'
    ,@monitor_server_security_mode = 1
    ,@overwrite = 1
    ,@copy_job_id = @LS_Secondary__CopyJobId OUTPUT
    ,@restore_job_id = @LS_Secondary__RestoreJobId OUTPUT
    ,@secondary_id = @LS_Secondary__SecondaryId OUTPUT
    GO

Получаем следующий набор идентификаторов:

    @LS_Secondary__CopyJobId = 338E7DF6-F266-40CD-902E-86F424E12E6C
    @LS_Secondary__RestoreJobId = 6C0FE890-5FC5-4BA7-BD89-EC13214514E8
    @LS_Secondary__SecondaryId = DC7800CE-767C-4F2B-9A42-70DC5F807184

После этого, среди заданий экземпляра GLADCHENKO-A\SUB появятся два новых задания, для которых определены шаги, но ещё не настроены расписания их работы. Первое задание с именем: LSCopy_GLADCHENKO-TEST_MIR. У этого задания только один шаг и он выполняет следующий сценарий:

    C:\Program Files\Microsoft SQL Server\100\Tools\Binn\sqllogship.exe -Copy DC7800CE-767C-4F2B-9A42-70DC5F807184 -server GLADCHENKO-A\SUB

Эта инструкция копирует файлы резервных копий журналов транзакций с сервера Источника на сервер Получателя.
Добавить типовое расписание работы этого задания можно с помощью следующего сценария:

    EXEC msdb.dbo.sp_add_schedule
     @schedule_name =N'DefaultCopyJobSchedule'
    ,@enabled = 1
    ,@freq_type = 4
    ,@freq_interval = 1
    ,@freq_subday_type = 4
    ,@freq_subday_interval = 15
    ,@freq_recurrence_factor = 0
    ,@active_start_date = 20081203
    ,@active_end_date = 99991231
    ,@active_start_time = 0
    ,@active_end_time = 235900
    ,@schedule_uid = @LS_SecondaryCopyJobScheduleUID OUTPUT
    ,@schedule_id = @LS_SecondaryCopyJobScheduleID OUTPUT

    EXEC msdb.dbo.sp_attach_schedule
    @job_id = '338E7DF6-F266-40CD-902E-86F424E12E6C'
    ,@schedule_id = @LS_SecondaryCopyJobScheduleID
    GO

Второе задание называется LSRestore_GLADCHENKO-TEST_MIR и предназначено для восстановления скопированных с Источника резервных копий на базе данных Получателя. Единственный шаг этого задания выполняет следующую инструкцию:

    С:\Program Files\Microsoft SQL Server\100\Tools\Binn\sqllogship.exe -Restore DC7800CE-767C-4F2B-9A42-70DC5F807184 -server GLADCHENKO-A\SUB

Для добавления этому заданию расписания запуска необходимо на сервере GLADCHENKO-A\SUB выполнить следующий сценарий:

    EXEC msdb.dbo.sp_add_schedule
     @schedule_name =N'DefaultRestoreJobSchedule'
    ,@enabled = 1
    ,@freq_type = 4
    ,@freq_interval = 1
    ,@freq_subday_type = 4
    ,@freq_subday_interval = 15
    ,@freq_recurrence_factor = 0
    ,@active_start_date = 20081203
    ,@active_end_date = 99991231
    ,@active_start_time = 0
    ,@active_end_time = 235900
    ,@schedule_uid = @LS_SecondaryRestoreJobScheduleUID OUTPUT
    ,@schedule_id = @LS_SecondaryRestoreJobScheduleID OUTPUT

    EXEC msdb.dbo.sp_attach_schedule
     @job_id = '6C0FE890-5FC5-4BA7-BD89-EC13214514E8'
    ,@schedule_id = @LS_SecondaryRestoreJobScheduleID
    GO

И теперь осталось только добавить в Доставку журналов базу данных Получателя, что можно сделать, выполнив следующий сценарий на сервере GLADCHENKO-A\SUB:

    EXEC master.dbo.sp_add_log_shipping_secondary_database
     @secondary_database = N'MIR'
    ,@primary_server = N'GLADCHENKO-TEST'
    ,@primary_database = N'MIR'
    ,@restore_delay = 0
    ,@restore_mode = 0
    ,@disconnect_users = 0
    ,@restore_threshold = 45
    ,@threshold_alert_enabled = 1
    ,@history_retention_period = 1440
    ,@overwrite = 1
    GO

Следующим действием в настройке Доставки журналов будет подготовка сервера мониторинга, в качестве которого будет выступать экземпляр GLADCHENKO-A. На этом экземпляре нужно выполнить следующий сценарий:

    EXEC msdb.dbo.sp_processlogshippingmonitorprimary
     @mode = 1
    ,@primary_id = N'3346F94D-3DB6-4A56-BE99-DF2E60E0A25F'
    ,@primary_server = N' GLADCHENKO-TEST'
    ,@monitor_server = N'GLADCHENKO-A'
    ,@monitor_server_security_mode = 1
    ,@primary_database = N'MIR'
    ,@backup_threshold = 60
    ,@threshold_alert = 14420
    ,@threshold_alert_enabled = 1
    ,@history_retention_period = 1440
    GO

После этого, нужно включить те задания обслуживания Доставки журналов, которые были отмечены как выключенные. Это должно привести к тому, что Доставка журналов начнёт свою работу и будет поддерживать актуальность базы данных MIR на сервере GLADCHENKO-A\SUB в автоматическом режиме.
Теперь, когда придёт время создавать Подписку, достаточно будет ненадолго отключить клиентов от базы данных Издателя, дождаться или запустить вручную поочерёдно все задания Доставки журналов на Источнике и потом на Получателе, отключить все эти задания, и подготовить базу данных Получателя к настройке Подписки, выполнив этот сценарий:

    RESTORE DATABASE MIR WITH RECOVERY
    GO

Шаг 3. Настройка Подписки

После успешной инициализации базы данных Подписчика резервной копией, которую мы поддерживали в актуальном состоянии средствами Доставки журналов, можно приступать к подписке на Публикацию. Для этого, на сервере GLADCHENKO-TEST нужно с помощью представленного ниже сценария объявить сервер и базу данных Подписчика:

    use [MIR]
    exec sp_addsubscription @publication = N'MIR'
    , @subscriber = N'GLADCHENKO-A\SUB'
    , @destination_db = N'MIR'
    , @sync_type = N'initialize with backup'
    , @backupdevicetype = N'disk'
    , @backupdevicename = 'LogShip\MIR_20081203195807.trn'
    , @fileidhint = 1
    , @subscription_type = N'pull'
    , @update_mode = N'read only'
    GO

Все последующие действия по оформлению Подписки нужно выполнять на сервере GLADCHENKO-A\SUB. Следующий сценарий создаёт Подписку по запросу:

    use [MIR]
    exec sp_addpullsubscription @publisher = N'GLADCHENKO-TEST'
    , @publication = N'MIR'
    , @publisher_db = N'MIR'
    , @independent_agent = N'True'
    , @subscription_type = N'pull'
    , @description = N''
    , @update_mode = N'read only'
    , @immediate_sync = 1

И наконец, следующий сценарий нужно выполнить для создания задания запуска Агента распространителя, который будет доставлять изменения непосредственно Подписчику:

    exec sp_addpullsubscription_agent @publisher = N'GLADCHENKO-TEST'
    , @publisher_db = N'MIR'
    , @publication = N'MIR'
    , @distributor = N'GLADCHENKO-VHD'
    , @distributor_security_mode = 1
    , @distributor_login = N''
    , @distributor_password = null
    , @enabled_for_syncmgr = N'False'
    , @frequency_type = 64
    , @frequency_interval = 0
    , @frequency_relative_interval = 0
    , @frequency_recurrence_factor = 0
    , @frequency_subday = 0
    , @frequency_subday_interval = 0
    , @active_start_time_of_day = 0
    , @active_end_time_of_day = 235959
    , @active_start_date = 20081203
    , @active_end_date = 99991231
    , @alt_snapshot_folder = N''
    , @working_directory = N''
    , @use_ftp = N'False'
    , @job_login = null
    , @job_password = null
    , @publication_type = 0
    GO

Когда все необходимые действия по оформлению подписки завершены, на сервер GLADCHENKO-A\SUB можно полностью удалить Доставку журналов и позаботиться об удалении созданных там заданий Доставки журналов.

Шаг 4. Зеркалирование публикуемой базы данных

В репликации транзакций существует очень опасная точка отказа, это Издатель, об обеспечении высокой доступности которого раньше было трудно заботиться. Теперь, для повышения доступности Издателя, можно создать Зеркальное отображение его базы данных и обеспечить автоматическое переключение на зеркальную копию в случае отказа. Для примера, мы попробуем настроить Зеркальное отображение базы данных Публикации на сервере Издателя и имитируем отказ с переключением Агента чтения журнала на зеркальную копию публикуемой базы данных.
В электронной документации BOL есть несколько статей, которые подробно рассматривают вопросы, связанные с настройкой Зеркального отображения баз данных. Вот список названий некоторых из них:

В последней из перечисленных выше статей говориться, что предварительно на зеркальном сервере должна быть восстановлена резервная копия основной базы данных, и все последующие резервные копии журнала транзакций, восстановленные с использованием предложения WITH NORECOVERY. Как уже отмечалось выше, лучше всего подготовку синхронной копии зеркалируемой базы данных позволяет сделать Доставка журналов. Поскольку в настоящей статье мы уже уделили много внимания настройке Доставки журналов, повторение этой процедуры для инициализации копии основной базы данных мы опустим, будем считать, что все эти процедуры уже выполнены. Копия базы данных на момент начала настройки Зеркального отображения должна находиться в актуальном по отношению к основной базе данных состоянии.
В нашем примере, основной экземпляр и зеркало настраиваются так, чтобы использовать одного и того же Распространителя. В конфигурации Агента чтения журнала мы изменим настройки, чтобы получить нужное нам поведение агента в случае отказа сервера Издателя. Для этого на сервере Распространителя GLADCHENKO-VHD нужно установить значение для параметра запуска Агент чтения журнала -PublisherFailoverPartner, например так:

    exec sp_add_agent_parameter
      @profile_id = 1 -- ID Агента моментальных снимков
    , @parameter_name = N'-PublisherFailoverPartner'
    , @parameter_value = N'GLADCHENKO-VHD' -- сервер зеркала
    GO

    exec sp_add_agent_parameter
      @profile_id = 2 -- ID Агента чтения журнала
    , @parameter_name = N'-PublisherFailoverPartner'
    , @parameter_value = N'GLADCHENKO-VHD' -- сервер зеркала
    GO

Для принятия изменений профиля, нужно перезапустить задание Агента чтения журнала.
Зеркалирование публикуемой базы данных начнём с создания конечных точек зеркалирования. В первую очередь создадим конечную точку на Издателе, это у нас компьютер GLADCHENKO-TEST:

    CREATE ENDPOINT [MirroringEndpoint]
        AUTHORIZATION [AG@troika.ru]
        STATE=STARTED
        AS TCP (LISTENER_PORT = 5022, LISTENER_IP = ALL)
        FOR DATA_MIRRORING
    ( ROLE = PARTNER
        , AUTHENTICATION = WINDOWS KERBEROS
        , ENCRYPTION = REQUIRED ALGORITHM RC4
    )
    GO

На сервере Зеркального отображения GLADCHENKO-VHD (это у нас Распространитель) тоже нужна конечная точка:

    CREATE ENDPOINT [MirroringEndpoint]
        AUTHORIZATION [AG@troika.ru]
        STATE=STARTED
        AS TCP (LISTENER_PORT = 5022, LISTENER_IP = ALL)
        FOR DATA_MIRRORING
    ( ROLE = ALL
        , AUTHENTICATION = WINDOWS KERBEROS
        , ENCRYPTION = REQUIRED ALGORITHM RC4
    )
    GO

Проверить, что конечная точка была успешно создана и запущена, можно с помощью следующей инструкции:

    SELECT role_desc, state_desc FROM sys.database_mirroring_endpoints

Одного создания конечных точек недостаточно, необходимо ещё выдать права на работу с ними тем именам входа, которые будут к ним подключаться. Пример соответствующей инструкции ниже:

    GRANT CONNECT ON ENDPOINT:: MirroringEndpoint TO AG@troika.ru GO

После раздачи прав, нужно объявить участников Зеркального отображения. На сервере Зеркальной копии GLADCHENKO-VHD выполним следующую команду:

    ALTER DATABASE [MIR] SET PARTNER = 'TCP://GLADCHENKO-TEST.troika.ru:5022'

После этого, с основного сервера GLADCHENKO-TEST запустим симметричную команду:

    ALTER DATABASE [MIR] SET PARTNER = 'TCP://GLADCHENKO-VHD.troika.ru:5022'

После этого должна произойти синхронизация основной и зеркальной баз данных. Зеркальное отображение должно быть запущено. Если этого не произошло, попробуйте внимательно изучить сообщение об ошибке. Чаще всего, причина в несогласованности баз, т.е. недостаёт последних изменений, которые могли быть не перенесены Доставкой журналов, или нечто подобное. Если причина не в этом, попробуйте отменить роль Зеркала и повторить настройку сначала. Сценарий отмены роли Зеркала представлен ниже:

    ALTER DATABASE [MIR] SET PARTNER OFF

Современные серверные операционные системы оснащаются встроенными Брандмауэрами. Такие сетевые экраны тоже способны помешать подключению к порту конечной точки. Проверить возможность подключения с основного сервера к серверу зеркальной копии можно с помощью консольной команды операционной системы:

    telnet GLADCHENKO-VHD 5022

Шаг 5. Переключение Публикации на зеркальную копию

Ну а теперь мы перейдём к самому интересному, ради чего были все эти подготовительные действия. Итак, мы имеем репликацию транзакций между двумя серверами, и Распространителя на третьем сервере. Кроме того, у нас настроено Зеркальное отображение базы данных Издателя на сервер Распространителя. Перед нами стоит задача - возобновить репликацию, которая была прервана в результате отказа Издателя. Доступность Издателя определяется Зеркальным отображением базы данных, на основе объектов которой создана наша Публикация. Отказ этой базы данных расценивается нами, как отказ Зеркального отображения и поводом для ручной или автоматической смены роли, когда зеркальная копия становится основной базой данных.
Далее, имитируем отказ основного сервера, который у нас является Издателем:

    ALTER DATABASE MIR SET PARTNER FAILOVER

В ответ получаем сообщение: "Nonqualified transactions are being rolled back. Estimated rollback completion: 100%".
После этого основная база данных Зеркального отображения меняется ролями со своим зеркалом. Причём, на Распространителе появляется публикация и видно, что у неё уже существует Подписка (которую мы создавали на сервере GLADCHENKO-TEST). Изменения, которые пользователи будут делать в базе данных MIR после её переезда на сервер GLADCHENKO-VHD, будут вначале закрепляться в новом зеркале, в базе данных на сервере GLADCHENKO-TEST. Потом они будут попадать в базу данных Распространителя, считанные Агентом чтения журнала, который умеет обращаться ко второму партнёру Зеркального отображения в случае отказа основного сервера или основной базы данных.
Если на сервере GLADCHENKO-TEST запустим следующую команду:

    SELECT PUBLISHINGSERVERNAME()

Эта команда возвратит имя исходного издателя для опубликованной базы данных, участвующей в Зеркальном отображении. Эта функция показывает первоначального издателя опубликованной базы данных, и поэтому у нас она вернёт, и всегда будет возвращать: "GLADCHENKO-TEST".
Если после этого выполнить на сервере GLADCHENKO-VHD ту же саму команду: "ALTER DATABASE MIR SET PARTNER FAILOVER", то всё вернётся в изначальное состояние. Публикация снова будет располагаться на сервере GLADCHENKO-TEST.
В отличие от агентов репликации, Монитор репликации не очень приспособлен для отслеживания работы с перемещённой на зеркальный сервер базой данных. Хотя его интерфейс будет показывать перемещение Публикации, информация о сеансах репликации может вводить администратора репликации в заблуждение. Трассировочные маркеры тоже не всегда помогают контролировать процесс репликации. В таком режиме работы репликации лучше всего контролировать сами данные, сверяя последние их изменения на издателе и подписчиках.
Существует опасность, что отказ основной базы данных приведёт к возникновению проблем репликации транзакций. Это особенно актуально, когда не соблюдается порядок первоначальной фиксации транзакции в зеркале базе данных. Если случиться так, что несколько некорректных транзакций будут препятствовать продолжению репликации данных, можно принудительно проигнорировать эти транзакции, что позволяет сделать системная процедура sp_setsubscriptionxactseqno.

Выводы

Зеркальное отображение можно использовать для повышения доступности Издателя в топологиях Репликации транзакций и Репликации слияния. Если важно, чтобы клиенты продолжали получать возможность изменения данных в издаваемой базе данных и автоматически переключались на её зеркальную копию в случае отказа, применение зеркалирования является оправданным.
В упомянутом в самом начале настоящей статьи техническом документе Майкрософт: " SQL Server Replication: Providing High Availability using Database Mirroring" предлагается вариант решения для зеркалирования баз данных Подписчиков. Там же вы найдёте аргументы для выбора такого решения.
Для повышения доступности базы данных Распространителя существует пока только одно решение - отказоустойчивая кластеризация.

 

Posted by gladchenko | 1 comment(s)
Filed under:

Сравнения списка объектов SQL Server в Powershell на примере сравнения логинов на двух серверах

В данном примере я покажу как с помощью Powershell можно сравнивать списки объектов на двух серверах на примере сравнения логинов: поиск одинаковых, поиск разницы. Для этого сначала выполним подключение к серверам. В данном примере первый сервер - локальный. К нему будем подключаться с использованием Windows аутентификации.
Второй сервер - удаленный, к нему будем подключаться с использованием аутентификации SQL Server и запросом на ввод логина и пароля при подключении.

Читаем в блоге Ирины Наумовой.

Posted by gladchenko

Вариант стратегии быстрого и надежного резервного копирования/восстановления VLDB по сети

По материалам технической статьи Майкрософт: A Case Study: Fast and Reliable Backup and Restore of a VLDB over the Network

Автор: Томас Грохсер (Thomas H. Grohser)
При содействии: Линдсей Аллен (Lindsey Allen)
Техническая экспертиза статьи: Sanjay Mishra, Lubor Kollar, Stuart Ozer, Thomas Kejser, Juergen Thomas, James Podgorski, Burzin Patel
Перевод: Александр Гладченко, Ирина Наумова
Редактура перевода: Алексей Халако
Дата издания: июнь 2009г.
Тематика статьи: SQL Server 2008

Резюме: Размер баз данных непрерывно растёт, так же, как и растут требования к надежности и доступности баз. Одновременно с этим как никогда важным становится требование быстрого и надежного восстановления данных. Этот документ посвящён проблемам проектирования устойчивого резервного копирования и решений по восстановлению очень больших баз данных (VLDB). В этой статье на реальном примере демонстрируется как лучше всего использовать функциональность SQL Server 2008 для осуществления резервного копирования и восстановления, которыми обладает SQL Server 2008, а также создание планов резервного копирования и восстановления VLDB по сети.

Читать статью в блоге Ирины Наумовой

Данный перевод опубликован на сайте SQLCAT: http://sqlcat.com/whitepapers_russian/archive/2009/11/25/pagelatch-insert.aspx

Posted by gladchenko
Filed under:

HDD DEAD?!

…и наконец моё любопытство возобладало, и я решил таки заглянуть, что же такого понаделел HP в TPC-H…

Не удивляйтесь, но поначалу я не предавал особого значения появляющимся с завидной регулярностью (раз в месяц) новым, ничем с виду не примечательным результатам. Но, когда появился третий из них, я заметил тенденцию, что они становятся с каждым разом немного ХУЖЕ и ДОРОЖЕ. Именно тогда мне стало любопытно, в чём там "порылась собака"…
Конечно, виною всему было то, как представлены результаты в сводной таблице: Complete TPC-H Results List - Sorted by Date Submitted
Просто, в представлении на сайте не видно главного изменения, поскольку испокон веков эта составляющая была неизменной и только в последние год - два ситуация начала выправляться к лучшему. На самом деле, HP в последнем из трёх своих результатов показал, как SAS диски могут оказаться более дорогим решением, чем SSD диски, при близкой производительности.
Вот несколько видоизменённое представление результатов, с детализацией по конфигурации дисковой подсистемы:

Ссылки на подробные описания тестов:

Результаты потрясающие! Оказывается решение на базе SSD получается чуть ли не в двое дешевле, чем на привычных нам SAS.
Похоже, гегемонии производителей жёстких дисков приходит долгожданный конец, а точнее, последний рудимент зари IBM PC, этот кошмарный механический монстр, самое слабое звено любого компьютера - HDD может уйти в небытие!

Posted by gladchenko | 6 comment(s)
Filed under:

Tips for DBA: Замена для стандартного "Back Up Database Task"

Очень часто получается так, что реальные бизнес -требования оказываются сложнее, чем возможности мастеров программного инструментария, поставляемого разными производителями ПО для задач администрирования SQL Server 2008. Например, недавно мне стало недостаточно гибкости мастера создания задачи резервного копирования базы данных для стандартного плана обслуживания БД. У меня возникла необходимость делать копию в несколько фалов на разных дисковых массивах и поддерживать хронологию копий по единым правилам. В несколько файлов выполнять резервное копирование бывает необходимо для повышения производительности этой операции, например, как это рекомендовано в этой статье: "A Technical Case Study: Fast and Reliable Backup and Restore of Multi-Terabytes Database over the Network".
Мастер SSMS может либо поддерживать хронологию для одного файла копий, либо копировать в фиксированные имена нескольких указанных файлов. К счастью, совершенно не составляет труда заменить задачу резервного копирования на задачу исполнения сценария T-SQL, в котором выполнить нужную работу. Ниже представлен соответствующий шаблон сценария, взяв который за основу можно составить необходимую задачу исполнения сценария T-SQL.

    DECLARE @path varchar(128)
    DECLARE @DBName AS nvarchar(4000) = 'ИМЯБАЗЫДАННЫХ'
    DECLARE @File1 AS nvarchar(4000), @File2 AS nvarchar(4000), @File3 AS nvarchar(4000)
    -- Если диск для копий один, и он корректно задан, узнать путь к папке копий можно так:
    EXEC master..xp_regread
        @rootkey='HKEY_LOCAL_MACHINE',
        @key='SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL.1\MSSQLServer',
        @value_name='BackupDirectory',
        @value=@path OUTPUT
    -- Формируем часть имени файла, в которой фиксируется имя БД и время создания копии
    DECLARE @FileName AS nvarchar(4000) = @path + '\' + @DBName +'\' + @DBName + '_' + 'backup_' +
        CONVERT(nvarchar(4),YEAR(CURRENT_TIMESTAMP), 112 ) + '_' +
        CASE MONTH(CURRENT_TIMESTAMP)
            WHEN 1 THEN '01' WHEN 2 THEN '02' WHEN 3 THEN '03'
            WHEN 4 THEN '04' WHEN 5 THEN '05' WHEN 6 THEN '06'
            WHEN 7 THEN '07' WHEN 8 THEN '08' WHEN 9 THEN '09'
            ELSE CONVERT(nvarchar(2),MONTH(CURRENT_TIMESTAMP), 112 ) END
        + '_' +
        CASE DAY(CURRENT_TIMESTAMP)
            WHEN 1 THEN '01' WHEN 2 THEN '02' WHEN 3 THEN '03'
            WHEN 4 THEN '04' WHEN 5 THEN '05' WHEN 6 THEN '06'
            WHEN 7 THEN '07' WHEN 8 THEN '08' WHEN 9 THEN '09'
            ELSE CONVERT(nvarchar(2),DAY(CURRENT_TIMESTAMP), 112 ) END
        + '_' +
        REPLACE(REPLACE(CAST(CONVERT(time(7),CURRENT_TIMESTAMP, 109 ) AS nvarchar(14)),':',''),'.','_');
    -- Добавляем к пути и имени файла идентификаторы и расширения
    SELECT @File1 = @FileName + + '01.bak', @File2 = @FileName + + '02.bak', @File3 = @FileName + + '03.bak'
    -- Запускаем резервное копирование в три файла.
    BACKUP DATABASE [ИМЯБАЗЫДАННЫХ] TO
            DISK = @File1,
            DISK = @File2,
            DISK = @File3
    WITH NOFORMAT, NOINIT, SKIP, REWIND, NOUNLOAD, COMPRESSION, STATS = 10

Posted by gladchenko | 1 comment(s)
Filed under: ,

Как справиться с PAGELATCH при больших INSERT-нагрузках

По материалам статьи: "Resolving PAGELATCH Contention on Highly Concurrent INSERT Workloads".
Авторы: Thomas Kejser, Lindsey Allen, Arvind Rao и Michael Thomassy
При участии и с рецензиями: Mike Ruthruff, Lubor Kollar, Prem Mehra, Burzin Patel, Michael Thomassy, Mark Souza, Sanjay Mishra, Peter Scharlock, Stuart Ozer, Kun Cheng и Howard Yin
Перевод Александра Гладченко

Введение

Недавно, мы проводили лабораторные испытания в Microsoft Enterprise Engineering Center, при которых использовалась большая рабочая нагрузка, характерная для OLTP систем. Целью этой лабораторной работы было определить, что случится при увеличении числа процессоров с 64 до 128, при обслуживании Microsoft SQL Server интенсивной рабочей нагрузки (примечание: эта конфигурация была ориентирована на релиз Microsoft SQL Server 2008 R2). Рабочая нагрузка представляла собой хорошо распараллеленные операции вставки, направляемые в несколько больших таблиц.
Рабочая нагрузка масштабировалась до 128 процессорных ядер, но в статистике ожиданий было очень много кратких блокировок PAGELATCH_UP и PAGELATCH_EX. Средняя продолжительность ожидания была десятки миллисекунд, и таких ожиданий было очень много. Такое их количество оказалось для нас неожиданностью, ожидалось, что продолжительность не будет превышать несколько миллисекунд.
В этой технической заметке вначале будет описано, как диагностировать подобную проблему и как для разрешения подобной проблемы использовать секционирование таблиц.

Диагностика проблемы

Когда в sys.dm_os_wait_stats наблюдается большое число PAGELATCH, с помощью sys.dm_os_waiting_tasks можно определить сессию и ресурс, который задача ожидает, например, с помощью этого сценария:

    SELECT session_id, wait_type, resource_description
    FROM sys.dm_os_waiting_tasks
    WHERE wait_type LIKE 'PAGELATCH%'

Пример результата:

В столбце resource_description указаны местоположения страниц, к которым ожидают доступ сессии, местоположение представлено в таком формате:

    <database_id>:<file_id>:<page_id>

Опираясь на значения в столбце resource_description, можно составить довольно сложный сценарий, который предоставит выборку всех попавших в список ожидания страниц:

    SELECT wt.session_id, wt.wait_type, wt.wait_duration_ms
    , s.name AS schema_name
    , o.name AS object_name
    , i.name AS index_name
    FROM sys.dm_os_buffer_descriptors bd
    JOIN (
    SELECT *
      , CHARINDEX(':', resource_description) AS file_index
      , CHARINDEX(':', resource_description
      , CHARINDEX(':', resource_description)) AS page_index
      , resource_description AS rd
    FROM sys.dm_os_waiting_tasks wt
    WHERE wait_type LIKE 'PAGELATCH%'
    ) AS wt
    ON bd.database_id = SUBSTRING(wt.rd, 0, wt.file_index)
    AND bd.file_id = SUBSTRING(wt.rd, wt.file_index, wt.page_index)
    AND bd.page_id = SUBSTRING(wt.rd, wt.page_index, LEN(wt.rd))
    JOIN sys.allocation_units au ON bd.allocation_unit_id = au.allocation_unit_id
    JOIN sys.partitions p ON au.container_id = p.partition_id
    JOIN sys.indexes i ON p.index_id = i.index_id AND p.object_id = i.object_id
    JOIN sys.objects o ON i.object_id = o.object_id
    JOIN sys.schemas s ON o.schema_id = s.schema_id

    
    

Сценарий показал, что ожидаемые страницы относятся к кластеризованному индексу, определённому первичным ключом таблицы с представленной ниже структурой:

    CREATE TABLE HeavyInsert (
    ID INT PRIMARY KEY CLUSTERED
    , col1 VARCHAR(50)
    ) ON [PRIMARY]

Что происходит, почему возникает очередь ожиданий к страницам данных индекса - всё это будет рассмотрено в этой технической заметке.

Основная информация

Чтобы определить, что происходит с нашей большой OLTP-нагрузкой, важно понимать, как SQL Server выполняет вставку в индекс новой строки. При необходимости вставки в индекс новой строки, SQL Server будет следовать следующему алгоритму внесения изменений:

  1. В журнале транзакций создаётся запись о том, что строка изменилась.
  2. Осуществляется поиск в В-дереве местонахождения той страницы, куда должна будет попасть новая запись.
  3. Осуществляется наложение на эту страницу краткой блокировки PAGELATCH_EX, которая призвана воспрепятствовать изменениям из других потоков.
  4. Осуществляется добавление строки на страницу и, если это необходимо, осуществляется пометка этой страницы как "грязной".
  5. Осуществляется снятие краткой блокировки со страницы.

В итоге, страница должна будет быть сброшена на диск процессом контрольной точкой или отложенной записи.
Если же все вставки строк направлены на ту же самую страницу, можно наблюдать рост очереди к этой странице. Даже притом, что краткая блокировка весьма непродолжительна, она может стать причиной конкуренции при высоком параллелизме рабочей нагрузки. У нашего клиента, первый и единственный столбец в индексе являлся монотонно возрастающим ключом. Из-за этого, каждая новая вставка шла на ту же самую страницу в конце В-дерева, пока эта страница не была заполнена. Рабочие нагрузки, которые используют в качестве первичного ключа IDENTITY или другие столбцы с последовательно увеличивающимися значениями, также могут столкнуться с подобной проблемой, если распараллеливание достаточно высоко.

Решение

Всегда, когда несколько потоков получают синхронный доступ к одному и тому же ресурсу, может проявиться описанная выше проблема. Стандартное решение состоит в том, чтобы создать больше ресурсов конкурентного доступа. В нашем случае, таким конкурентным ресурсом является последняя страница В-дерева.
Одним из способов снизить конкуренцию за одну страницу состоит в том, чтобы выбрать в качестве первого столбца индекса другой, не увеличивающийся монотонно столбец. Однако, для нашего клиента это потребовало бы внесения изменений на прикладном уровне в клиентских системах. Мы должны были найти другое решение, которое могло бы ограничиться только изменениями в базе данных.
Помните, что местом конкуренции является одна страница в В-дерева, и конкуренция была бы меньше, если бы было возможно использовать для этого несколько В-деревьев, и в то же время работать только с одной таблицей. К счастью, такая возможность есть, это: Секционированные таблицы и индексы. Таблица может быть секционирована таким способом, чтобы новые строки размещались в нескольких секциях.
Сначала нужно создать функцию и схему секционирования:

    CREATE PARTITION FUNCTION pf_hash (INT) AS RANGE LEFT FOR VALUES (0,1,2)
    CREATE PARTITION SCHEME ps_hash AS PARTITION pf_hash ALL TO ([PRIMARY])

Представленный выше пример использует четыре секции. Число необходимых секций зависит от числа активных процессов, выполняющих операции INSERT в описанную выше таблицу. Есть некоторая сложность в секционировании таблицы с помощью хэш-столбца, например, в том что всякий раз, когда происходит выборка строк из таблицы, будут затронуты все секции. Это означает, что придётся обращаться более чем к одному В-дереву, т.е. не будет отброшенных оптимизатором за ненадобностью ненужных секций. Будет дополнительная нагрузка на процессоры и некоторое увеличение времени ожиданий процессоров, что побуждает минимизировать число планируемых секций (их должно быть минимальное количество, при котором не наблюдается PAGELATCH). В рассматриваемом нами случае, в системе нашего клиента имелось достаточно много резерва в утилизации процессоров, так что было вполне возможно допустить небольшую потерю времени для инструкций SELECT, и при этом увеличить до необходимых объёмов норму инструкций INSERT.
Ещё одной сложностью является то, что нужен дополнительный столбец, по которому будет выполняться секционирование, т.е. на основании значения которого будут распределяться вставки по четырем секциям. Такого столбца изначально в сценарии Microsoft Enterprise Engineering Center не было. Однако, его достаточно просто было создать. Для этого использовался тот факта, что столбец ID монотонно увеличивается с приращением равным единице, и здесь легко применима довольно простая хеш-функция:

    CREATE TABLE HeavyInsert_Hash(
      ID INT NOT NULL
      , col1 VARCHAR(50)
      , HashID AS CAST(ABS(ID % 4) AS TINYINT) PERSISTED NOT NULL)

С помощью столбца HashID, вставки в четыре секции будут выполняться циклически.
Создаём кластеризованный индекс следующим образом:

    CREATE UNIQUE CLUSTERED INDEX CIX_Hash
    ON HeavyInsert_Hash (ID, HashID) ON ps_hash(HashID)

Используя новую схему таблицы с секционированием вместо первоначального варианта таблицы, мы сумели избавиться от очередей PAGELATCH и повысить скорость вставки. Этого удалось достичь за счёт балансировки вставки между несколькими секциями и высокого параллелизма. Вставка происходит в несколько страниц, и каждая страница размещается в своей собственной структуре В-дерева. Для нашего клиента удалось повысить производительность вставки на 15 процентов, и справится с большой очередью PAGELATCH к горячей странице индекса одной таблицы. Но при этом удалось также оставить достаточно большой резерв циклов процессоров, что оставило возможность сделать аналогичную оптимизацию для другой таблицы, тоже с высокой нормой вставки.
Строго говоря, суть этой уловки в оптимизации логической схемы первичного ключа таблицы. Однако, потому что ключ просто стал длиннее на величину хеш-функции относительно изначального ключа, дубликатов для столбца ID удалось избежать. Уникальные индексы по единственному столбцу таблицы зачастую становятся причиной проблем с очередями PAGELATCH. Но даже если эту проблему удастся устранить, у таблицы может оказаться другой, некластеризованный индекс, который будет подвержен той же самой проблеме. Как правило, проблема наблюдается для уникальных ключей на единственном столбце, где каждая вставка попадает на одну и ту же страницу. Если и у других таблиц индексы подвержены проблеме с PAGELATCH, можно применить эту же уловку с секционированием к индексам таких таблиц, используя такой же ключ с хэшем в качестве первичного ключа.
Не всегда возможно внести изменения в приложение, особенно, если оно является плодом усилий третьих фирм. Но если изменение запросов возможно, становится доступной их оптимизация за счёт добавления к ним условий фильтрации по предикатам первичного ключа.

Пример: Чтобы отбросить ненужные секции, можно внести следующие изменения в сценарий:

    SELECT * FROM HeavyInsert_Hash
    WHERE ID = 42

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

    SELECT * FROM HeavyInsert_Hash
    WHERE ID = 42 AND HashID = 42 % 4

Исключение оптимизатором ненужных секций по значению хэша не будет вам ничего стоить, если только не считать большой платой за это увеличение на один байт каждой строки кластеризованного индекса.

Posted by gladchenko | 4 comment(s)
Filed under:

Query Processor Modeling Extensions

По материалам стать Иан Джоз (Ian Jose): Query Processor Modelling Extensions in SQL Server 2005 SP1
Перевод Александра Гладченко
Редактура Алексея Халако

В этой статье Иан рассказывает про флаг трассировки T2301, который включает более тонкую оптимизацию запросов, что бывает полезно для больших баз данных с нагрузкой, характерной для систем поддержки принятия решений. В тестах TPC-H этот флаг используется очень часто и разными вендорами.

Появившееся в SQL Server 2005 SP1 расширение оптимизатора Query Processor Modelling Extensions можно включить с помощью флага трассировки 2301. Это расширение обеспечивает возможность системы моделирования оптимизатора запросов выбирать более производительные планы исполнения сложных запросов к базе данных. Улучшенное моделирование планов запроса в некоторых случаях может привести к существенному повышению производительности исполнения запросов. Однако, эти расширения моделирования процессора запросов, могут привести к заметному увеличенному времени компиляции, и поэтому рекомендуются для использования только в тех приложениях, в которых компиляций бывает немного, и они происходят нечасто. Были добавлены следующие расширения моделирования:
  • Целочисленное моделирование (Integer Modelling)

    Обычно, моделирование предполагает, что между кодовыми точками чисел двойной ширины значения шага гистограммы распределены одинаково. Расширение этой модели запоминает, что значения могут встречаться только для целочисленных кодовых точек, и за счёт этого оценки множества элементов для фильтров неравенства выполняются точнее.

  • Углублённое использование гистограммы (Comprehensive Histogram Usage)

    Обычно, гистограммы игнорируются, когда количество элементов отношения оказывается меньше числа шагов в гистограмме. Это - логика, которая определяет вероятность того, что гистограмма и далее будет описывать отношение. Это расширение применяет гистограмму в оценке количества элементов независимо от оценки количества элементов для отношения.

  • Предположение основного ограничения (Base Containment Assumption)

    Обычно, при соединении двух отношений мы предполагаем, что для Х определенных кодовых точек (которые относятся к тому же самому ключевому диапазону, что и отношение R) при соединении с Y уникальных точек того же самого ключевого диапазона, что и в отношении S, всегда найдётся пара MIN(X,Y). Такое допущение называют "Простое ограничение". Мы предполагаем, что наименьшее число из уникальных точек кода соотносится с точками кода на другой стороне. Это моделирование игнорирует относительную совокупность уникальных точек кода в основных формах R и S, и также игнорирует любую фильтрацию, которая была применена к основными формами R и S перед соединением. Основное ограничение основывается на предположение, что ограничение применяется только к основным отношениям и использует вероятностные методы вычисления степени соединения. Кроме того, подразумевается, что модели фильтров применены правильно, так как их поведение очень отличается от ортогональных фильтров.

  • Углублённое изменение отображения плотности (Comprehensive Density Remapping)

    Плотность (Density) - это мера числа дубликатов значений для каждого уникального значения. Обычно, после CONVERT-тации столбцов довольно незначительное число плотностей для этих столбцов заново отображается как новые определения столбцов. Обратите внимание, что такие операции, как конвертация довольно редко меняют плотность столбца. Когда задействовано это расширение моделирования, все такие повторные отображения будут обязательно сделаны, что делает возможным последующее использование плотности для целей оценки количества элементов. В некоторых случаях, это может привести к чрезмерной утилизации памяти.

  • Углублённое согласование плотности (Comprehensive Density Matching)

    Обычно, плотности согласованы, когда те же самые основные столбцы задействованы в фильтре или соединении. При использовании этого расширения моделирования, применяется понятие эквивалентности столбцов как результат экви-соединений, что ведёт к более точному согласованию плотности и, в итоге, к лучшей оценке количества элементов.

Все эти расширения были разработаны в ответ на обращения наших клиентов, у которых возникали проблем с отношениями, вследствие которых использовались неудачные планы исполнения запросов. Если наблюдается использование неудачных планов исполнения, для которых применение одного или более из описанных выше расширений может помочь поднять производительность, тогда есть смысл включить флаг трассировки 2301. Важно понимать, что время компиляции от этого увеличится, и в некоторых случаях утилизация памяти может возрасти очень сильно. Таким образом, к применению этого флага трассировки нужно подходить осторожно и обязательно проводить дотошное тестирование перед применением его в промышленной среде.

Posted by gladchenko | 1 comment(s)
Filed under:

Флаги трассировки, рекомендованные Майкрософт для эталонного теста производительности TPC-E

По материалам статьи: Microsoft SQL Server 2008 TPC-E Trace Flags
Вольный перевод Александра Гладченко

Наиболее часто используемым способом изменения поведения SQL Server является выставление флагов трассировки. Следующие флаги трассировки поддерживаются в настоящее время Майкрософт для публикации результатов тесов производительности TPC-E. Если сотрудники Майкрософт рекомендуют Вам использовать другие флаги трассировки, которые не представлены в списке ниже, пожалуйста, сообщите об этом Джейми Редингу (Jamie.Reding@Microsoft.com) или Чарльзу Левину (Charles.Levine@Microsoft.com) до того, как вы опубликуете использование этих флагов.
Единственными поддерживаемыми для SQL Server 2008 флагами трассировки для TPC-E являются флаги: -T661 -T834 -T3502 -T8744.
Единственным поддерживаемыми для SQL Server 2008 параметрами запуска сервера для теста TPC-E являются параметры: -c -E -x, которые хорошо описаны в BOL.

661: Disable the ghost record removal process

Флаг трассировки 661 отключает системный процесс удаления фантомных записей. Фантомные записи появляются в результате исполнения операций удаления, после которых удалённые записи могут оставаться в файле как фантомные записи. Через некоторое время, удаленные записи вычищаются процессом удаления фантомных записей. Когда этот процесс отключается, удаленные записи не вычищаются. Поэтому, место, которое занимают удаленные записи, не высвобождается. Это влияет на занимаемое данными место и на производительность операций просмотра.
Флаг трассировки 661 всегда действует в контексте всего сервера, т.е. имеет глобальный контекст. Вы можете включать флаг трассировки 661 при запуске сервера или в пользовательском сеансе.

834: Use Microsoft Windows large-page allocations for the buffer pool

Флаг трассировки 834 применяется в SQL Server 2008 для включения механизма распределения буферному пулу больших страницы памяти, которыми умеют оперировать последние версии Microsoft Windows. У разных аппаратных платформ может быть разный размер страниц, он может изменяться от 2 до 16 Мбайт. Большие страницы распределяются при запуске и сохраняются на протяжении всей жизни процесса. Флаг трассировки 834 повышает производительность, увеличивая эффективность TLB буфера процессоров.
Флаг трассировки 834 применим только к 64-битным версиям SQL Server 2008. Включить флаг трассировки 834 может только та учётная запись, для которой разрешена локальная политика "Lock pages in memory". Включать флаг трассировки 834 можно только при запуске SQL Server.
Флаг трассировки 834 может препятствовать запуску сервера, если память сильно фрагментирована и это мешает распределению больших страниц. Поэтому, флаг трассировки 834 безопаснее использовать на серверах, которые обслуживают только SQL Server 2008.
Для получения более подробной информации о поддержке больших страниц Windows, перейдите на следующую страницу сайта Microsoft Developer Network (MSDN): Large-Page Support.

3502: Log Database Checkpoint Start and End times in the SQL Server ErrorLog

Флаг трассировки 3502 не влияет на производительность, но он нужен для контроля выполнения эталонных тестов TPC. Этот флаг трассировки заставляет SQL Server регистрировать в SQL Server ErrorLog время начала и окончания работы системного процесса контрольной точки.

8744: Disable pre-fetching for ranges

Флаг трассировки 8744 отключает предварительную выборку для таких операторов, как "Nested Loops". Неуместное использование этого флага может спровоцировать дополнительные физические чтения, при реализации плана с оператором "Nested Loops".
Когда флаг трассировки 8744 включён при запуске сервер, он получает глобальный контекст. Когда он включен в сеансе пользователя, контекст ограничивается сеансом.

Posted by gladchenko | 1 comment(s)
Filed under:

Флаги трассировки, рекомендованные Майкрософт для эталонного теста производительности TPC-C

По материалам статьи: Microsoft SQL Server 2005 TPC-C Trace Flags
Вольный перевод Александра Гладченко

Наиболее часто используемым способом изменения поведения SQL Server является выставление флагов трассировки. Следующие флаги трассировки поддерживаются в настоящее время Майкрософт для публикации результатов тесов производительности, таких как TPC-C. Если сотрудники Майкрософт рекомендуют Вам использовать другие флаги трассировки, которые не представлены в списке ниже, пожалуйста, сообщите об этом Джейми Редингу (Jamie.Reding@Microsoft.com) или Чарльзу Левину (Charles.Levine@Microsoft.com) до того, как вы опубликуете использование этих флагов.

652: Disable page pre-fetching
Флаг трассировки 652 отключает предварительную выборку страниц во время полных просмотров. Если флаг трассировки 652 включается при запуске SQL Server, он получает глобальный контекст. Если он взводится в пользовательском сеансе, то контекст ограничивается сеансом.

661: Disable the ghost record removal process
Флаг трассировки 661 отключает системный процесс удаления фантомных записей. Фантомные записи появляются в результате исполнения операций удаления, после которых удалённые записи могут оставаться в файле как фантомные записи. Через некоторое время, удаленные записи вычищаются процессом удаления фантомных записей. Когда этот процесс отключается, удаленные записи не вычищаются. Поэтому, место, которое занимают удаленные записи, не высвобождается. Это влияет на занимаемое данными место и на производительность операций просмотра.
Флаг трассировки 661 всегда действует в контексте всего сервера, т.е. имеет глобальный контекст. Вы можете включать флаг трассировки 661 при запуске сервера или в пользовательском сеансе.

834: Use Microsoft Windows large-page allocations for the buffer pool
Флаг трассировки 834 применяется в SQL Server 2005 для включения механизма распределения буферному пулу больших страницы памяти, которыми умеют оперировать последние версии Microsoft Windows. У разных аппаратных платформ может быть разный размер страниц, он может изменяться от 2 до 16 Мбайт. Большие страницы распределяются при запуске и сохраняются на протяжении вей жизни процесса. Флаг трассировки 834 повышает производительность, увеличивая эффективность TLB буфера процессоров.
Флаг трассировки 834 применим только к 64-битным версиям SQL Server 2005. Включить флаг трассировки 834 может только та учётная запись, для которой разрешена локальная политика "Lock pages in memory". Включать флаг трассировки 834 можно только при запуске SQL Server.
Флаг трассировки 834 может препятствовать запуску сервера, если память сильно фрагментирована и это мешает распределению больших страниц. Поэтому, флаг трассировки 834 безопаснее использовать на серверах, которые обслуживают только SQL Server 2005.
Для получения более подробной информации о поддержке больших страниц Windows, перейдите на следующую страницу сайта Microsoft Developer Network (MSDN): Large-Page Support.

836: Use the max server memory option for the buffer pool
Флаг трассировки 836 нужен для того, чтобы размер буферного пула при запуске SQL Server 2005 устанавливался на основе значения параметра глобальной конфигурации "max server memory", вместо того, чтобы использовать в качестве ориентира размер физической памяти. Вы можете использовать флаг трассировки 836, чтобы уменьшить число описателей буфера, которые распределяются при запуске службы в 32-разрядном режиме Address Windowing Extensions (AWE). Это позволяет предоставить в памяти больше места для заимствованных из буферного пула страниц.
Флаг трассировки 836 применяется только с 32-разрядным версиям SQL Server 2005, которым разрешено распределение страниц через окно AWE. Включить флаг трассировки 836 можно только при запуске.

1228 и 1229 - Enable lock partitioning and disable lock partitioning
По умолчанию, секционирование блокировок становится возможным, когда сервер имеет 16 или более процессоров. Иначе, секционирование блокировок заблокировано. Флаг трассировки 1228 включает секционирование блокировок для двухпроцессорных и более систем. Для отключения секционирования блокировок используется флаг трассировки 1229.
Секционирование блокировок полезно на мультипроцессорных серверах, где для некоторых таблиц очень высоки нормы блокировок. Включить флаги трассировки 1228 и 1229 можно только при запуске.

2301: Enable advanced decision support optimizations
Флаг трассировки 2301 включает дополнительную оптимизацию, которая улучшает работу запросов систем поддержки принятия решений, характерных большим объёмом затрагиваемых запросом данных.
Когда флаг трассировки 2301 включается при запуске сервера, он получает глобальный контекст, иначе, он будет иметь контекст сеанса.

Флаги трассировки, которые отключают некоторые кольцевые буферы

Кольцевой буфер - внутренний диагностический механизм SQL Server 2005, который может использоваться для сбора дополнительной информации о сервере. Как правило, эту информацию используют для исследования проблем в работе сервера. Посмотреть накапливаемую кольцевыми буферами информацию можно с помощью динамического административного представления sys.dm_os_process_memory.
Отключение кольцевых буферов в целом приводит к повышению производительности. Однако, отключение кольцевых буферов исключает возможность использования диагностической информации для передачи в службу поддержки Microsoft и может помешать успешному поиску решения возможных проблем.
Представленные ниже флаги трассировки отключают разные кольцевые буферы.

8011: Disable the ring buffer for Resource Monitor
Флаг трассировки 8011 отключает сбор дополнительной диагностической информации, относящейся к Resource Monitor. Информация этого кольцевого буфера можете использовать для диагностики состояния исчерпания памяти. Флаг трассировки 8011 всегда применяется ко всему серверу и имеет глобальный контекст. Вы можете включить флаг 8011 при запуске или в сеансе пользователя.

8012: Disable the ring buffer for schedulers
Флаг трассировки 8012 отключает кольцевой буфер планировщиков. Информация этого кольцевого буфера можете использовать для диагностики проблем планирования потоков процессорам. Например, информацию этого кольцевого буфера можно использовать для выявления проблем, проявляющихся как потеря отклика от SQL Server 2005. Включать флаг трассировки 8012 можно только при запуске сервера.

8018 and 8019: Disable the exception ring buffer and disable stack collection for the exception ring buffer Этот кольцевой буфер хранит данные о последних 256 исключениях, которые были зафиксированы для узла. Каждая запись содержит немного информации об ошибке и трассировку стека. Запись добавляется кольцевым буфером в момент срабатывания исключения.
Флаг трассировки 8018 отключает создание кольцевого буфера, и после этого информация об исключения не сохраняется. Флаг трассировки 8019 отключает сбор трассировок стека, когда фиксируется информация об исключении. Флаг 8019 нельзя использовать без предварительного включения флага трассировки 8018. Отключение этого кольцевого буфера затрудняет диагностику связанных с проблемами исключений, т.е. с внутренними ошибками сервера. Включить флаги трассировки 8018 и 8019 можно только при запуске сервера.

8020: Disable working set monitoring
SQL Server 2005 использует информацию о размере рабочего множества, когда возникает необходимость интерпретировать глобальные сигналы операционной системы о состояния оперативной памяти. Флаг трассировки 8020 исключает из этой интерпретации информацию о размере рабочего множества. Этот флаг трассировки применяется только к обычным страницам памяти. Например, этот флаг трассировки не относится к заблокированным страницам или большим страницам.
Флаг трассировки 8020 может быть полезен, когда операционная система постоянно урезает рабочее множество обслуживающего SQL Server 2005 сервера, и когда сервер неспособен высвобождать память из-за большой рабочей нагрузки. В таком случае, попытки мониторинга приведут к напрасному расходованию циклов процессора. Флаг трассировки 8020 нужно использовать с осторожностью и только после глубокого тестирования на реальных рабочих нагрузках. Кроме того, использовать флаг трассировки 8020 нужно после тщательного выбора значения глобального параметра "max server memory". Неуместное использование этого флага может привести к сильным листаниям.
Включить флаг трассировки 8020 можно только при запуске сервера.

8744: Disable pre-fetching for ranges
Флаг трассировки 8744 отключает предварительную выборку для таких операторов, как "Nested Loops". Неуместное использование этого флага может спровоцировать дополнительные физические чтения, при реализации плана с оператором "Nested Loops".
Когда флаг трассировки 8744 включён при запуске сервер, он получает глобальный контекст. Когда он включен в сеансе пользователя, контекст ограничивается сеансом.

Posted by gladchenko | 1 comment(s)
Filed under:

Настройка Windows Server 2008/2003 x64 для обслуживания SQL Server 2008

По состоянию на 2009 год

Эта статья - вольная интерпретация рекомендаций: Microsoft, IBM, HP, Dell, QLogic, LSI, EMC, ACER, Bull, Fujitsu, Hitachi, NEC и Unisys. Некоторые рекомендуемые настройки требуют отдельного, обстоятельного разговора, и потому не включены в эту статью, а найти эти рекомендации можно в моём блоге.

Материал подготовил Александр Гладченко.

Обновление установки Windows

После установки Windows Server 2008 x64, установите последний сервисный пакет обновлений (Service Pack), обязательно установите выходившие после сервисного пакета исправления безопасности и последние версии драйверов и аппаратных прошивок.

Настройка файла подкачки Windows

Размер файла подкачки Windows в случае размещения на сервере только SQL Server не играет такой важной роли, как в типовых сценариях. SQL Server старается избегать листания. Размер файла подкачки можно выбрать небольшим, чтобы его было достаточно для формирования мини-дампов. Если сервер также обслуживает приложения, которые нуждаются в файле подкачки, размер его стоит выбирать в полтора раза больше, чем размер физической памяти сервера, но не более 50Гб. В случае монопольного владения ресурсами SQL Server, размещать файл подкачки можно на том же диске, где базируется операционная система. Минимальная активность работы с файлом подкачки не будет создавать конкуренцию другим задачам. В Windows Server 2003 имеется несколько предлагаемых вариантов настройки файла подкачки, он может настраиваться жёстко, может отсутствовать вообще или его размер будет управляться операционной системой. В последнем случае, Windows создаст файл подкачки, размер которого на один Мегабайт будет превышать размер физической памяти сервера. Такой выбор обусловлен тем, что ровно столько места необходимо для создания полного дампа памяти после аварии с выдачей "синего экрана". Если вас такой дамп не интересует, можно взять управление размером файла подкачки в свои руки.

Настройка размера системных журналов

Размеры системных журналов стоит выбирать такими, чтобы их открытие для анализа или копирование по сети выполнялось за приемлемое время.

Настройка локальных политик Windows

Ограничьте размер системных дампов физической памяти размером 64 Гб, большой размер дампа непрактичен с точки зрения отладки. Можно оптимизировать управление памятью и существенно сократить листания с помощью установки локальной политики для пользователя, от имени которого запускается служба SQL Server. Политика называется "Lock pages in memory", располагается она в узле "User rights assignment" локальных политик. Оболочку менеджера политик можно вызвать из командной строки, набрав там gpedit.msc и нажав ввод.

Ещё одной полезной локальной пользовательской политикой является мгновенная инициализация файла данных: "Выполнение задач обслуживания тома" (Perform Volume Maintenance). Для файлов журналов эта политика неприменима. Однако, включение этой политики хоть и позволяет сделать процесс создания файла данных, его приращение или восстановление практически мгновенным, оно чревато повышением рисков безопасности, т.к. инициализация не будет сопровождаться перезаписью новых файлов или приращиваемых к ним областей нулями. Т.е. существует риск, что расположенная в этих областях информация будет доступна для чтения и к удаленной ранее информации смогут получить доступ неавторизированные участники. Дополнительную информацию по безопасности этой операции можно найти в главе SQL Server Books Online: "Инициализация файлов базы данных". Одной из альтернатив этому методу, также очень быстро позволяющей выполнять задачи создания и приращения файлов, является размещение файлов баз данных на "сырых" разделах (RAW).

Настройка Панели Управления Windows

Чтобы исключить снижение производительности в режиме энергосбережения, в оснастке "Электропитание" (Power Options) нужно выбрать режим "Высокая производительность" (High Performance). Стоит также убедиться, что сервер никогда не будет переключаться в спящий режим.

На закладке "Визуальные эффекты" можно отключить расходование процессорного времени на некритичные для работы сервера эффекты, выбрав режим "Обеспечить наилучшее быстродействие" (Adjust for best performance).

Настройка конфигурации системы

Отключите контроль учётных записей UAC. Для этого нажмите кнопку "Пуск" (Start), в списке "Программы" (All Programs) перейдите к папке "Администрирование" (Administrative Tools) и запустите утилиту "Конфигурация системы" (System Configuration). Отключить UAC можно на закладке "Сервис" (Tools), выбрав из списка средств задачу: "Отключите контроль учётных записей UAC". Для применения изменений необходима перезагрузка системы.

Настройка кэширования ввода-вывода

Для включения кэширования операционной системой операций ввода-вывода логических дисков воспользуйтесь оснасткой Disk Management или Device Manager, перейдя в ней в узел Disk Drives. Для каждого настраиваемого устройства логического диска нужно выбрать Свойства (Properties) и перейти на закладку Policies. Для индивидуальной настройки дисков лучше подходит оснастка Disk Management, там настройки выполняются из свойств дисков, которые вызываются в графической, нижней части окна оснастки. Включение чекбокса "Enable write caching on the disk" разрешает кэширование записи на диск. После пометки этого чекбокса становится доступен для пометки второй чекбокс: "Enable advanced performance". Включение обеих чекбоксов не только разрешает кэширование, но и заставляет операционную систему изымать из запросов ввода-вывода команды прямой записи на диск и сброса дискового кэша. Не рекомендуется включать эти чекбоксы если аппаратные кэши не имеют защиты от потери электропитания.

Если логический диск представлен внешним дисковым массивом, который оснащён аппаратным кэшем с батарейкой, можно не помечать второй чекбокс: "Enable advanced performance". Однако, пометка этого чекбокса может повысить производительность записи в журнал транзакций.

Для вступления в силу изменений настроек кэша дисков перезагрузки сервера не требуется.

Настройка параметров сетевых плат

В окне Local Area Connection Properties мастера Network Connections панели управления Windows можно вызвать окно параметров сетевой платы, если нажать кнопку Configure. В этом окне на закладке Advanced перечислены несколько параметров, некоторые из них могут существенно влиять на производительность обмена данными по сети.

Link Speed and Duplex

Лучшим решением зачастую является согласованный выбор полнодуплексного режима связи в свойствах сетевой платы и в свойствах порта сетевого коммутатора/концентратора. Это не означает, что выбор автоматической подстройки там и тут будет работать хуже, просто история помнит такие случаи…

Существует ещё ряд распространённых параметров, настройка которых может помочь поднять производительность сетевого интерфейса (например, Receive Buffers, Coalesce Buffers, Offload features и т.п.). Чтобы выдрать верные значения для подобных параметров, обратитесь к документации производителя сетевой платы.

Для того чтобы исключить возможность отключения сетевого интерфейса в целях оптимизации энергопотребления сервера, стоит на закладке Power Management убрать пометку чекбокса "Allow the computer to turn off this device to save power".

Отключение неиспользуемых протоколов

Для того чтобы убрать незначительную дополнительную нагрузку, создаваемую для обслуживания неиспользуемых сетевых протоколов, предлагается отключить или деинсталлировать такие протоколы. Например, первым кандидатом на отключение является TCP/IPv6, в силу пока ещё своей малой распространённости. Сделать это можно в окне Local Area Connection Properties мастера Network Connections панели управления Windows.

SQL Server также использует по умолчанию несколько протоколов, отключить лишние из них позволяет оснастка SQL Server Configuration Manager, в которой протоколы перечислены в узле "Сетевая конфигурация SQL Server".

В большинстве случаев и в свойствах сетевого интерфейса и в протоколах SQL Server достаточно ограничится использованием протокола TCP/IPv4.

Если же требуется поддерживать несколько протоколов, стоит задать приоритеты использования протоколов и их провайдеров в окне Advanced Settings мастера Network Connections.

Настройка антивирусного программного обеспечения

В тех случаях, когда операционная среда или бизнес - требования вынуждают устанавливать на обслуживающем службы SQL Server сервере антивирусное программное обеспечение, настраивать антивирусное ПО нужно так, чтобы обновление вирусных сигнатур и сканирование системы происходило во время низкой активности SQL Server.

Необходимо настроить исключение сканирования файлов баз данных, журналов транзакций и резервных копий, которые типично имеют разрешения: mdf, ldf, ndf, bak и trn. Это позволит предотвратить повреждение этих файлов при попытке со стороны SQL Server их открытия, когда они уже открыты для проверки антивирусным ПО. Кроме того, необходимо принять меры для защиты каталогов полнотекстового поиска и содержащих данные Analysis Services от повреждений, связанных с активностью антивирусного программного обеспечения. Исключите также папку журналов SQL Server (MSSQL\Log), журнал ошибок открыт постоянно и в него может выводиться много событий. Если антивирусное ПО планируется использовать совместно с SQL Server работающем в кластере, нужно исключить сканирование кворум - диска и каталога: "c:\Windows\Cluster". Для получения более подробной информации о требованиях к настройкам антивирусного ПО обратитесь к статье базы знаний Майкрософт: Guidelines for choosing antivirus software to run on the computers that are running SQL Server.

Во избежание вмешательства в работу служб SQL Server и для предотвращения увеличения времени их запуска из-за сканирования антивирусным ПО, рекомендуется исключить из проверки службы SQL Server. Наиболее распространенными в использовании являются службы: sqlservr.exe, sqlagent.exe, sqlbrowser.exe и sqlwriter.exe

Дополнительные рекомендации: Рекомендации по использованию антивирусных программ для компьютеров под управлением Windows Server 2003, Windows 2000 и Windows XP.

Управление памятью в SQL Server

SQL Server, при необходимости, старается заполучить всю доступную ему оперативную память компьютера. Если установлено несколько экземпляров SQL Server, вероятна конкуренция за ресурсы памяти между менеджерами динамической памяти каждого экземпляра. До появления в SQL Server 2008 средств регулировки ресурсов, единственной возможностью снижения конкуренции за память между разными экземплярами была установка в глобальной конфигурации экземпляра SQL Server ограничений для максимального и минимального объемов используемой физической памяти. Кроме того, хорошей практикой считается оставлять не менее 10% оперативной памяти для нужд операционной системы.

Настройка параметров глобальной конфигурации SQL Server

affinity I/O mask

Этот параметр глобальной конфигурации позволяет изолировать обслуживание ввода-вывода данного экземпляра SQL Server одним или несколькими ядрами процессоров. Привязка ввода-вывода позволяет закрепить за вводом-выводом фиксированное число планировщиков. По умолчанию и в большинстве случаев маску привязки процессоров к вводу-выводу устанавливают в ноль, разрешая SQLOS самой управлять подобной привязкой. Чаще всего это позволяет получить максимальную производительность, однако, для некоторых типов рабочей нагрузки (которым характерны