Главная SQL, Без рубрики, Новое SQL, SQL 2008 R2, Конкурс
  • Итоги конкурса: Microsoft SQL Server 2008 R2

    AND-THE-WINNER-IS-OSCAR-37748Спустя месяц обсуждений и голосований мы с удовольствием подводим итоги конкурса посвященного Microsoft SQL Server 2008 R2.

    От лица организаторов конкурса прощу прощения у авторов за перенос сроков окончания конкурса и столь длительный процесс подведения итогов.

    Данный конкурс мы проводили совместно с порталом sql.ru при поддержке компании Microsoft. Конкурсные статьи номинировались в трех категориях:

  • Главная SQL, Без рубрики, Новое SQL, SQL 2008 R2
    • Обработка ошибок и формирование сообщений об ошибках для баз данных Microsoft SQL Server 2005/2008

      errorreportДля программ, работающих с БД, важным является не только корректность обработки ошибок БД, но и формирование информативных сообщений об этих ошибках. Наличие таких сообщений позволяет быстрее выявлять причины ошибок изменения данных БД и их исправлять. Особенно это актуально для пользователей программных продуктов, которым в большинстве случаев может быть не известна не только структура конкретной БД, но и теоретические основы реляционных баз данных. Хотя Microsoft SQL Server и формирует довольно подробные сообщения об ошибках ограничений базы данных, но при непосредственной передаче их пользователям в большинстве случаев, сложно рассчитывать на то, что ему удастся выявить по такому сообщению причину ошибки. Можно выделить несколько основных причин возникновения такой ситуации:

    • Главная SQL, Без рубрики, Новое Scripts, SQL, SQL 2008 R2
      • Безопасные скрипты по-новому


        imagesCACUV4IU

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

      • Главная SQL, Без рубрики, Новое Scripts, SQL, SQL 2008 R2
        • Делаем простые вещи сложными или безопасные скрипты

          vert_scriptsПри разработке приложений программистам частенько приходится писать SQL скрипты. С одной стороны, это хорошо. Программист специализируется на некоторой части функционала приложения, знает как устроена соответствующая часть базы данных, знает что ему нужно изменить. Он – именно тот человек, который может сделать изменения наиболее быстро. Даже если в компании есть специализированный отдел по базам данных, на то чтобы привлечь специалиста, объяснить что нужно и дождаться результата – уйдет значительно больше времени. С другой стороны, скрипты программистов часто оставляют желать лучшего. Для многих программистов SQL – дополнительный язык, они знают его хуже основного.

          Также при современной разработке часто используются build –системы. Одним из шагов такой системы может быть применение скриптов к базе данных. Что означает, что одни и те же скрипты будут вызываться снова и снова. А значит их нельзя писать в произвольном стиле, скрипты должны быть пере-запускаемыми (re-runnable) или их еще называют безопасными (safe-style).

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

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

          Несколько оговорок. Первое, наши скрипты мы будем писать для Microsoft SQL Server. Второе, процесс разработки очень сильно различается от компании к компании. Я буду ориентироваться на некоторую абстрактную большую компанию, выбирая лучшее из своей практики. Что-то может быть лишним для вас или чего-то может недоставать. Смело присылайте мне свои соображения.

          Прежде всего, несколько общих замечаний.

          1. Заголовок скрипта.

            Каждый уважающий себя скрипт должен иметь заголовок. В заголовке должно быть написано кто написал скрипт, зачем и когда. Автора нужно указывать, чтобы потом его можно было найти, если со скриптом возникнут проблемы. Цель добавления скрипта (имя проекта, номер тикета и так далее) поможет найти человека, кто отвечает за этот проект, чтобы уточнить неясные моменты. Дату создания скрипта можно не указывать, но иногда она бывает полезна чтобы составить представление об актуальности скрипта. Итого, заголовок может выглядеть так:

          2. –Description: Adding two more client profiles
            –Ticket No : D0012345
            –Author : Pupkin Ivan
            –Created on : 19/08/2010

            Отдельно надо указать, что НЕ надо писать в заголовках. Не надо писать копирайт. Это ценное знание никак не поможет DBA. Также не надо приводить полный или частичный текст лицензии. Это хороший способ увеличить скрипт на килобайт-другой, но смысла не имеет никакого.

          3. Начало и конец скрипта должны быть обозначены оператором PRINT.Билд-система будет запускать скрипты один за другим. Или хитрый DBA слепит из всех скриптов один мега-скрипт и будет запускать его. Когда что-то пойдет не так, скрипт выдаст ошибку. Часто есть возможность получить текстовый вывод от запуска интерпретатора. Если начало и конец каждого скрипта обозначены, найти скрипт-источник ошибки легко. Если нет – придется решать ребусы. Итого – помещаем в начало и конец скрипта что-то вроде:
          4. print ‘BEGIN D0012345 script’
            print ‘END D0012345 script’

            Не надо писать строки вроде «Такого-то числа такого-то месяца, я, разработчик Василий, с трепетом в душе и верой в Бога, начинаю обработку пользовательских документов». DBA будет искать напечатанную строку в файлах скриптов, и если по строке можно найти ваш файл, это уже достаточно хорошо.

          5. Печатайте сообщения, когда ваш скрипт достиг (или не достиг) каких-то важных результатов. Когда ваш скрипт упадет на production сервере, и вам пришлют лог его выполнения и ошибку, будет гораздо легче разобраться в том, что же пошло не так, если вы делали правильные PRINT.
          6. if not exists(select …)
            begin
              <do something>
              PRINT‘do completed successfully’
            end
            else
              PRINT‘do has been already done’

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

          7. Три разные сущности реализуют концепцию уникальности строк в таблице – это первичный ключ, ограничение UNIQUE (иногда его называют альтернативным ключом) и уникальный индекс. Ключи физически реализуются с помощью индексов. Есть логическое различие, отличающее одно понятие от другого, но для наших целей эта разница не важна. Иногда для краткости я буду упоминать только первичный ключ, хотя на самом деле это может быть и UNIQUE и уникальный индекс.
          8. Внешние ключи и триггеры могут существенно разнообразить жизнь написателя скриптов. Я исхожу их того, что девелоперским скриптам не надо производить действия, вызывающие нарушение внешнего ключа, а триггеров либо нет, либо их работа абсолютно прозрачна.

          Три кита

          Теперь о самих скриптах, которыми мы будем изменять данные. У нас есть три оператора для изменения данных. Это INSERT, UPDATE и DELETE. Рассмотрим их по очереди.

          INSERT

          В таблицу можно добавить сколько угодно строк, если они не нарушают первичного ключа, ограничений UNIQUE и уникальных индексов. Можно создать таблицу, которая не будет содержать ничего из вышеперечисленного. В такую таблицу можно вставлять сколько угодно строк и не получить ни одой ошибки. Такие таблицы используются при операциях с данными, но не для постоянного хранения данных. Как правило, таблица имеет как минимум первичный ключ. Если вы попытаетесь добавить в таблицу строку с таким значением ключа, который уже есть в таблице, произойдет ошибка. Однако, есть подводные камни. Например, если в качестве первичного ключа используется тип данных DATETIME или автоинкрементное поле (IDENTITY) и на таблице больше не объявлено ни уникальных индексов, ни ограничений UNIQUE. Очень сложно представить себе ситуацию, когда произойдет нарушение первичного ключа в таком случае. Штамп времени меняется каждую долю секунды, автоинкрементное поле послушно выдает увеличенное на шаг значение. Такие ключи называются суррогатными, и если в таблице есть только суррогатный первичный ключ, для наших целей это то же самое, что не иметь первичного ключа вообще.

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

          if not exists(select 1 from dbo.Table1 where PK = 10)
            INSERT INTO dbo.Table1 VALUES (10,…)

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

          if not exists( select top 1 1 from dbo.Table1 where nonPK = ‘abc’)
            INSERT INTO dbo.Table1 VALUES (…)

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

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

          create table #tmp(ID int, Name nvarchar(50))

          insert into #tmp values (10, ‘aaa’)
          insert into #tmp values (11, ‘bbb’)

          INSERT INTO dbo.Table1
          SELECT *
          FROM #tmp t
            left join dbo.Table1 t1
            on t.ID = t1.ID
          WHERE
            t1.ID is null

          drop table #tmp

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

          INSERT INTO dbo.Table1
          SELECT *
          FROM #tmp
          WHERE
            ID not in (SELECT ID FROM dbo.Table1)

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

          UPDATE

          Казалось бы, оператор обновления изначально является безопасным. Одну и ту же строку можно обновлять множество раз, без всяких ошибок. Но и здесь есть нюансы. Безопасно обновление только неключевых полей по ключевым. Такой оператор безопасен:

          UPDATE dbo.Table1
          SET Name =‘abc’
          WHERE ID = 10

          Маленькое замечание – если скрипт запускается многократно, нет смысла обновлять снова и снова то, что уже было обновлено. Эту проблему можно решить, проверяя изменяемое поле в WHERE.

          UPDATE dbo.Table1
          SET Name =‘abc’
          WHERE ID = 10
            and Name <>‘abc’

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

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

          CustomerID    AttribID
          ----------------------------
          1    100
          1    101
          2    100

          Рассмотрим две типичные задачи. Первая задача – поменять атрибут 101 у клиента 1 на атрибут 100.

          Запрос такого вида отлично работает на девелоперских и тестовых серверах (где нет первой строки) и падает на продакшен сервере (гда такая строка есть).

          UPDATE dbo.CustomerAttributes
          SET AttribID = 100
          WHERE CustomerID = 1
             and AttribID = 101

          Почему именно так а не иначе? По закону Мерфи о полноте базы данных.

          Усложним запрос, чтобы избежать неприятностей:

          UPDATE ca
          SET ca.AttribID = 100
          FROM dbo.CustomerAttributes ca
            left join dbo.CustomerAttributes ca1
              on ca.CustomerID = ca1.CustomerID
                and ca1.AttribID = 100
          WHERE ca.CustomerID = 1
             and ca.AttribID = 101
             and ca1.CustomerID is null

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

          UPDATE ca
          SET ca.AttribID = 100
          FROM dbo.CustomerAttributes ca
          WHERE ca.CustomerID = 1
            and ca.AttribID = 101
            and not exists (select 1 from dbo.CustomerAttributes
              where CustomerID = 1 and AttribID = 100)

          Также можно записать этот UPDATE как DELETE и безопасный INSERT.

          begin tran

          DELETE FROM dbo.CustomerAttributes
          WHERE CustomerID = 1
            and AttribID = 101

          if not exists(
            select 1
            from dbo.CustomerAttributes
            where CustomerID = 1 and AttribID = 100
            )
            INSERT INTO dbo.CustomerAttributes VALUES (1, 100)

          commit tran

          Вторая задача – атрибут 101 больше не используется, вместо него надо использовать атрибут 100 для всех клиентов. Решаем через удаление 101 атрибута для тех клиентов, у которых есть оба атрибута, и обновлением 101 атрибута на 100 для тех, у кого есть только 101 атрибут. Это DELETE и UPDATE. Можно записать во временную таблицу всех клиентов, у которых есть 101 атрибут, удалить все строки со 101 атрибутом, безопасно вставить клиентов с 100-м атрибут из временной таблицы, DELETE + INSERT.

          Третья задача – клиенты 1 и два объединились, теперь это один клиент. Надо объединить атрибуты клиентов 1 и 2. В этом случае нам надо добавить к клиенту 1 те атрибуты, которые есть у клиента 2, но нет у клиента 1, затем удалить все атрибуты клиента 2, INSERT + DELETE.

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

          DELETE

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

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

          Триггеры какому-либо обобщению не поддаются. Например, они могут проверять некоторые условия, и не позволять удаления если условия не выполняются. С триггерами проблем меньше всего. Потому что если они не разрешают какое-либо изменение, то и вам не стоит его делать. В крайнем случае триггеры можно временно отключить.

          Транзакции

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

          Первое – что они обязательно нужны. На самом деле это не так. Особенно это верно для безопасных скриптов. Мы их пишем специально таким образом, чтобы их можно было перезапускать, и при перезапуске они проверяют состояние данных, внося изменения по мере необходимости. Поэтому в большинстве девелоперских скриптов транзакции не нужны. У транзакций есть неочевидные минусы. Работающая транзакция накапливает блокировки, и освобождает их после финального commit. Чем больше транзакция – тем большая часть базы данных оказывается задействована. Во время работы транзакции другие транзакции вынуждены ждать. Это наблюдается в виде «тормозов». Если скрипты запускают на сервере под нагрузкой, такие «тормоза» стараются минимизировать. На самом деле транзакции необходимы только для рисковых операций. Если логика процесса такова, что при возникновении ошибки мы получим нарушение целостности (кто-то недополучит денег, или получит слишком много, появятся «висящие» строки, и так далее), то транзакция строго необходима.

          Второе заблуждение – что оператора транзакции достаточно. На самом деле нет. Обработка ошибок на SQL Server заслуживает отдельного описания, но я упомяну только один нюанс. Не достаточно написать begin transaction в начале скрипта и commit transaction в конце. Потому что откат транзакции вызывают не все ошибки, как многие ожидают. То есть возможна ситуация, когда в контексте транзакции произойдет ошибка, но выполнение транзакции будет продолжено. Пример – ошибки преобразования типа. Это расстраивает, но решение простое. Достаточно установить переменную xact_abort в on. В этом случае практически все ошибки будут вызывать откат транзакции. Увы, все равно возможны ошибки, которые не вызовут откат транзакции. Но транзакция с установленной переменной уже достаточно надежна, а все редкие исключения можно отследить и исправить вручную. Итого, транзакция должна выглядеть так:

          set xact_abort on
          begin tran

          commit tran

          Заключение

          Хоть было сказано и достаточно много слов, написание безопасных скриптов совсем не трудно. Больше всего хлопот причиняет оператор INSERT, потому что приходится дописывать проверку существования записей. Оператор UPDATE требует внимания в редких случаях. DELETE не требует внимания почти никогда. К месту и со вкусом примененная транзакция может сделать жизнь значительно легче. В результате мы получаем перезапускаемый скрипт, который легко можно комбинировать с другими скриптами и встраивать в build и deployment системы.

          Александр Синицын

        • Главная SQL, Без рубрики, Новое SQL, SQL 2008 R2
          • Microsoft StreamInsight и обработка сложных событий Complex Event Processing (CEP)

            00_logo С выходом новой версии SQL Server 2008 R2 Microsoft представила платформу StreamInsight для создания приложений обработки сложных событий. Главной задачей CEP приложений является обработка в режиме реального времени множества событий из различных источников (потоков событий) с целью выявления значимых событий, основанных на одном или нескольких потоков событий, либо выявления ряда событий, за определенный промежуток времени.

          • Главная SQL, Без рубрики, Новое SQL, SQL 2008 R2
            • Интеграция Данных

              data_monthly2 Служба Integration Services, входящая в SQL Server 2008 R2
              является платформой для построения высокоэффективной интеграции данных и для решения задач, связанных с последовательно выполняемыми действиями, включающими операции по извлечению, преобразованию и загрузке (ETL) данных в хранилище. Функциональные возможности службы Integration Services SQL Server 2008 R2 имеют улучшенную производительность, благодаря лучшей поддержке многопоточности в мультипроцессорных системах и высокоэффективным коннекторам доступа к источникам данных сторонних производителей. Платформа Integration Services в SQL Server 2008 R2 версии Enterprise превосходит традиционный процесс ETL благодаря набору адаптеров и преобразований для "добывания" данных, очистки данных и поддержке, близкой к реальному времени, среды окружения Analysis
              Services.

            • Главная SQL, Без рубрики, Новое SQL, SQL 2008 R2, StreamInsight, Конкурс
              • Обзор технологии Microsoft StreamInsight

                SQL-Server-StreamInsight-CTP2-Available-for-Download-2 Технология StreamInsight – это новая технология для анализа данных от Microsoft. Microsoft StreamInsight предназначен для обработки потоков событий и базируется на технологии Complex Event Processing.

                Complex Event Processing (CEP) – это непрерывная инкрементальная обработка потоков событий из множества источников на основании декларированных запросов и шаблонов с близкой к нулю задержкой.

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

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

                Понятно, что для описанного примера использование подхода СУБД, возможно, является не самой лучшей идеей.

                Теперь рассмотрим работу CEP системы. В CEP системе запросы являются «статичными», то есть автоматически выполняются системой при поступлении новых событий и отправляют обработанные данные в выходной поток. В общем случае, данные даже не обязательно сохранять куда-либо.

                Данные из входного потока обрабатываются инкрементально, то есть CEP движку не нужно обращаться к старым данных, если в этом нет необходимости.

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

                СУБД

                CEP

                Парадигма запросов Запросы выполняются по требованию Запросы обрабатываются непрерывно
                Задержка Секунды, часы, дни Миллисекунды или меньше
                Пропускная способность Сотни записей/сек Десятки тысяч записей/сек

                Связь StreamInsight и Microsoft SQL Server 2008 R2

                StreamInsight поставляется вместе с SQL Server 2008 R2 и является частью платформы обработки данных MS SQL, однако StreamInsight никак не относится к Database Engine и не зависит от SQL Server (обратное тоже верно).

                Так что связь SQL Server и StreamInsight является достаточно условной.

                Более того, StreamInsight можно скачать отдельно от MS SQL Server.

                С другой стороны, при установке StreamInsight отдельно от MS SQL Server, нужно ввести ключ активации MS SQL Server 2008 R2 или выбрать ознакомительный 180-дневный режим. Полностью бесплатную версию я найти не смог.

                Возможности StreamInsight зависят от редакции MS SQL Server 2008 R2, с которой ассоциирован вводимый ключ, и представлены в таблице:

                Возможности StreamInsight Редакция Microsoft SQL Server 2008 R2
                Standard

                Тысячи событий/сек

                Задержка: секунды

                Standard

                Enterprise

                Web

                Premium

                Десятки тысяц событий/сек

                Задержка: менее секунды

                Datacenter

                Developer

                Evaluation

                Установка StreamInsight

                Дистрибутив StreamInsight можно скачать по ссылке

                http://www.microsoft.com/sqlserver/2008/en/us/R2-complex-event.aspx, на момент написания статьи доступны версии для 32-битных и 64-битных операционных систем.

                Системные требования

                Аппаратное обеспечение:

                • Рекомендуется: 2.2 GHz CPU, 1024 MB RAM
                • Минимум: 1.6 GHz CPU, 384 MB RAM

                Программное обеспечение:

                Поддерживаются следующие операционные системы:

                • Windows XP Service Pack 2 и более новые (x86 и x64)
                • Windows Server 2003 Service Pack 2 и более новые (x86 и x64)
                • Windows Server 2003 R2 и более новые (x86 и x64)
                • Windows Vista (x86 и x64)
                • Windows Server 2008 и более новые (x86 и x64)
                • Windows 7 (x86 и x64)

                Дистрибутив занимаем около 10 Mb, установка происходит при помощи простого мастера. Наиболее важный вопрос при установке – это имя инстанса StreamInsight – это имя нужно обязательно запомнить, поскольку оно используется при программировании.

                После установки необходимо удалить старую версию Microsoft SQL Server Compact (Панель управления->Установка и удаление программ) и установить новую (идущую в комплекте со StreamInsight). На момент написания статьи, это Microsoft SQL Server Compact 3.5 SP1.

                В комплект установки входят следующие компоненты (после установки их можно найти в папке c:\Program Files\Microsoft StreamInsight 1.0\):

                Файлы Путь
                Microsoft.ComplexEventProcessing DLLs*

                StreamInsightDumper

                C:\Program Files\Microsoft StreamInsight 1.0\Bin
                Документация StreamInsight C:\Program Files\Microsoft StreamInsight 1.0\Documentation\<LanguageFolder>
                Конфигурация и исполняемые файлы Microsoft StreamInsight Server C:\Program Files\Microsoft StreamInsight 1.0\Host

                C:\Program Files\Microsoft StreamInsight 1.0\Host\<InstanceName>

                Лицензия Microsoft StreamInsight C:\Program Files\Microsoft StreamInsight 1.0\license\<LanguageFolder>
                Пакет установки SQL Server Compact Edition C:\Program Files\Microsoft StreamInsight 1.0\Redist
                Отладчик потоков событий Microsoft StreamInsight

                Для Windows XP и Windows Server 2003 урезанные версии

                C:\Program Files\Microsoft StreamInsight 1.

                * – Сборки регистрируются в GAC во время установки

                Схема работы StreamInsight

                Рассмотрим более подробно схему работы StreamInsight:

                Данные могут поступать с различных источников. Например, это могут быть различные сенсоры, веб-сервисы, хранилища данных и другие источники. Информация из таких источников, скорее всего, является разнородной (например, имеет разный формат), для преобразования этой информации в события, понятные системе, используются входные адаптеры (Input Adapters). Полученные события поступают на движок StreamInsight, где обрабатываются заранее описанными запросами. Причем, данные могут обрабатываться несколькими запросами по очереди, может также использоваться информация из внешних источников данных. Всю работу по максимально быстрой обработке событий берет на себя движок StreamInsight. Обработанные данные нужно каким-то образом передать получателям событий. Для преобразования данных в удобную для получателей форму используются выходные адаптеры (Output Adapters).

                Основные понятия

                К основным структурым элементам StreamInsight относятся:

                • Потоки
                • События
                • Адаптеры
                • Запросы

                Рассмотрим каждый элемент:

                Потоки

                Поток – это набор событий, который, в общем случае, может быть бесконечным.

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

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

                События

                Пожалуй, основной элемент StreamInsight – это событие.

                Любое событие в StreamInsight состоит из двух частей:

                • Заголовок
                  • Тип события (Event Kind)
                  • Временные отметки (Timestamps)
                • Нагрузка (Payload)
                  • Данные

                Событие может иметь одну или более (начало события, конец события) временных отметок. Время задается типом DateTime в фомате UTC. Стоит отметить, что временные отметки задаются программно, то есть разработчик отвечает за корректное заполнение временных отметок, а не движок StreamInsight.

                Тип события может иметь значения INSERT (новые данные) и CTI (Current Time Increment). Событие CTI используется для корректной обработки данных в тех случаях, когда события поступают в неправильной хронологической последовательности. Событие CTI по сути сообщает о том, что все события, появившиеся до временной отметки, соответствующей событию CTI, уже были отправлены и теперь могут быть обработаны.

                Рассмотрим пример:

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

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

                Добавим события CTI. Событие CTI говорит о том, что можно начать обрабатывать события лежащие левее его по оси времени. Таким образом мы вычисляем среднее значение 1.

                Идем дальше: пусть появилось еще некоторое количество событий и событие CTI 2. Теперь все события, лежащие левее CTI 2 могут быть обработаны.

                Что произойдет если после появления события CTI появилось новое событие лежащее левее него по оси времени (обозначено красным)? Ведь получается, что среднее значение 1 уже не является актуальным.

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

                Существует два вида таких политик:

                • Drop – отбрасывать все подобные события
                • Adjust – изменять временные характеристики события таким образом, чтобы оно оказалось правее последнего события CTI на временной оси. Правда такая политика применима не ко всем событиям, а лишь к тем которые обладают моделью отличной от точечной.

                Что такое модель события?

                Еще одной характеристикой события является его модель (Event Model), существует три модели событий:

                • Interval – известен период времени, в течении которого происходило событие

                  Пример: температура в комнате с 14-00 до 16-00 была равна 21 градусу.

                • Point – точечная модель, событие произошло мгновенно.

                  Пример: атмосферное давление в 14-30 было равно 760 мм.

                • Edge – известно время начала события, но не известно его время окончания

                  Пример: дождь начался в 18-00 (пока не закончился)

                Рассмотрим примеры:

                Interval

                Тип события Начало Конец Нагрузка (Id)
                INSERT 2009-12-27

                02:04:00.213

                2009-12-27

                02:04:04.329

                EU-23423-12
                INSERT 2009-12-27

                02:04:04.329

                2009-12-27

                02:04:08.234

                EU-23423-15
                INSERT 2009-12-27

                02:04:04.234

                2009-12-27

                02:04:04.523

                EU-23423-18

                Point

                Тип события Начало Конец Нагрузка (Id)
                INSERT 2009-12-27

                02:04:00.213

                2009-12-27

                02:04:00.213 + c

                EU-23423-12
                INSERT 2009-12-27

                02:04:04.329

                2009-12-27

                02:04:04.329 + c

                EU-23423-15
                INSERT 2009-12-27

                02:04:04.234

                2009-12-27

                02:04:04.234 + c

                EU-23423-18

                Где с – это наименьшая измеримая единица времени.

                События типа Edge обычно состоят из двух событий (начало и конец события):

                Тип события Тип границы Начало Конец Нагрузка (Id)
                INSERT Start 2009-12-27

                02:04:00.213

                EU-23423-12
                INSERT End 2009-12-27

                02:04:00.213

                2009-12-27

                02:04:04.329

                EU-23423-12
                INSERT Start 2009-12-27

                02:04:04.234

                EU-23423-18
                INSERT End 2009-12-27

                02:04:04.234

                2009-12-27

                02:04:08.238

                EU-23423-18

                ∞ – на самом деле DateTime.MaxValue

                Нагрузка

                Нагрузка – это те данные, которые сопровождают событие. Например, текущее значение температуры, полученное с термометра.

                Нагрузка описывается стандартной структурой на C#, однако существует ряд ограничений, отметим основные (полный список ограничений может быть найден в Microsoft StreamInsight Help -> Developer’s Guide -> Creating Event Types):

                • Размер события не должен превышать 16Kb с учетом всей служебной информации.
                • Событие должно иметь не менее одного поля нагрузки
                • Допустимы только скалярные и элементарные типы (byte,int,byte[],string,datetime)
                • Нагрузка должна быть упакова в класс или структуру C# (даже если в нагрузке содержится только одно поле, оно должно быть размещено в структуре или классе)
                • Нельзя использовать пользовательские атрибуты для полей

                Следующий класс описывает нагрузку с двумя полями типа int.

                public class SimplePayload

                {

                public int V1 { get; set; }

                public int V2 { get; set; }

                }

                Адаптеры

                Как было замечено ранее, адаптеры служат для преобразования потоков данных. Адаптеры делятся на входные и выходные. Входные адаптеры преобразуют данные полученные с источников, в формат понятный StreamInsight, а выходные адаптеры преоразуют данные в формат понятный конечным получателям.

                Основные задачи, которые нужно решить при разработке адаптеров:

                Определить тип адаптера.

                Входной или выходной.

                Определить тип событий.

                Адаптер может быть типизированным либо нетипизированным. Структура событий для типизированных адаптеров известна заранее (описана в виде класса), а для нетипизиванных неизвестна (например, данные получаются с использованием DataReader из Microsoft SQL Server).

                Определить модель событий.

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

                Выбрать подходящий базовый класс для адаптера.

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

                Тип адаптера Базовый класс входного адаптера Базовый класс выходного адаптера
                Типизированный, Point TypedPointInputAdapter TypedPointOutputAdapter
                Нетипизированный, Point PointInputAdapter PointOutputAdapter
                Типизированный, Interval TypedIntervalInputAdapter TypedIntervalOutputAdapter
                Нетипизированный, Interval IntervalInputAdapter IntervalOutputAdapter
                Типизированный, Edge TypedEdgeInputAdapter TypedEdgeOutputAdapter
                Нетипизированный, Edge EdgeInputAdapter EdgeOutputAdapter

                Создать фабрики для входных и выходных адаптеров

                Тип адаптера Базовый класс фабрики входного адаптера Базовый класс фабрики выходного адаптера
                Типизированный ITypedInputAdapterFactory ITypedOutputAdapterFactory
                Нетипизированный IInputAdapterFactory IOutputAdapterFactory

                Далее перечислены основные обязанности фабрики:

                • Разделяет ресурсы между сходными адаптерами, различающимися только моделью событий.
                • Предоставляет интерфейс Create() и Dispose(). С их помощью адаптеры работают с событиями.
                • Автоматически создает CTI события с учетом пользовательских настроек.

                Разработчик адаптера должен имплементировать методы Start() и Resume() базового класса, в которых производится преобразование событий.

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

                Очень подробно разработка адаптеров описана в разделе Creating Input and Output Adapters документации StreamInsight.

                Запросы

                Пожалуй, наиболее интересным с точки зрения разработчика элементом StreamInsight являются запросы.

                Запросы StreamInsight представляют собой не что иное, как LINQ запросы к потокам данных.

                Простейший запрос мог бы выглядеть следующим образом:


                var queryOutput = from e in input


                select e;

                Такой запрос просто возвращает все полученные события.

                StreamInsight запросы предоставляют следующие возможности:

                • Проекции (Project)
                • Фильтры (Filter)
                • Корелляция потоков (Join)
                • Объединения (Union)
                • Агрегация (Aggregation)
                • Оконные операции

                Рассмотрим каждую из возможностей:

                Проекции

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

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

                Следующий запрос использует пользовательскую функцию для получения нового поля:


                var queryOutput = from e in input


                select
                new { e.Lane, e.TagId,

                VehicleType = TollPointEvent.VehicleTypeName(e.VehicleTypeId) };

                Фильтры

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

                Следующий запрос выбирает только те события, для которых выполняется условие из блока where:


                var queryOutput = from e in input


                where e.VehicleTypeId == 2


                select
                new { e.Lane, e.TagId, VehicleType = TollPointEvent.VehicleTypeName(e.VehicleTypeId) };

                Корелляция потоков

                Используется операция Join, хорошо знакомая разработчикам, использующим SQL. Разница в том, что объединятся не все события, а только те, которые произошли в один и тот же промежуток времени.

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


                var queryOutput = from nbv in northboundVehicles


                from sbv in lane0Vehicles


                select
                new

                {

                ExitNorth = nbv.ExitGate,

                NorthVehicle = nbv.TagId,

                TollPointId = sbv.TollPointId

                };

                Объединение

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

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


                var northboundVehicles = from e in input


                where e.DirectionId == 0


                select e;


                var lane0Vehicles = from e in input


                where e.Lane == 0


                select e;


                var queryOutput = northboundVehicles.Union(lane0Vehicles);

                Безусловно, можно объединять более двух потоков, последовательно вызывая Union()

                Агрегация

                Агрегация позволяет вычислять некоторую функцию по набору событий. К таким фукнциям относятся: Avg, Sum, Count и т.д. Также можно использовать пользовательские функции. Функции агрегации можно применять только к наборам событий, в данном случае – только к окнам.

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

                Для окон поддерживаются следующие виды операций:

                • Агрегация.
                • TopK – ранжирование.
                • Пользовательские операторы.

                В StreamInsight существует четыре типа окон:

                • Временные окна: Hopping Window и Tumbling Window
                • Окна моментальных снимков: Snapshot Window
                • Окна количества: CountByStartTime Window

                Hopping Window (прыгающее окно)

                Для прыгающего окна определяются два параметра: H – размер «прыжка» и размер окна S. Новое окно создается через каждые H моментов времени (окно «прыгает»), а размер этого окна равен S.

                Если H равно S, такое окно называется Tumbling Window (окно вращения).

                Следующий запрос вычисляет сумму по всем событиям, произошедшим за последний час, каждые 10 минут.

                var hoppingAgg = from w in inputStream.HoppingWindow(TimeSpan.FromHours(1),

                TimeSpan.FromMinutes(10),

                HoppingWindowOutputPolicy.ClipToWindowEnd)

                select new { sum = w.Sum(e => e.i) };

                Обратите внимание, что при создании окна используется параметр, задающий политику HoppingWindowOutputPolicy.ClipToWindowEnd. Интересным является тот факт, что в текущей версии StreamInsight существует всего один вариант задания данной политики (правда он называется по-разному для каждого типа окна).

                Snapshot Window (Окно моментального снимка)

                Параметры окон моментального снимка определяются только событиями в потоке. Появление нового события или окончание старого приводит к окончанию текущего окна и созданию нового (если какие-либо события еще не закончены). Рисунок хорошо иллюстрирует сказанное:

                Следующий запрос создает окно мементального снимка:

                var snapshotAgg = from w in inputStream.SnapshotWindow(WindowInputPolicy.ClipToWindow,

                SnapshotWindowOutputPolicy.Clip)

                select new { sum = w.Sum(e => e.i) };

                CountByStartTime Window (Окно количества)

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

                Окно количество можно задать следующим запросом:

                var agg = from w in inputStream.CountByStartTimeWindow(10, CountWindowOutputPolicy.PointAlignToWindowEnd)

                select new { sum = w.Sum(e => e.i) };

                Для более подробного ознакомления с окнами событий, я рекомендую обратиться к соответствующему разделу документации StreamInsight (Developer’s Guide->Writing Query Templates in LINQ->Using Event Windows).

                Этапы разработки

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

                1. Определить параметры событий и создать класс нагрузки
                2. Создать входные адаптеры и фабрики входных адаптеров для каждого типа источников данных
                3. Создать шаблоны StreamInsight запросов с использованием LINQ
                4. Создать выходные адаптеры и фабрики выходных адаптеров для каждого типа получателей событий
                5. Связать все компоненты системы вместе

                Также нужно отметить, что движок StreamInsight может работать в нескольких режимах:

                • Как отдельный сервис
                • Как встроенная часть приложения

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

                Пример

                Теперь пора перейти к примеру. Я буду использовать пример, идущий в комплекте с SQL Server 2008 R2 Update for Developers Training Kit, который называется HighwayMonitor.

                Задача:

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

                Существует пять типов машин:

                • Скорая (Ambulance)
                • Автобус (Bus)
                • Грузовик (Truck)
                • Такси (Taxi)
                • Легковой автомобить (Car)

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

                Пока будем просто выводить события в DataGridView.

                Открываем проект в Visual Studio 2010.

                В окне Solution Explorer посмотрим на структуру проекта:

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

                Определим формат событий в файле TollPointTypes.cs


                public
                class
                TollPointEvent : IRandomInit

                {


                public
                static
                int TollPoints = 6;


                public
                static
                int Lanes = 8;


                public
                static
                Random rand;


                public
                Guid EventID;


                public
                Int32 TollPointId;


                public
                Int32 DirectionId;


                public
                Int32 Lane;


                public
                Int32 VehicleTypeId;


                public
                string TagId;


                public
                DateTime EnterGate;


                public
                Int32 MillisecondsToPassSpeedCheckPoint;


                public
                DateTime ExitGate;


                public TollPointEvent()

                {

                }


                public
                void Init()

                {


                if (null == rand)

                {

                rand = new
                Random();

                }


                this.EventID = Guid.NewGuid();


                this.TollPointId = rand.Next(TollPoints);


                this.DirectionId = PickDirection(rand);


                this.Lane = rand.Next(Lanes);


                this.VehicleTypeId = PickVehicleTypeId(rand);


                this.TagId = rand.Next(Int32.MaxValue).ToString();


                this.ExitGate = DateTime.Now;


                Int32 vehicleSpeed = rand.Next(30, 120); // generate car speeds between 30 and 120 kph


                //Int32 vehicleSpeed = 80;


                Int32 vehicleLength = PickLength(rand, this.VehicleTypeId);


                this.EnterGate = CalcEnter(this.ExitGate, vehicleSpeed, vehicleLength);


                this.MillisecondsToPassSpeedCheckPoint = CalcPassSpeedCheckPoint(this.EnterGate, vehicleSpeed);


                Trace.WriteLine(this.EventID.ToString());

                }

                }

                Также в классе содержатся дополнительные методы, но для нас они не слишком важны.

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


                public
                class
                TollPointInput<TollPointEvent> : TypedPointInputAdapter<TollPointEvent>

                {


                private
                TollPointInputConfig _config;


                public TollPointInput(TollPointInputConfig config)

                {

                _config = config;

                }


                public
                override
                void Resume()

                {

                ProduceEvents();

                }


                public
                override
                void Start()

                {

                ProduceEvents();

                }


                private
                void ProduceEvents()

                {


                PointEvent<TollPointEvent> currEvent = default(PointEvent<TollPointEvent>);


                EnqueueOperationResult result = EnqueueOperationResult.Full;


                Random rand = new
                Random();


                while (!HighwayMonitor.Program.MainWindow.NeedToStop)

                {


                if (AdapterState.Stopping == AdapterState)

                {

                Stopped();


                return;

                }

                currEvent = CreateInsertEvent();


                if (null == currEvent)

                {


                continue;

                }

                currEvent.StartTime = DateTime.Now;


                currEvent.Payload = (TollPointEvent)Activator.CreateInstance(typeof(TollPointEvent));

                (currEvent.Payload as
                IRandomInit).Init();

                result = Enqueue(ref currEvent);


                if (EnqueueOperationResult.Full == result)

                {

                ReleaseEvent(ref currEvent);

                Ready();


                return;

                }


                Thread.Sleep(rand.Next(1,500));

                }


                this.Stopped();

                }

                }

                Мы знаем параметры событий (TollPointEvent), а наши события являются мгновенными, так что в качестве базового класса выбран TypedPointInputAdapter.

                Реализованы методы Resume() и Start(), которые просто вызывают метод ProduceEvents(), на котором стоит остановиться подробнее.

                currEvent = CreateInsertEvent();

                Создает новое событие с типом INSERT, далее поля события (включая время возникновения) заполняются. После того как все поля заполнены, вызывается метод Enqueue(), который ставит новое событие в очередь обработки.

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

                В файле TollPointInputFactory.cs описана фабрика входных адаптеров, метод Create которой просто возвращает экземпляр только что рассмотренного адаптера.


                public
                class
                TollPointInputFactory : ITypedInputAdapterFactory<TollPointInputConfig>, ITypedDeclareAdvanceTimeProperties<TollPointInputConfig>

                {


                public
                InputAdapterBase Create<TollPointEvent>(TollPointInputConfig configInfo, EventShape eventShape)

                {


                return
                new
                TollPointInput<TollPointEvent>(configInfo);

                }


                public
                void Dispose()

                {

                }


                public
                AdapterAdvanceTimeSettings DeclareAdvanceTimeProperties<TPayload>(TollPointInputConfig configInfo, EventShape eventShape)

                {


                var atgs = new
                AdvanceTimeGenerationSettings(configInfo.CtiFrequency, TimeSpan.FromSeconds(0), true);


                var ats = new
                AdapterAdvanceTimeSettings(atgs, AdvanceTimePolicy.Drop);


                return ats;

                }

                }

                Метод DeclareAdvanceTimeProperties устанавливает политику работы с событиями CTI.

                Первая строка метода создает настройки времени таким образом, чтобы событие CTI создавалось после каждого configInfo.CtiFrequency-го события. В основной программе для этого параметра будет задано значение 1, это значит, что событие CTI будет создаваться после каждого события.

                Во второй строчке указывается политика обработки событий, которые поступили позже события CTI, но их временная отметка более раняя. В данном случае устанавливается значение Drop (на самом деле мы не могли установить другое значение, поскольку мы работаем с мгновенными (Point) событиями).

                Файл GridOutputAdapter.cs содержит описание выходного адаптера


                public
                class
                GridOutputAdapter : PointOutputAdapter

                {


                private
                EventWaitHandle _adapterStopSignal;


                private
                CepEventType _bindtimeEventType;


                private
                int _eventsDequeued = 0;


                public GridOutputAdapter(string StopSignalName, CepEventType EventType)

                {

                _bindtimeEventType = EventType;

                _adapterStopSignal = EventWaitHandle.OpenExisting(StopSignalName);

                }


                public
                override
                void Start()

                {


                List<string> columnHeaders = new
                List<string>();

                columnHeaders.Add(“Command”);

                columnHeaders.Add(“Timestamp”);


                for (int fieldCounter = 0; fieldCounter < _bindtimeEventType.FieldsByOrdinal.Count; fieldCounter++)

                {


                CepEventTypeField eventFieldType = _bindtimeEventType.FieldsByOrdinal[fieldCounter];

                columnHeaders.Add(eventFieldType.Name);

                }

                HighwayMonitor.Program.MainWindow.AddEventToDisplayListBox(columnHeaders.ToArray(), _bindtimeEventType.FieldsByOrdinal.Count + 2);

                ConsumeEvents();

                }


                public
                override
                void Resume()

                {

                ConsumeEvents();

                }


                protected
                override
                void Dispose(bool disposing)

                {


                base.Dispose(disposing);

                }


                private
                void ConsumeEvents()

                {


                PointEvent currEvent = default(PointEvent);


                DequeueOperationResult result;


                try

                {


                while (true)

                {


                if (AdapterState.Stopping == AdapterState)

                {

                result = Dequeue(out currEvent);

                PrepareToStop(currEvent, result);

                Stopped();

                _adapterStopSignal.Set();


                return;

                }


                result = Dequeue(out currEvent);


                if (DequeueOperationResult.Empty == result)

                {

                PrepareToResume();

                Ready();


                return;

                }


                else

                {

                _eventsDequeued++;


                HighwayMonitor.Program.MainWindow.AddEventToDisplayListBox(CreateStringArrayFromEvent(currEvent), _bindtimeEventType.FieldsByOrdinal.Count + 2);

                ReleaseEvent(ref currEvent);

                }

                }

                }


                catch (AdapterException e)

                {


                Console.WriteLine(“ConsumeEvents – “ + e.Message + e.StackTrace);

                }

                }


                private
                void PrepareToStop(PointEvent currEvent, DequeueOperationResult result)

                {


                if (DequeueOperationResult.Success == result)

                {

                ReleaseEvent(ref currEvent);

                }

                }


                private
                void PrepareToResume()

                {

                }


                private
                string[] CreateStringArrayFromEvent(PointEvent currEvent)

                {


                if (EventKind.Cti == currEvent.EventKind)

                {


                return
                new
                string[] {null, “CTI”, currEvent.StartTime.ToString() };

                }


                else

                {


                List<string> eventDetails = new
                List<string>();

                eventDetails.Add(null); // leave the first column for an image

                eventDetails.Add(“INSERT”);

                eventDetails.Add(currEvent.StartTime.ToString());


                for (int fieldCounter = 0; fieldCounter < _bindtimeEventType.FieldsByOrdinal.Count; fieldCounter++)

                {


                CepEventTypeField eventFieldType = _bindtimeEventType.FieldsByOrdinal[fieldCounter];


                object value = Convert.ChangeType(currEvent.GetField(fieldCounter), eventFieldType.Type.ClrType, CultureInfo.CurrentCulture);

                eventDetails.Add((value != null) ? value.ToString() : “NULL”);

                }


                return eventDetails.ToArray();

                }

                }

                }

                Наиболее интересным для нас методом тут является метод ConsumeEvents(). При помощи метода Dequeue() из очереди извлекается событие (если оно там есть), из которого создается набор строк в методе CreateStringArrayFromEvent() . Полученные строки добавляются в грид на форме.

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

                Фабрика выходных адаптеров описана в файле GridOutputAdapterFactory.cs:


                public
                class
                GridOutputAdapterFactory : IOutputAdapterFactory<string>

                {


                public
                OutputAdapterBase Create(string StopSignalName,EventShape Shape, CepEventType EventType)

                {


                return
                new
                GridOutputAdapter(StopSignalName, EventType);

                }


                public
                void Dispose()

                {

                }

                }

                Тут совсем нет ничего интересного, мы просто возвращаем новый экземпляр выходного адаптера.

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


                private
                void StartButton_Click(object sender, EventArgs buttonArgs)

                {


                if (StartButton.Text == “&Start”)

                {

                NeedToStop = false;


                try

                {

                _tracer.WriteLine(“Starting Toll Tracker”);

                _server = Microsoft.ComplexEventProcessing.Server.Create(“si”);

                _application = _server.CreateApplication(“HighwayMonitor”);


                var input = CepStream<TollPointEvent>.Create(“TollPointInputStream”,


                typeof(TollPointInputFactory),

                _inputConfig,


                EventShape.Point);


                //———————————————————————-


                //- Modify this query – must have a resultant query called queryOutput


                // 2.1.01 – original query


                var queryOutput = from e in input


                select e;


                //———————————————————————

                _results = queryOutput.ToQuery(_application, “TollData”, “TollData Query”, typeof(GridOutputAdapterFactory),

                _stopSignalName,


                EventShape.Point,


                StreamEventOrder.FullyOrdered);

                _adapterStopSignal.Reset();

                _results.Start();

                StartButton.Text = “Sto&p”;

                DisplayDataGridView.Rows.Clear();

                DisplayDataGridView.Columns.Clear();

                }


                catch (Exception e)

                {

                _tracer.WriteLine(e.ToString());


                if (_results != null) _results.Stop();


                MessageBox.Show(“Unable to start query. Error returned was:\n\r” + e.ToString());

                }

                }


                else

                {

                NeedToStop = true;

                _adapterStopSignal.WaitOne();

                _results.Stop();

                _results = null;


                if (_server != null)

                {

                _server.Dispose();

                _server = null;

                }

                StartButton.Text = “&Start”;

                }

                }

                Первое, на что стоит обратить внимание – это создание движка StreamInsight.

                _server = Microsoft.ComplexEventProcessing.Server.Create(“si”);

                Параметр метода Create() – это название экземпляра StreamInsight сервера, которые мы задавали при инсталляции (в моем случае – “si”).

                Далее создается входной поток с использованием фабрики входных адаптеров.


                var input = CepStream<TollPointEvent>.Create(“TollPointInputStream”,


                typeof(TollPointInputFactory),

                _inputConfig,


                EventShape.Point);

                queryOutput – это запрос для обработки входного потока, в данном случае просто выбираются все события.


                var queryOutput = from e in input


                select e;

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

                _results = queryOutput.ToQuery(_application, “TollData”, “TollData Query”, typeof(GridOutputAdapterFactory),

                _stopSignalName,


                EventShape.Point,


                StreamEventOrder.FullyOrdered);

                Обратите внимание на вызов метода ToQuery(), он преобразует LINQ запрос в запрос StreamInsight.

                Вызов _results.Start(); запускает StreamInsight запрос на выполнение.

                Если запустить приложение и нажать на кнопку Start, то должна получиться следующая картина:

                Рекомендую ознакомиться с файлами Demo 2.1 QueryStreamInsightUsingLINQ.txt и Demo 2.2 Using Advanced Query Options.txt, в них содержатся примеры более интересных запросов для данного приложения.

                Заключение

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

                С другой стороны, технология новая и некоторые моменты вызывают сомнения, на мой взгляд, наиболее очевидные недостатки технологии на сегодняшний день следующие:

                • Непонятная ценовая политика. Во время подготовки статьи я не смог найти бесплатной версии StreamInsight без ограничения по времени или возможности купить StreamInsight отдельно от SQL Server 2008 R2. (Хотя, возможно, я плохо искал)
                • Не понятно, каким образом аналитик должен писать LINQ запросы. Как пока известно, планируется разработка специального языка StreamInsight запросов, понятного аналитику, а не только разработчику.

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

                Материалы для дальнейшего изучения

                В рамках данной статьи были рассмотрены лишь основные аспекты работы со StreamInsight, для более глубокого понимания технологии, я рекомендую обратиться к следующим источникам:

                Techdays.ru – материалы на русском

                (http://www.techdays.ru/Search.aspx?Tag=SQL%2bServer%2b2008%2bR2)

                SQL Server 2008 R2 Update for Developers Training Kit – крайне полезные обучающие материалы
                (http://www.microsoft.com/downloads/details.aspx?familyid=FFFAAD6A-0153-4D41-B289-A3ED1D637C0D&displaylang=en)

                Документация по StreamInsight (поставляется вместе с StreamInsight)

                Исходный код

                Иван Андреев

              • Главная SQL, Без рубрики, Новое SQL, SQL 2008 R2, Конкурс
                • SQL Server 2008 R2. The SQL Server Utility.Практика применения.

                  1226059553_pic_id159312 В настоящее время даже весьма не крупные предприятия и организации имеют по несколько SQL серверов (более формально – экземпляров серверов, instance), хранящих и обслуживающих бизнес-данные такого предприятия. Большие предприятия могут иметь десятки MS SQL серверов/экземпляров, очень крупные – сотни. Как показывает практика, такое разрастание количества экземпляров ставит перед IT-отделами организаций один не сложный (как кажется не посвященному взгляду) вопрос: как управляться со всем этим хозяйством? Взгляд просвещённый же мгновенно поймет всю не тривиальность задаваемого вопроса. Экземпляры могут быть одной редакций и разных. Располагаться и работать они могут на компьютерах современных и морально устаревших. Обслуживать они могут 2 базы по 5 таблиц в каждой, а могут 50 баз по 100 таблиц. С большой степенью вероятности хотя бы часть (а иногда все) экземпляров будут иметь свои настройки и требовать в отношении себя соблюдения своих собственных «правил поведения» (научно называемых политиками). И, не забудем, все эти сервера могут еще дополнительно быть географически разнесены, а вовсе не находиться в одной серверной комнате. Так как не запутаться в этом море настроек, правил, редакций?

                • Главная SQL, Без рубрики, Новое SQL, SQL 2008 R2, Конкурс
                  • SQL Server 2008 R2 для многопроцессорных систем

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

                    Оглавление

                    Введение

                    До недавнего времени, наиболее распространённой архитектурой систем с большим числом физических процессоров являлась архитектура неоднородного доступа к памяти Non-Uniform Memory Architecture (NUMA). В основе этой архитектуры лежит такой способ организации доступа к оперативной памяти сервера, который зависит от её расположения (удалённости) по отношению к процессору. Внутренняя организация такой серверной архитектуры отличается от архитектуры SMP систем набором дополнительных компонент, которые обеспечивают взаимодействие процессорных блоков – узлов между собой. Говоря упрощённо, эти компоненты как бы связывают несколько обычных SMP серверов на одной или на нескольких материнских платах, обеспечивая обращения процессоров из одного узла к памяти на другом узле, если это оказывается необходимо.
                    Сегодня мы с вами становимся свидетелями смены тенденции в развитии архитектур неоднородного доступа к памяти. Классические типы архитектур, которые запомнились, прежде всего, своей высокой стоимостью решений, заменяются близкими по смыслу, но построенными на более дешёвых компонентах, архитектурными решениями. Если сделать краткий экскурс в историю, то мы увидим, что долгое время наиболее распространённой была архитектура NUMA с обеспечением когерентности процессорных кэшей. Вам должна быть знакома аббревиатура ccNUMA. Существовало несколько протоколов и реализаций поддержки когерентности кэшей, некоторые из них были подробно описаны в работе: Архитектура S2MP – свежий взгляд на cc-NUMA. Если кратко, то суть сводится к тому, что изменение в кэше приводит к удалению копий данных из кэшей других процессоров, а информация о копиях хранится в виде битового вектора, в специальном “оглавлении”, которое иногда называют кэшем четвёртого уровня, который, по сути, является кэшем метаданных. Пример реализации и описание подобной архитектуры можно найти в статье: Архитектура серверов HP Superdome. Один из вариантов реализации архитектуры ссNUMA был предложен компанией Sequent, и получил название NUMA-Q (это ссNUMA с кводами). Компания была приобретена IBM, и развитие описываемых в статье технологий можно видеть в современных решениях этого вендора.
                    В последние годы существенно возросло число физических ядер процессоров на один процессорный разъем – сокет (многоядерные процессоры). Существуют решения, например, IBM X-Architecture, которые позволяют ещё больше увеличить число процессорных ядер, что осуществляется за счёт объединения многоядерных SMP систем в блоки серверов. Такие серверные “бутерброды” способны по числу процессоров и по производительности конкурировать с традиционными NUMA системами. По сути, обычные шасси SMP серверов связываются между собой специальными межсоединениями. Многоядерность сама по себе уже вносит неоднородность в доступ к памяти. В случае же, когда несколько серверов с многоядерными процессорами объединяются в единый серверный блок (управляемый одной операционной системой или гипервизором), доступ процессорных ядер к оперативной памяти другого шасси носит ярко выраженный неоднородный характер. Всё это размывает разницу между классическими NUMA – системами и современными SMP-решениями. Некоторые вендоры даже называют построенную на SMP многопроцессорную архитектуру – NUMA-like Architecture.
                    В современных массовых процессорных архитектурах тоже используется неоднородный доступ к памяти. Например, такая схема реализована для Intel QuickPath Integrated Memory Controller. Это решение отличается тем, что в нём отказались от архитектуры фронтальной шины. Вместе с процессорами на одном кристалле интегрирован контроллер памяти, посредством которых, по схеме точка – точка, подключаются модули оперативной памяти. Такое решение позволяет сгладить “застарелые” проблемы с поддержкой когерентности NUMA. Кроме того, за счёт применения дополнительных чипсетов (например, IBM eX5), удаётся с ущественно нарастить объём оперативной памяти, выделенной каждому процессорному ядру. Это позволяет очень сильно сократить трафик ввода-вывода через процессорные межсоединения. По сути, межсоединениям останется обслуживание только задач поддержки когерентности процессорных кэшей. О современных шинах межсоединений можно почитать в этих статьях: Intel QuickPath Interconnect и HyperTransport.
                    Что же дальше? Очень похоже на то, что нелинейность доступа к ресурсам останется головной болью администраторов надолго. Появление таких массовых архитектурных решений, как решения на основе QPI, и их “творческое” воплощение разными вендорами способно породить большое разнообразие топологий NUMA. Но это ещё не всё. Сегодня появляются решения, в основе которых заложена парадигма обратной виртуализации. Очень может оказаться, что изложенные в настоящей статье рекомендации и методики окажутся непригодны для таких нетрадиционных решений. Остаётся надеяться, что таким технологиям, как ScaleMP, не покорятся горизонты платформы Windows, и мне не придётся дополнять эту статью главой о NUMAlink.
                    Современные операционные системы уже немыслимы без поддержки NUMA. Эта поддержка обретает новые качественные улучшения от версии к версии. Так, например, в Windows 2003 была доработана поддержка NUMA в планировщике потоков и диспетчере памяти, а в Windows 2008 поддержка NUMA была добавлена в диспетчере запросов ввода-вывода и внесены усовершенствования в диспетчере памяти.
                    Существует утилита, которая позволяет увидеть топологию NUMA вашего компьютера. Эта утилита создана Марком Русиновичем, и скачать её можно с сайта sysinternals.com Название утилиты: “Coreinfo“. Она позволяет получить данные о ядрах (параметр -c), о группах (параметр -g) и о NUMA – узлах (параметр -n).
                    Последние версии SQL Server тоже оптимизированы для работы в архитектуре NUMA. Особенности работы SQL Server в NUMA и способы настройки для оптимальной работы NUMA c нагрузками SQL Server будут разобраны в этой статье, а также вашему вниманию будут предложены ссылки на материалы, которые помогут уточнить или углубить представленные в статье рекомендации и описания.
                    Для успешного использования многопроцессорных платформ, системным администраторам и разработчикам приложений требуются дополнительные средства и меры, позволяющие влиять на распределение потоков между процессорами и закрепление за процессорами ресурсов сервера. Именно о таких мерах и средствах пойдёт речь в этой статье.

                    К оглавлению

                    Традиционная архитектура NUMA

                    В этой статье речь идёт об особенностях работы SQL Server на платформе с архитектурой NUMA. Мы ограничимся в своём рассмотрении только теми компьютерами, на которые устанавливается версия операционной системы старше Windows 2000. Традиционной для использования с этой СУБД и ОС архитектурой NUMA являлась платформа на базе процессоров Intel Itanium. В этой главе вам будет представлен краткий обзор особенностей этой архитектуры. Долгие годы серверы масштаба предприятия строились именно на таких процессорах. В 2010 году было объявлено о выходе новой линейки процессоров Itanium, и в этом же году Корпорация Майкрософт объявила о прекращении поддержки этой платформы в своих новых версиях операционных систем, которые появятся после Windows Server 2008 R2.
                    В традиционной архитектуре NUMA каждый процессорный NUMA – узел имеет локальную по отношению к нему память, доступ к которой процессоры узла осуществляют симметрично и с минимальными задержками. Процессоры работают с памятью через специализированный контроллер памяти, который имеется у каждого узла. Этот контроллер решает и другие задачи, к числу которых относится организация взаимодействия с устройствами ввода-вывода и доступ к памяти других NUMA – узлов компьютера. Для доступа к памяти других узлов контроллеры использует специальную шину, позволяющую процессорам использовать память других узлов. Память других узлов будет являться для них удалённой, и доступ к ней будет с большей задержкой, чем к локальной памяти. Неоднородность доступа в этой архитектуре главным образом относится к памяти, что является основным отличием этой многопроцессорной архитектуры от SMP, и именно такой метод доступа к памяти определяет её название. Задержки обращения к удалённой по отношению к узлу памяти могут иногда на порядок отличаться от задержек обращения к локальной памяти узла. Современные архитектуры серверов используют механизмы “горячих” страниц, которые позволяют отслеживать наиболее активно используемые NUMA узлом участки удалённой по отношению к нему памяти и переносить располагаемые там данные в локальную память.
                    Кроме локальной памяти, каждый NUMA узел может иметь собственный канал ввода-вывода. Это позволяет соотносить NUMA узлы портам ввода-вывода и локализовать на этом узле только те задачи, которые поступили по предписанным узлам портам.
                    В качестве шины поддержки когерентности кэшей часто используются специализированные коммутаторы, которые позволяют масштабировать подключения контроллеров памяти. Контроллер памяти может иметь несколько портов, которые выделенной шиной подключаются к отдельным коммутаторам масштабируемости. Ещё одной задачей таких коммутаторов является обеспечение подключения контроллеров памяти к концентраторам ввода-вывода. Концентратор ввода-вывода через мосты подключается к дисковым устройствам ввода-вывода, а также другим, унаследованным устройствам ввода-вывода.
                    Основной целью NUMA является масштабируемость. Традиционно, наиболее сложными для масштабируемости являются массовые сетевые запросы. Основным недостатком NUMA – систем является их более высокая стоимость относительно традиционных SMP систем. Кроме того, сервера построенные не на платформе Itanium (NUMA-like системы) по числу ядер уже вплотную подобрались к традиционным NUMA системам. Ну и самой плохой новостью является уже упомянутый отказ Майкрософт поддерживать в будущих версиях семейство процессоров Intel Itanium.
                    Дополнительную информацию о неоднородном доступе к памяти можно найти в электронной документации Microsoft SQL Server Books Online: “Основные сведения о неоднородном доступе к памяти“.

                    К оглавлению

                    Особенности NUMA-like архитектур

                    Появление архитектуры NUMA-like обусловлено желанием масштабирования недорогих SMP серверов. Выглядит NUMA-like как многоядерный блок из нескольких шасси серверных SMP систем. В NUMA-like неоднородным становится не только доступ к памяти, но и дисковый ввод-вывод, сетевой ввод-вывод, доступ к устройствам на шинах PCI или USB. Такие устройства, как гибкие магнитные диски и приводы CD-ROM могут выборочно отключаться, поскольку классическая архитектура персонального компьютера не предусматривает наличие этих устройств на нескольких шинах.
                    Когда мы имеем дело с одним шасси сервера, процессоры могут обращаться ко всей его памяти, ко всем присутствующим шинам и адаптерам ввода-вывода. Разницы в производительности у процессоров при работе с памятью практически не будет. Всё меняется, когда шасси соединяются с помощью кабеля/разъёма масштабируемости. В этом случае, та память, шины и адаптеры ввода-вывода, которые будут с процессорами на одном шасси, позволят получать наибольшую операционную производительность. Аналогичные же устройства во втором шасси будут работать с процессорами первого шасси с издержками, которые могут оказаться весьма значительными. Кроме этих издержек, на производительность системы в целом могут повлиять и другие факторы, косвенно или напрямую зависящие от использования схемы с несколькими шасси.
                    В отличие от традиционной архитектуры NUMA, для которой выпускались специальные версии ОС и СУБД, архитектурные решения NUMA-like становятся чувствительны к выбору платформы, версий и редакций. Неверный выбор редакции или настроек операционной системы может породить проблемы. Например, возможна неверная оценка лицензий при учёте процессорных ядер, вследствие чего система будет неверно представлять прикладному уровню группировку ядер физических процессоров (сокетов). Неудачные реализации или настройки BIOS, а также драйверов устройств, тоже могут породить проблемы для определения точной топологии ресурсов соединённых межсоединениями серверов. Ещё одним возможным источником проблем могут стать неадаптированные к подобной схеме прикладные программы. Причиной деградации производительности таких программ может стать неправильная нагрузка на несколько шасси, приводящая к большому трафику по межсоединениям. Нужно очень тщательно подходить к планированию таких систем. Тут нет мелочей. Как уже отмечалось, даже выбор редакции операционной системы может сказаться на возможностях и производительности системы. Наиболее полный набор возможностей для многопроцессорных систем имеет редакция Datacenter. Проконсультируйтесь у вендора по поводу планируемой конфигурации и выбору версий и редакций её компонент.
                    В NUMA-like системе физическая память каждого из подключённых в одну систему серверов объединяется в единое, последовательное адресное пространство. С точки зрения организации доступа к памяти, это очень похоже на традиционную архитектуру NUMA. Физическая память каждого из серверов будет ближе к процессорным ядрам этого же сервера, чем к ядрам процессоров других SMP – серверов. Топология узла NUMA-like может включать в один узел все процессоры, относящиеся к одному шасси. Изменить такое формирование узлов позволяет только настройка Soft-NUMA, о которой речь пойдёт ниже.
                    У NUMA-like обращение к памяти другого сервера подвержено существенным задержкам, точно так же, как это было в традиционной архитектуре NUMA. Это необходимо учитывать при планировании нагрузки системы. Нужно учитывать и увеличение времени ожидания в процессорных очередях. Как ни странно, но численное увеличение числа ядер провоцирует увеличение числа передач контекста пользовательских запросов с одного сервера на другой. Это увеличивает затраты на исполнение запроса, но делает лучше параллелизм.
                    Более сложными становятся протоколы работы ядер с локальным кэшем. Например, в стандартном для SMP протоколе MESI (Modified. Exclusive. Shared. Invalid), используемом для определения актуальности состояния находящегося в кэше контекста, могут появиться дополнительные типы состояний. Обработка новых состояний тоже потребует дополнительных ресурсов. Да и сами размеры кэшей должны быть существенно больше, что обусловлено необходимостью снижения нагрузки на фронтальную шину FSB, если речь идёт о старых платформах Intel. Впрочем, одноранговые межпроцессорные соединения тоже не упрощают и не удешевляют такие решения.
                    Ещё одним отличием от традиционной NUMA является то, что соединяющие шасси серверов шины масштабируемости будут обслуживать не только задачи доступа к удалённой памяти. Межсоединения могут взять на себя ещё и задачи ввода-вывода с устройств, подключаемых по шинам PCI или USB. Такое произойдёт, если запросы ввода-вывода будут направлены с одного шасси на другое. В общем случае, для того, чтобы производительность не страдала от архитектурных особенностей NUMA-like, требуется добиться исполнения следующих трёх условий:

                    1. Частота обращений к удаленной памяти должна оставаться существенно ниже, чем к локальной памяти шасси. Тут стоит стремиться к отношению 20% к 80%.
                    2. Задержки удаленного доступа должны быть незначительны, т.е. они должны отличаться от задержек обращения к локальной памяти не больше чем в 10 раз.
                    3. Пропускная способность межсоединения должна в идеале быть больше, чем та, которая требуется для SQL Server.

                    Бывают случаи, когда использовать возможности NUMA мешают другие аппаратные возможности. Например, у некоторых многопроцессорных серверов на базе процессоров AMD в BIOS может быть включена опция “Node memory interleave”, которая фактически перетасовывает адресное пространство разных узлов и делает невозможным использование возможностей NUMA. Для обеспечения поддержки NUMA эта опция должна быть заблокирована.

                    К оглавлению

                    Поддержка NUMA в операционной системе

                    Поддержка NUMA реализована в Windows Server 2003/2003R2/2008/2008R2 Enterprise Edition и Datacenter Edition. Для того чтобы операционная система могла задействовать предоставляемые NUMA и NUMA-like возможности, ей должно быть передано с аппаратного уровня описание физической топологии системы. Для этого задействуется специальный интерфейс расширений конфигурации, который определяет спецификацию передаваемой операционной системе таблицы статической привязки ресурсов – Static Resource Affinity Table (SRAT). Если на сервере запущено несколько операционных систем, таблица ресурсов будет включать только выделенные каждой системе ресурсы. Таблица привязки ресурсов может изменяться, при добавлении новых ресурсов, например “горячей” памяти, или вследствие физического изъятия ресурсов.
                    Во время запуска операционной системы для каждого узла NUMA формируется граф стоимости доступа ядер процессоров к ресурсам. Оценка стоимости основана на величине задержки запросов на доступ к памяти. Подсистема обслуживания листания памяти в Windows Server дополняется новым типом реакции на события доступа к странице памяти – “soft page fault”. В отличие от “hard page fault”, который показывал, что страницу нужно забрать с диска, этот новый признак говорит о том, что искомая страница находится на дальнем узле.
                    В спецификацию входит понятие доменов близости, которое позволяет объединить локальные ресурсы с точки зрения NUMA-узла в одну, прикреплённую к этому узлу логическую группу. Операционная система использует информацию из таблицы привязки ресурсов для того, чтобы выбрать используемую по умолчанию привязку процессора, процессов и потоков. Механизм доменов близости позволяет системе преимущественно планировать потоки одного процесса на процессоры одного и того же узла NUMA. Кроме того, система старается распределять для такого процесса локальную по отношению к выбранному узлу память. Таким образом система старается минимизировать дорогостоящие обращения процессоров одного узла к ресурсам других узлов. Каждый новый процесс будет планироваться на следующий по порядку NUMA узел.
                    Алгоритм работы с ближней и дальней памятью развивается вместе с появлением новых версий Windows. Мы затронем только те особенности, которые присутствовали в Windows 2003 и проследим некоторые изменения и улучшения этих алгоритмов в последующих версиях.
                    Для управления памятью система создаёт видимые и скрытые пулы для каждого домена близости, которые сопоставляются узлам NUMA. Аналогичным образом распределяется и расширенная для процесса память, доступная через окно трансляции адресов AWE. После первоначального выделения участков памяти для каждого из узлов, система не может динамически перераспределять для нужд приложения уже выделенные участки локальной по отношению к узлу памяти. В Windows 2003 это приводило к тому, что распределение памяти происходило за счёт ресурсов других узлов. Такое наблюдалось в тех случаях, когда поток, которому уже была выделена локальная память, нуждался в дополнительном распределении памяти, и такое распределение уже не возможно было сделать из локальных ресурсов NUMA-узла. Вследствие такого поведения, увеличивались задержки доступа к памяти, поскольку память являлась удалённой по отношению к работающему с ней процессору. Работа с удалённой памятью приводила к снижению производительности за счёт увеличения времени доступа примерно на 100ns. Фактически, по некоторым оценкам, стоимость доступа к удалённой памяти оказывается от 40% до – 300% больше, чем стоимость доступа к локальной памяти. Хотя эта стоимость существенно ниже стоимости доступа к физическим дискам. SQL Server очень часто используется в таких приложениях, которым свойственно большое число потоков. В этом случае, память должна делиться между большим числом потоков или процессов. Операционная система не способна самостоятельно оптимально распределить память, потоки и процессы. Это приводит к тому, что сервер баз данных на системе NUMA будет страдать от частого обращения к удаленным страницам памяти. Ещё одной проблемой становилась такая ситуация, когда процесс короткое время работал на неоптимальном по удалению от памяти узле, мог получать дальнейшее распределения памяти на этом же, неоптимальном узле. Такое распределение тоже снижало эффективность выполняемых операций.
                    В Windows Server 2003 необходимо особое внимание уделить настройке параметров устройств ввода-вывода и управляющего ими программного обеспечения. Это обусловлено тем, что в этой операционной системе возможности обслуживания ввода – вывода с учётом специфики NUMA были реализованы ещё не в полной мере. Если подсистема ввода – вывода постоянно взаимодействует с одним и тем же NUMA – узлом, снижение нагрузки на дисковую подсистему может быть достигнуто за счёт использования механизмов прямого доступа к памяти – DMA. Если же NUMA – узел должен взаимодействовать с внешней дисковой подсистемой, которая подключена к серверу посредством нескольких адаптеров, схема в вода – вывода будет многоканальной. Это потребует такой настройки программного уровня поддержки ввода – вывода, которая обеспечит обслуживание запросов на ввод – вывод по каждому каналу на тех узлах, которые располагают необходимыми для этих запросов ресурсами. В такой конфигурации прямой доступ к памяти не будет разделяться между несколькими узлами. В операционной системе реализованы возможности оптимизации многоканального ввода – вывода для многопроцессорных систем, но эти возможности должны быть предусмотрены производителем внешней дисковой подсистемы, который должен обеспечить необходимую поддержку для программного уровня.
                    Дисковая подсистема пока ещё является одним из ключевых компонентов производительности. Поскольку NUMA-like узел потенциально может иметь прямой доступ к дисковому вводу-выводу, операционная система может получить преимущество, обслуживая прерываниями локальных для узла устройств на локальных процессорах. В Windows Server 2003, операционная система умела получать топологию NUMA, но это ограничивалось получением числа узлов и памяти в каждом из узлов. В следующих версиях ситуация стала существенно лучше.

                    К оглавлению

                    Windows 2008 и NUMA

                    В Windows Server 2008 алгоритмы распределения памяти были существенно доработаны и улучшены. Операционная система теперь старается распределять память на идеальном с точки зрения близости узле. Этот выбор будет сделан даже в том случае, если процесс начал выполняться на узле неоптимальном с точки зрения близости ресурсов. Если у оптимального узла нет свободной памяти, по таблице SRAT будет выбран наиболее близкий к идеалу узел. Такая политика распределения ресурсов повышает вероятность того, что процесс и его ресурсы будут обслуживаться на одном и том же узле или будет выбрана наиболее оптимальная альтернатива. Наверное, самым важным преимуществом Windows Server 2008 по отношению к Windows Server 2003 в поддержке NUMA является то, как оптимально они управляют близостью ресурсов. Улучшения планировщика в этом направлении позволили заметно оптимизировать размещение ресурсов в узлах NUMA.
                    Имеющиеся в операционной системе программные интерфейсы позволяют получать информацию о топологии NUMA из прикладных программ. Кроме того, через эти интерфейсы разработчики могут управлять привязкой задач к NUMA узлам. Приложения Windows могут получать информацию о NUMA через специализированный программный интерфейс (API). Вот несколько доступных для этого функций:

                    • GetNumaHighestNodeNumber – возвращает число узлов;
                    • GetNumaProcessorNode – возвращает номер узла данного процессора;
                    • GetNumaNodeProcessorMask – возвращает бинарную маску процессоров данного узла;
                    • GetNumaAvailableMemoryNode – возвращает размер доступной узлу памяти.

                    Приложения, адаптированные для использования возможностей предоставляемых интерфейсами NUMA , могут в полной мере воспользоваться масштабируемостью современных архитектур, и демонстрировать высокие показатели производительности. К таким приложениям относится SQL Server. Используя упомянутые выше API, высокопроизводительные приложения могут самостоятельно задавать или изменять привязку потоков к процессорам, чтобы они использовали ресурсы домена близости одного узла. Это особенно полезно, когда потоки сильно зависимы от одних и тех же структур памяти. Адаптированные приложения могут создавать множество потоков, и для этих потоков разработчики приложений смогут использовать возможности оптимизации распределений неоднородной памяти. За счёт этого, адаптированные приложения будут более эффективны в системах с числом процессоров более четырёх, и повышение числа процессоров будет позитивно сказываться на общей производительности.
                    В Windows 2008 добавилась возможность получения не только топологии процессоров, но и ввода-вывода. Например, можно узнать, на каких шасси NUMA-like системы размещены адаптеры шины (HBA). Имея такую расширенную информацию, можно заметно оптимизировать использование процессоров, настраивая привязку прерываний устройств к ближним процессорам. Это позволяет оптимизировать использование процессоров для обслуживания запросов ввода-вывода. Можно привязать обслуживающие ввод-вывод прерывания к наиболее оптимальным для производительной работы процессорам. В Windows 2003 ввод-вывод мог обслуживаться не тем процессором, который инициировал ввод-вывод. Таким образом данные могли попадать в память не того узла, через который к ним был получен доступ. Поэтому Windows 2008 старается обслуживать ввод-вывод и процедуры отложенного вызова (DPC) на процессорах того узла, где они были инициированы.
                    Кроме того, в Windows Server 2008 появился новый способ управления прерываниями. В Windows Server 2003 использовались прерывания в виде строки. Прерывание инициировалось устройством, путём подачи электрического сигнала на нужном штырьке (строка прерывания). Такая схема сильно затрудняла привязку нужного процессора к заданному устройству. В Windows Server 2008 устройство генерирует прерывание в виде сообщения, записывая значения данных по специальному адресу. С помощью MSI можно менять приоритет прерывания и для обслуживания прерываний стало возможно указывать конкретные процессоры. Мало того, если система оснащена PCI шиной с поддержкой расширения стандарта MSI-X, управлять обслуживанием прерываний ввода-вывода можно на уровне драйверов устройств. Делается это через специализированные программные интерфейсы Windows 2008. Т.о. прерывание ввода-вывода может быть сразу привязано к тому процессору, который инициировал этот ввод-вывод. Получить дополнительную информацию о поддержке NUMA операционной системой и специализированных функциях можно на сайте Майкрософт, в статье: “NUMA Support“.
                    Диспетчер памяти операционной системы Windows 2008 при размещении невыгружаемого пула, т.е. тех участков оперативной памяти, которые распределяются для ядра и драйверов, учитывает топологию NUMA узлов. Он старается распределять их так, чтобы эти участки памяти выделялись на том NUMA-узле, на котором было инициировано это выделение памяти. Так, например, в случае возникновения необходимости распределения новой страницы PTE (таблица распределения страниц), она окажется на том узле, который инициировал распределение, а не на любом узле, как это было в Windows 2003.
                    В Windows 2008 диспетчер памяти всегда пытается распределять память потоку из пула наиболее подходящего узла, даже если поток в это время обслуживается другим узлом. Если же на идеальном узле недостаточно памяти, диспетчер проанализирует задержки доступа к другим процессорам и узлам, и на основании полученной информации выберет для распределения тот узел, задержки к которому меньше всего. Кроме того, если поток переходит в состояние ожидания доступа к данным или коду, диспетчер памяти переместит соответствующие страницы в список ожидания наиболее удачного для этого потока NUMA-узла.
                    Операционная система Windows Server 2008 выбирает оптимальный процессор на основе приоритетов, и если идеальный процессор недоступен, поток планируется на ближайшем к идеальному процессоре локального узла. Если все процессоры локального узла недоступны, операционная система планирует поток на самом ближнем к локальному узлу процессоре. Такая привязка потока к неоптимальному процессору называется мягкой. Привязку потока к процессору можно сделать жёсткой, чтобы впоследствии этот поток не мог быть привязан к другому NUMA узлу. В случае мягкой или жёсткой привязки, операционная система не может просигнализировать приложению, чтобы оно самостоятельно изменило привязку потока. Кроме того, операционная система не сможет самостоятельно перемещать данные из локальной памяти одного узла в локальную память другого узла. Такое перемещение можно осуществить из приложения. Кроме этого, данные могут быть перемещены естественным путём, за счёт механизма листания, который позволяет выгружать давно неиспользуемые страницы данных и по мере необходимости распределять их снова. В последнем случае высока вероятность того, что данные после повторного распределения окажутся в локальной памяти того узла, к которому относится запрашивающий данные поток.
                    Хотелось бы обратить внимание на то, что вполне вероятна ситуация, когда поток привязан к одному из процессоров NUMA узла, и ему необходимо выполнить распределение памяти, но для этого ему недостаточно локальной памяти этого узла. Важно понимать, что у администратора нет возможности повлиять на распределение памяти в рамках этого узла, и воспрепятствовать распределению потоку дальней по отношению к его узлу памяти. Только в самом приложении, за счёт использования соответствующих программных интерфейсов операционной системы, можно препятствовать тому, чтобы для потока кэшировалась дальняя память. Однако, при таком подходе велика вероятность того, что не занятая приложением дальняя память может быть помечена, как свободная, и будет задействована для других нужд. Вендоры не отмечают каких-либо существенных отличий в адаптации операционной системы к NUMA-like системе, относительно традиционной NUMA. Может возникнуть необходимость в изменении привязки ввода-вывода SQL Server к процессорным ядрам. Например, IBM для своих серверов серии “System x” рекомендует устанавливать адаптеры ввода-вывода (HBA) равномерно распределив их по всем шасси (как вариант: по 2 в каждом шасси). Если адаптеры ввода-вывода устанавливаются не во все шасси, то с помощью параметра глобальной конфигурации сервера “affinity I/O mask” лучше настроить привязку ввода-вывода для ядер тех узлов, в домене близости которых расположены имеющиеся адаптеры, т.е. в тех шасси серверов, куда физически адаптеры были установлены. По поводу привязки сетевых интерфейсов из разных шасси инженеры из IBM рекомендуют при планировании использования для нужд SQL сервера нескольких IP-адресов (например, для балансировки нагрузки или для обеспечения гарантированной производительности передачи данных пользователей и серверов приложений), и привязывать эти адреса к разным NUMA-узлам. Если планируется использовать только один IP-адрес, то никакой привязки делать не надо.

                    К оглавлению

                    Windows 2008 R2 и NUMA

                    Начиная с Windows Server 2008 R2 добавлена возможность работы сервера с числом процессорных ядер больше 64-х. Это изменение напрямую повлияло на поддержку операционной системой многопроцессорных архитектур NUMA и non-NUMA. Наиболее заметным новшеством стало добавление ещё одной сущности – групп процессорных узлов. Если процессорных ядер больше 64 – число групп становится больше одной. По существу, с помощью механизма групп разделяются зоны планирования потоков, концентрируя в каждой группе возможности предыдущей версии Windows Server 2008. Это означает, что процесс может работать с несколькими группами одновременно, а поток может исполняться только в рамках одной группы. Кроме того, прерывание может вызываться только для процессоров той же группы. В рамках одной группы работа драйверов и приложений происходит точно так же, как это было в системах, где число процессоров не превышало 64. Это позволяет сохранить обратную совместимость для приложений, которые не были рассчитаны на работу с числом процессоров больше 64. Кроме этого, с помощью групп можно локализовать те аппаратные компоненты, работа которых зависит от места запуска связанных с ними программ, что может положительно сказаться на работе таких программно-аппаратных комплексов.
                    Каждая группа представляется статическим набором ядер, число которых не превышает 64. В Windows Server 2008 R2 администратору не предоставлено возможности влиять на формирование групп. Принадлежность ядер группе устанавливается во время начальной загрузки Windows, и каждое процессорное ядро может включаться только в одну группу. Операционная система старается минимизировать число групп. Кроме того, все логические процессоры ядра, и все ядра одного физического процессора тоже помещаются в одну и ту же группу. В одной и той же группе оказываются те процессоры, которые физически близки друг к другу. Группа может содержать процессоры одного или нескольких узлов архитектуры NUMA. Если в одном узле ядер больше 64-х, этот узел может быть поделён между несколькими группами. Если сервер non-NUMA, формирование групп основано только на ограничении в 64 ядра, а ядра по группам распределяются равномерно. Концепция групп процессорных ядер допускает горячее добавление процессоров. Если в системе есть сокеты, куда можно будет добавить процессоры, это будет учтено при создании групп, чтобы горячее добавление процессоров не привело к нарушению уже изложенных выше принципов формирования групп.
                    Каждый процесс или порождённые им процессы могут быть привязаны к неограниченному числу групп, однако в каждый момент времени одиночный процесс может принадлежать только одной группе. Разработчики операционной системы старались обеспечить наилучшую производительность приложений, потоки которых обслуживаются в одной группе (прежде всего, в целях поддержки унаследованных приложений). Кроме того, выбор процессорной группы для приложения может быть обусловлен близостью к тем аппаратным компонентам, к которым приложение обращается. Если приложение явно распределяет свои потоки по нескольким группам, потери производительности не произойдёт только при условии, что работа потоков из разных групп независима, например, приложение умеет выделять независимые секции данных для этих потоков. Иначе, производительность может оказаться существенно ниже варианта с обслуживанием потоков в одной группе.
                    В прежних версиях Windows, процесс или поток могли быть привязаны к указанному процессору, что гарантировало их исполнение на этом процессоре. В Windows Server 2008 R2 это стало немного сложнее, добавилась концепция групп. Вначале процессы не распределяются последовательно между процессорами групп. Процесс начинает исполняться в рамках только одной группы. Первый поток процесса будет исполняться в той группе, которую ему назначила Windows, если это не изменить из приложения (такая возможность существует и реализуется посредством интерфейсов). Каждый новый поток будет по умолчанию назначен в ту же группу, где обслуживается создавший его поток. Однако, при создании потока, приложение может определить группу, на которую он назначен.
                    В начале, все потоки процесса создаются в одной группе. Получить назначение в несколько групп может только системный процесс во время запуска системы. Все другие процессы должны быть явно назначены в несколько групп. Это им нужно для того, чтобы использовать все присутствующие в системе процессоры. Т.о. процесс может разрастись, и его потоки будут присутствовать во всех группах, но каждый поток единовременно может исполняться только в одной группе, хотя и может потом сменить её на другую. Смена группы потока отдаётся на откуп приложению, которое будет ответственно за привязку потока к правильной группе, считается, что разработчик может сделать это лучше. Если ничего не предпринимать, то каждое приложение будет удерживаться в рамках одной группы.
                    Системный пул потоков тоже был доработан и поддерживает теперь привязанную к узлу очередь. Это означает, что Windows будет планировать задачи из очереди узла для потоков этого узла. Если процесс в этом узле недоступен, Windows гарантирует, что задача будет обслужена в той же группе, из которой она попала в очередь. Такой механизм облегчает сохранение близости задачи приложения к её ресурсам. Однако есть несколько документированных исключений из последнего правила, которые выходят за рамки темы этой статьи.

                    К оглавлению

                    NUMA I/O

                    Для настройки привязки прерываний устройств ввода-вывода к процессорам или узлам используется специализированный инструмент Майкрософт, который называется Interrupt-Affinity Policy Tool (IntPolicy). Привязка прерывания к одному процессору или группе в документации называется “Interrupt Affinity”. Ранее для аналогичных целей использовался Interrupt-Affinity Filter (IntFiltr). Для устройства ввода-вывода IntPolicy позволяет выбрать одну из политик доступности данного устройства или задать маску привязки процессоров. Но даже без такой искусственной привязки, Windows Server 2008 будет стараться обслуживать ввод-вывод на тех процессорах и распределять для него память того узла, который является локальным для этого устройства ввода-вывода. Всё это стало возможно из-за усовершенствования механизма прерываний в Windows Server 2008. Пример использования утилиты IntPolicy для привязки прерываний сетевых интерфейсов можно найти в статье Майкрософт: We Loaded 1TB in 30 Minutes with SSIS, and So Can You. Следует помнить, что неверный выбор привязки прерываний может привести к деградации производительности.
                    Прерывание может применяться к процессорам только одной группы. В Windows Server 2008 R2 появилась возможность для PCI-адаптеров систем хранения динамически переадресовать прерывания и отложенные вызовы процедур. В документации эта функциональность названа соответственно: “Dynamic interrupt redirection” и “DPC redirection”, где DPC это аббревиатура: “Deferred Procedure Call”. Такой функционал получил название “NUMA I/O”. Задача NUMA I/O – помочь многопроцессорной системе лучше секционировать рабочую нагрузку, повысить норму удачного попадания в кэш, и высвободить встроенные аппаратные средства межсоединений от передачи большого трафика ввода-вывода.
                    Windows Server 2008 R2 из коробки поддерживает работу сетевых адаптеров по протоколу Receive Side Scaling (RSS). Эта реализация RSS также адаптирована к NUMA. Данные из сетевых пакетов, которые посредством RSS распределяются между процессорами, будут обслуживаться теми же процессорными ядрами, которые обслуживают это TCP-подключение. Пакеты будут переданы на обслуживание физическим процессорам, без учёта гиперпоточности. Операционная система, балансируя средствами RSS входящие пакеты между процессорами, учитывает близость ресурсов узлов NUMA. Причём, при запуске адаптеры с более высокой пропускной способности получают больше процессоров, а несколько равноценных адаптеров поделят имеющиеся процессоры поровну. В системном реестре, в ветке “HKLM\system\CurrentControlSet\Control\class\{XXXXX72-XXX}\<номер сетевого адаптера>\”, можно найти несколько ключей, которые показывают закрепление процессоров за адаптерами:

                    • RssBaseProcNumber – номер первого процессора из диапазона выделенных RSS адаптеру процессоров.
                    • MaxRSSProcessors – максимальное число процессоров для этого адаптера.
                    • NumaNodeID – NUMA узел на котором адаптер может распределять память.

                    К оглавлению

                    Hard-NUMA

                    В SQL Server поддержка архитектуры NUMA появилась, в ограниченном виде, начиная с SQL Server 2000 SP4. После этого, разработчиками следующей версии SQL Server 2005 была проделана очень большая работа по совершенствованию механизмов взаимодействия с аппаратной платформой и операционной средой. Были выполнены необходимые доработки компонентов ядра сервера баз данных, для того чтобы обеспечить поддержку новшеств, появившихся в SQL Server 2005. Одной из первостепенных задач при разработке компонентов ядра было повышение масштабируемости сервера за счёт использования возможностей, заложенных в современные аппаратные платформы многопроцессорных серверов. В SQL Server 2005 поддержка NUMA была добавлена без каких-либо оговорок. Эта поддержка подразумевает, что планировщики непривилегированного режима (UMS) автоматически группируются точно так же, как группируются в NUMA узлы физические процессорные ядра. Необходимую для этого информацию получает специальный программный слой ядра сервера баз данный – SQLOS. Для этих целей используются описанные ранее программные интерфейсы операционной системы.
                    Операционная система передаёт в SQL Server аппаратную конфигурацию NUMA, которую принято называть Hard-NUMA. SQL Server создает для каждого узла памяти свой логический планировщик, так, чтобы привязка планировщиков соответствовала аппаратной конфигурации. Изменить привязку планировщиков к ядрам процессоров можно с помощью системной хранимой процедуры sp_configure и параметра глобальной конфигурации сервера “affinity mask”. Впоследствии SQL Server старается удерживать планировщиков за своими узлами, если только какой-нибудь процессор не выйдет из строя или не будет отключен.
                    Если с аппаратного уровня передаётся информация о наличии NUMA – системы, но в топологии присутствует всего один процессор, SQL Server поведёт себя так, как будто он имеет дело с компьютером без NUMA (Non-NUMA).
                    Для администратора баз данных полезным является тот факт, что при запуске службы SQL Server обнаруженная конфигурация NUMA выводится в виде сообщения в журнал ошибок SQL Server. По этим сообщениям администратор может судить о том, какая конфигурация процессорных узлов используется сервером баз данных в настоящий момент.
                    В случае с Hard-NUMA, при изменении параметра глобальной конфигурации “max server memory”, память для экземпляра SQL Server будет равномерно поделена между доступными ему узлами. SQLOS старается так распределить страницы буферного пула между узлами Hard-NUMA, чтобы потом потоки обращались к страницам буферного пула преимущественно в домене близости локального узла, а не из памяти удалённого узла. Однако возникает необходимость контроля равномерности распределения памяти между узлами. Во время отработки сигнала вытеснения памяти в системе с Hard-NUMA на процесс управления буферным пулом SQL Server будет влиять физическое расположение страниц памяти. Т.е. по сути, это повлияет на то, попадут ли страницы в домен близости данного узла. Однако если страница памяти оказалась вне домена близости узла, к которому относится работающий с ней поток, меры к перемещению страниц буферного пула в ближнюю память предприняты не будут. Если для работы SQL Server выделены не все процессоры, это означает, что при запуске будет предпринята попытка равномерного разделения буферного пула между всеми выделенными экземпляру процессорными узлами. Чтобы не допустить использование под буферный пул одного экземпляра всей оперативной памяти, необходимо задать максимальный размер используемой экземпляром памяти.
                    Встроенный в SQL Server стабилизатор нагрузки может перемещать процессы от одного процессора к другому. Следуя логике доменов близости, привязка процесса подразумевает то, что он не будет перемещён на процессор, который относится к другому узлу, т.е. на процессор вне домена близости первоначального процессора.
                    Когда сервер баз данных работает с Hard-NUMA, системный процесс отложенной записи (Lazy Writer) будет присутствовать в одном экземпляре на каждом процессорном узле. Сделано это для того, чтобы работа с памятью была локализована внутри домена близости каждого узла, а также это способствует сокращению числа страниц вне домена близости. Процесс отложенной записи будет вызываться для обслуживания каждой явной и неявной контрольной точки, поэтому работа на NUMA-системе приведёт к увеличению частоты появления контрольной точки.
                    С помощью системной хранимой процедуры sp_configure можно на ходу менять привязку процессоров к экземпляру сервера баз данных. Т.о. можно отключить процессоры, процессорные узлы и обслуживающие их планировщики. В Hard-NUMA, пока хотя бы один планировщик активен, активным считается и весть NUMA узел. Узел может считаться отключенным, только если все приписанные к нему планировщики отключены. Используемая до этого узлом память будет высвобождена и перераспределена между другими узлами. Если перераспределение памяти нежелательно, необходимо соответствующим образом уменьшить максимальный размер выделяемой серверу памяти. Исполнители, которые работали с отключенным планировщиком, перейдут к активным планировщикам.
                    Есть небольшая хитрость в том, к какому узлу в Hard-NUMA будут привязаны планировщики с самыми первыми идентификационными номерами. Дело в том, что SQL Server учитывает тот факт, что нулевой физический процессорный узел после запуска операционной системы будет загружен сильнее других, и у него будет меньше других свободной физической памяти. Поэтому, SQL Server перемещает узел по умолчанию с нулевого физического узла на другой узел, вследствие чего основные структуры данных для SQL Server будут обслуживаться на узле, который не так сильно обременён задачами операционной системы. Однако это не означает, что нулевому физическому процессорному узлу память SQL Server распределяться не будет, ему достанется примерно одна треть от нормы.

                    К оглавлению

                    SQLOS и NUMA

                    Чтобы лучше понять базовые принципы дизайна и взаимодействия компонент SQLOS в многопроцессорной среде, давайте немного углубимся в архитектуру SQLOS. Узлы процессоров являются подмножеством узлов памяти. Для наглядности, этот факт проиллюстрирован на Рисунке 1. Более подробно о месте узлов памяти в архитектуре SQLOS можно узнать в серии переводов статей Славы Окс на сайте sql.ru: Архитектура SQL Server.

                    SQLOSРисунок 1. Вариант упрощённой блок – схемы SQLOS

                    Как видно из рисунка, основным элементом управления процессорными ресурсами является узел памяти SQLOS. Все процессорные ядра, попадающие в один узел NUMA, объединяются в узел процессора SQLOS, который сопоставляется с одним узлом памяти SQLOS. Т.о. производительность сервера может быть оптимальной, если приложение способно ограничиваться ресурсами одного узла памяти, либо оно физически так секционировано, что каждой секции данных достаточно одного узла памяти. Это достигается за счёт сбалансированной з агрузки доступных экземпляру сервера аппаратных и программных ресурсов. Однако даже если удастся оптимально секционировать нагрузку по процессорным узлам, останется возможность конфликтов за ресурсы памяти для ядер одного процессорного узла. Такие конфликты возможны, когда ядра разделяют в своей работе одну и ту же область оперативной памяти или общий кэш сокета.
                    В идеале, нагрузка пользовательских приложений должна секционироваться по всем процессорам или узлам NUMA. В реальных условиях этого достичь очень трудно, особенно, если такое секционирование не было заложено на этапе первоначального дизайна приложения. Операционная система и сервер баз данных должны настраиваться таким образом, чтобы каждый поток попадал на отдельный процессор и на этом же процессоре должно обслуживаться прерывание отдельного сетевого интерфейса, а также порта обслуживания дискового ввода-вывода. Т.е. число сетевых плат и дисковых контроллеров (или HBA) должно равняться числу процессорных ядер, или хотя бы числу узлов NUMA. Продвинутые модели сетевых коммутаторов умеют поддерживать работу с множеством сетевых плат одного сервера. В качестве альтернативы большому числу сетевых плат можно использовать современные сетевые адаптеры, которые поддерживают Receive Side Scaling (RSS) и адаптированы для NUMA архитектур.
                    В реальных системах достичь равномерного распределения нагрузки между ресурсами сервера оказывается очень сложно. Вероятность обращений к ресурсам вне домена близости процессора оказывается достаточно высокой. Однако прогресс не стоит на месте, и современные архитектурные решения позволяют существенно снизить потери от обращений к ресурсам других узлов. В первую очередь это относится к архитектурам, где процессор обращается к памяти посредством собственного контроллера, по схеме точка – точка, а не через общую шину. Чаще всего, приходится жертвовать оптимизацией загрузки узлов и ядер. Это обусловлено не только сложностью такой оптимизации, но и то, что потери на межузловых взаимодействиях с каждым годом становятся заметно меньше, а встроенные возможности операционных систем и адаптированных к NUMA приложений становятся всё лучше и лучше. Однако стоит помнить, что в резерве остаётся возможность повысить эффективность за счёт приближения распределения нагрузки между ресурсами сервера к идеалу.
                    Некоторые структуры данных, такие как блокировки или планы исполнения запросов, были адаптированы к использованию на NUMA системах. Такие структуры контролируют своё местоположение и стараются оставаться на том же узле, где они были созданы. Например, блокировки связаны со структурами данных, которые используются для обслуживания транзакций. Если бы они не придерживались единого местоположения, это могло бы породить проблемы. К примеру, стала бы возможна ситуация, когда, несколько организованных координатором распределённых транзакций сеансов попадут для обслуживания на разные узлы. Получается, что один из сеансов должен будет завершить всю транзакцию и получить доступ ко всем структурам данных блокировок. Это приведёт к потерям производительности, поскольку структуры памяти блокировок рассредоточены по разным узлам. По той же самой причине контролирует своё местоположение и буферный пул, т.е. обслуживающие блокировки структуры памяти будут возвращены в тот список свободных буферов, который относится к тому узлу, который владеет выделяемой под структуры памятью.
                    Посмотреть, сколько памяти распределено на каждом узле, можно с помощью команды DBCC MEMORYSTATUS. Описание этой команды можно найти в статье Базы Знаний Майкрософт: “How to use the DBCC MEMORYSTATUS command to monitor memory usage on SQL Server 2005“.
                    Давайте рассмотрим особенности работы SQL Server со страницами памяти и буферами, которые для процессорного ядра считаются удалёнными, т.е. принадлежащими другому узлу процессора SQLOS. Буферный пул SQL Server может находиться в трёх состояниях. Вначале происходит инициализация буферного пула. Следующее состояние некого переходного периода, когда удалённые относительно локальных процессорных узлов буферы возвращаются операционной системе. После переходного периода наступает устойчивое состояние, когда положение буферов в пуле стабилизируется относительно узлов. Восьмикилобайтный буфер относительно узла может быть внешним или локальным. Число внешних буферов можно определить с помощью счётчика производительности: SQL Server: Buffer Node: Foreign pages. Он показывает число страниц, не относящихся к страницам памяти узла процессора SQLOS, т.е. распределённых узлу из дальней о тносительно узла памяти.
                    В начальном состоянии буферный пул каждого узла увеличивается до достижения расчётного максимума. При этом SQL Server сортирует выделяемые узлу буферы по двум спискам. В лист свободных буферов попадают те буферы, которые в ближней к узлу памяти. Остальные буферы попадают в лист неблизких буферов. Чтобы сократить использование удалённой памяти, SQL Server не использует буферы из второго списка, Увидеть распределение буферов по разным типам можно с помощью команды DBCC MEMORYSTATUS. К сожалению, в административном динамическом представлении sys.dm_os_buffer_descriptors нет информации о том, какими являются буферы, внешними или локальными. После того, как число буферов достигнет требуемого значения, лист неблизких буферов сбрасывается. Буферы, попавшие в лист неблизких буферов, возвращаются системе. Пока этого не произойдёт, дальнейший “захват” памяти под буферный пул не выполняется. После сброса неблизких буферов, процесс повторяется. Когда буферный пул, после завершения переходной фазы, достигнет заданных величин, его состояние стабилизируется. Листы свободных буферов закрепляются за узлами окончательно, а лист неблизких буферов больше не используется. В таком состоянии, SQL Server пытается по возможности распределять местную память из листа свободных буферов. Если будет использована внешняя страница, это будет отражено в значениях представленного чуть выше счётчика Foreign pages. Т.е., несмотря на то, что SQL Server во время запуска пытается оптимизировать выделение буферов в соответствии с их близостью узлу, наличие внешних страниц не исключается. Мало того, все последующие распределения могут включать внешние страницы. Последующие сжатия и рост буферных пулов NUMA узлов также может приводить к увеличению числа внешних страниц. Для решения подобной проблемы, может оказаться полезным установить в параметрах глобальной конфигурации SQL Server одинаковых значений для максимального и минимального объёма оперативной памяти, выделяемой экземпляру сервера.
                    Дополнительную информацию можно найти в статьях:

                    Важно также помнить, что SQL Server использует для всех NUMA узлов один и тот же откомпилированный план исполнения запроса, но этот план базируется на использовании локальной памяти узла.
                    Выделенное Административное Соединение (DAC) тоже зависит от того, используется ли NUMA система. SQL Server просто создает в одном из узлов планировщик и узел памяти, необходимые для DAC, и привязывает порт DAC к этому узлу. Дополнительную информацию о поддержке SQL Server архитектуры NUMA можно найти в электронной документации Microsoft SQL Server Books Online: “Как SQL Server поддерживает архитектуру NUMA“.

                    К оглавлению

                    Soft-NUMA

                    В SQL Server 2005 появилась возможность создавать программные абстракции физических NUMA-узлов, которые могут переопределять число NUMA-узлов и процессорный состав узлов. Это предоставляет администратору баз данных возможность самостоятельно изменить порядок группировки процессоров, с учётом особенностей архитектуры сервера. Такие абстракции получили название Soft-NUMA. Создавать эти абстракции можно путём добавления специальных ключей в системный реестр операционной системы. Подразумевается, что в каждый такой узел может входить один или несколько процессоров.
                    Soft-NUMA влияет на компоненты SQLOS, которые адаптированы к NUMA. Ели сервер и платформа поддерживают NUMA, то для расщепления узлов Hard-NUMA можно использовать узлы Soft-NUMA. С помощью узлов Soft-NUMA можно перераспределить планировщики и привязать к узлам порты сетевых интерфейсов. Soft-NUMA позволяет вносить изменения только в работу планировщиков и сетевых интерфейсов SQL Server (то, что в англоязычной документации относится к I/O Completion Threads). Число и привязка узлов памяти остаётся неизменно. Это нужно учитывать, т.к. с помощью Soft-NUMA невозможно изменить привязку памяти к узлам Hard-NUMA. Кроме того, узлы Soft-NUMA не получают свой процесс отложенной записи, как это происходит с физическими узлами.
                    Убедиться в том, что число потоков отложенной записи не превышает числа узлов Hard-NUMA, позволяет следующий сценарий:

                      SELECT scheduler_id FROM sys.dm_os_workers AS w
                      JOIN   sys.dm_os_schedulers AS s
                      ON     w.scheduler_address = s.scheduler_address
                      AND    w.last_wait_type LIKE '%LAZYWRITER%'

                    Важным является также тот факт, что использование Soft-NUMA не изменяет поведение буферного пула (Buffer Pool Locality). Во время инициализации буферного пула происходит его распределение для Hard-NUMA или для Soft-NUMA. В случае с Soft-NUMA, получается, что память узла может резервироваться из локальной и условно удалённой относительно такого узла памяти. Расположение страниц памяти буферного пула не отслеживается, как это делается в Hard-NUMA. Т.е. возможна ситуация обращения к удалённой памяти, что потенциально может привести к снижению производительности. С другой стороны, удалённая для Soft-NUMA узла память может оказаться (и, скорее всего, окажется) локальной для узла Hard-NUMA. Как было описано выше, сервер старается минимизировать работу с удалёнными страницами, что способствует их высвобождению. А поскольку новые страницы распределяются с приоритетом у локальной по отношению к узлу памяти, ситуация с большим количеством страниц в удалённой памяти может постепенно улучшаться.
                    Если по каким – либо причинам нежелательно разделение буферного пула между узлами Hard-NUMA, есть возможность изменить это поведение. Заставить SQL Server работать со всей памятью, выделенной под буферный пул, как с единственным узлом памяти (плоский доступ), можно включив флаг трассировки 8015, который отключает поддержку NUMA в SQL Server. Подробности о таком режиме использования буферного пула можно найти в статье: How It Works: Soft NUMA, I/O Completion Thread, Lazy Writer Workers and Memory Nodes.
                    По сути, наиболее важными возможностями Soft-NUMA являются две вещи. С помощью Soft-NUMA можно жёстко задать какие процессорные ядра будут задействованы для обслуживания запроса, направленного на заданный порт сетевого интерфейса. Это будет относиться только к пакетам TDS, и не будет касаться активности базы данных и журналирования этих операций. Каждый узел Soft-NUMA может иметь ассоциированный с ним порт сетевого ввода-вывода. В соответствии с выбранными настройками сетевого протокола, такие порты могут прослушиваться на разных сетевых интерфейсах, что позволяет балансировать не только процессоры, но и сетевой трафик, используя для этого сегментацию локальной сети. Т.е. запросы клиентов будут утилизировать те процессоры, Soft-NUMA узел которых привязан к указанному клиентом порту в строке подключения. Вторая возможность, это увеличение с помощью Soft-NUMA числа потоков завершения ввода-вывода (I/O Completion), о чём пойдёт речь ниже.
                    Soft-NUMA узлы можно создавать и для систем с обычной архитектурой симметричного доступа процессоров к памяти (SMP). Например, для Non-NUMA серверов, которые оборудованы многоядерными процессорами, характерно совместное, конкурентное использование кэшей второго и/или третьего уровней. Узлы Soft-NUMA можно определять на основе близости процессоров к таким кэшам. В этом случае за счёт объединения в один узел нескольких процессорных ядер оптимизируется использование ими кэша третьего или второго уровня. Также это позволяет частично балансировать загрузку процессоров, распараллеливая рабочую нагрузку среди процессоров Soft-NUMA узла. К слову, можно отметить, что некоторые возможности балансирования нагрузки между процессорами присутствовали и в предшествующих SQL Server 2005 версиях. К таким возможностям можно было отнести: параметры глобальной конфигурации “max degree of parallelism” и “cost threshold for parallelism”, подсказку оптимизатору MAXDOP, опцию сервера и запроса – “query governor cost limit”, а также методы, подобные описанным в книге Кена Хендерсона “Профессиональное руководство по SQL Server: хранимые процедуры, XML, HTML”, и реализованных в виде расширенной хранимой процедуры xp_setpriority.
                    В случае серверов Non-NUMA, наиболее значительный эффект Soft-NUMA может дать для оптимизации ввода-вывода. Кроме уже упомянутого выше выигрыша от оптимизации за счёт снижения конкурентного доступа к кэшу третьего или второго уровня, дополнительный выигрыш можно получить за счёт увеличения числа потоков ввода-вывода. Увеличение числа потоков происходит потому, что для каждого Soft-NUMA узла будет создан свой поток завершения ввода-вывода. В Non-NUMA сервере существует только один узел с точки зрения NUMA. Современные многоядерные процессоры способны содержать до шести и больше ядер. Т.о. потенциально можно увеличить число потоков ввода-вывода во столько раз, сколько ядер размещено на одном кристалле процессора.
                    Получить информацию о процессорах и Soft-NUMA узлах, которые доступны SQL Server, можно с помощью специального динамического административного представления (Dynamic Management Views), вызвать которое можно так:

                      SELECT * FROM sys.dm_os_schedulers;
                      GO

                    Это динамическое представление выводит по одной строке для каждого присутствующего в системе планировщика. Каждый планировщик закреплён за одним из процессоров. Представление позволяет диагностировать состояние планировщиков и определять меру их активности, для чего в представлении присутствует множество полей со счётчиками разнообразных событий планировщиков. Принадлежность планировщика к NUMA узлу можно определить по полю parent_node_id, а по полю cpu_id можно определить привязку к процессору. Значение равное 255 в последнем из этих двух полей говорит о том, что планировщик не привязан ни к одному из присутствующих в системе процессоров. Другие поля позволяют определить статус планировщика, т.е. является ли он активным или скрытым, поле is_online позволяет определить используется ли данный планировщик, поле current_workers_count позволяет определить, сколько исполнителей обслуживается этим планировщиком. Более подробную информацию об этом динамическом системном представлении можно получить в комплекте электронных материалов SQL Server 2008 Books Online, поставляемых с дистрибутивом. Ознакомьтесь со статьёй “sys.dm_os_schedulers (Transact-SQL)“.
                    Для того чтобы проверить, обеспечена ли аппаратная или программная поддержка архитектуры NUMA (Hard-NUMA или Soft-NUMA) можно воспользоваться сценарием из блога Славы Окс (blogs.msdn.com/b/slavao), одного из разработчиков подсистем SQLOS:

                      SELECT CASE COUNT(DISTINCT parent_node_id)
                      WHEN 1 THEN 'Поддержка NUMA отключена'
                      ELSE 'Поддержка NUMA включена'
                      END
                      FROM   sys.dm_os_schedulers
                      WHERE  arent_node_id <> 32

                    Существует ряд ограничений на количество и состав входящих в Soft-NUMA узел процессоров. Эти ограничения зависят от архитектуры сервера и числа процессоров, входящих в аппаратный NUMA – узел. Так, для SMP архитектуры, нельзя допускать, чтобы один и тот же физический процессор входил в состав более одного Soft-NUMA узла. В архитектуре NUMA нельзя включать в один Soft-NUMA узел процессоры из разных NUMA узлов.
                    Описание Soft-NUMA узла создаётся вручную в системном реестре операционной системы. Для размещения конфигураций узлов необходимо создать специальный раздел:

                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\90\NodeConfiguration]

                    В этом разделе создаются по одному подразделу для каждого Soft-NUMA узла, и эти подразделы должны называться Node0, Node1, Node2 и т.д. Например:

                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\90\NodeConfiguration\Node0]

                    В каждом разделе создаётся ключ-параметр с названием CPUMask, например:

                      "CPUMask"=dword:00000002

                    Последний пример задаёт шестнадцатеричное значение маски второго физического процессора. Значения маски процессоров устанавливается точно так же, как это делалось для глобального параметра конфигурации SQL Server, известного как: Affinity Mask. Значение этого параметра является целым числом размером в 4 байта и может быть установлено в окне редактирования параметров системного реестра в режиме десятичных или шестнадцатеричных значений.
                    Дополнительную информацию о том, как настраивать Soft-NUMA узлы, можно получить в электронной документации Microsoft SQL Server Books Online: “Как настроить сервер SQL Server на использование программной архитектуры NUMA“.
                    После определения Soft-NUMA узлов, соответственно должен измениться и порядок закрепления планировщиков непривилегированного режима за этими узлами. Новый порядок закрепления планировщиков повлияет на порядок обслуживания этими планировщиками реальных, физических NUMA – узлов. Это может быть полезно, когда системное окружение SQL Server не позволяет равномерно распределить ресурсы узлам памяти и требуется внести коррективы, чтобы сделать такое распределение более равномерным. Администратор, определяя Soft-NUMA узлы, может постараться так перегруппировать процессорные узлы, чтобы сгладить возможные неравномерности распределения системой ресурсов между NUMA узлами.
                    Для того чтобы было легче понять, какими средствами обладает администратор в случае необходимости переопределения процессорных узлов, давайте рассмотрим жизненный пример, который можно найти в отчётах по эталонному тесту TPC-C. Информацию об этом тесте можно почерпнуть на сайте tpc.org. Речь идёт о результате в некластерной группе, показанном на сервере HP Integrity Superdome. Результат был опубликован 7 июня 2005г. На этом сервере удалось получить 1082203 tpmC.
                    Изучая полную версию отчёта, которую можно прочитать на странице краткого описания используемой в эталонном тесте аппаратно – программной конфигурации, можно понять как настраивалась Soft-NUMA. В отчёте легко обнаружить следующие дополнения, которые были внесены в системный реестр операционной системы тестируемого сервера, и которые показаны ниже:

                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\90\NodeConfiguration\Node0]
                      "CpuMask"=hex:0F,00,00,00,00,00,00,00
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\90\NodeConfiguration\Node1]
                      "CpuMask"=hex:F0,00,00,00,00,00,00,00
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\90\NodeConfiguration\Node2]
                      "CpuMask"=hex:00,0F,00,00,00,00,00,00
                      * * *
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\90\NodeConfiguration\Node13]
                      "CpuMask"=hex:00,00,00,00,00,00,F0,00
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\90\NodeConfiguration\Node14]
                      "CpuMask"=hex:00,00,00,00,00,00,00,0F
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\90\NodeConfiguration\Node15]
                      "CpuMask"=hex:00,00,00,00,00,00,00,70
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\90\NodeConfiguration\Node16]
                      "CpuMask"=hex:00,00,00,00,00,00,00,80

                    Звёздочками в представленном здесь фрагменте ключей системного реестра заменены ключи нескольких программных узлов, алгоритм создания которых можно легко понять, анализируя представленные в отрывке значения CpuMask.
                    Суть этого решения сводится к тому, что операционная система в процессе загрузки, следуя внутреннему алгоритму очередности использования процессоров, планирует задачи начиная с первого процессора. Если для SQL Server поменять порядок очерёдности выбора процессоров на обратный, тогда SQL Server не будет нагружать во время своего запуска те же процессоры, которые нагружаются операционной системой. Следовательно, после запуска всех служб, процессорные узлы будут более сбалансированно распределять между собой ресурсы, и система быстрее придёт к равновесному их распределению. Для этого, Soft-NUMA узлы переопределяют в представленном выше примере реальные NUMA – узлы таким образом, чтобы последняя четвёрка процессоров последнего NUMA – узла стала первым Soft-NUMA узлом. Следуя этой логике, предпоследний реальный NUMA – узел переопределяется во второй Soft-NUMA узел и так далее, плоть до пятнадцатого узла.
                    Определение пятнадцатого и шестнадцатого Soft-NUMA узлов немного отличается. Суть этих отличий в том, что первый процессор второго реального NUMA – узла выделен в отдельный программный узел и на этот узел средствами специального скрипта посылается задача исполнения контрольной точки для тестовой базы данных. Это требование определяется спецификой организации тестирования и реализацией, которая была избрана компанией Hewlett-Packard.
                    Продемонстрированный Вам пример наглядно показывает, как с помощью абстракций процессорных узлов можно повлиять на распределение ресурсов между процессорами и выделить отдельные процессоры или их группы для решения отдельных задач или для обслуживания отдельных процессов, например, процесса контрольной точки.
                    29 октября 2005 года компания HP представила ещё лучший результат, который был получен путём изменения схемы разбивки и перераспределения физических процессоров на узлы Soft-NUMA. Использовался сервер HP Integrity Superdome 64P c/s, на котором удалось получить 1231433 tpmC. Ниже представлены в сокращённом виде ключи системного реестра, определяющие конфигурацию программных узлов NUMA:

                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\90\NodeConfiguration\Node0]
                      "CpuMask"=hex:01
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\90\NodeConfiguration\Node1]
                      "CpuMask"=hex:02
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\90\NodeConfiguration\Node2]
                      "CpuMask"=hex:0c
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\90\NodeConfiguration\Node3]
                      "CpuMask"=hex:30
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\90\NodeConfiguration\Node4]
                      "CpuMask"=hex:c0
                      * * *
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\90\NodeConfiguration\Node32]
                      "CpuMask"=hex:00,00,00,00,00,00,00,c0

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

                    К оглавлению

                    Soft-NUMA для Non-NUMA

                    Кроме NUMA, сегодня существует ещё несколько архитектур, которые направлены на реализацию масштабируемых многопроцессорных решений. Большое распространение получают многоядерные процессоры и платформы, с числом процессорных сокетов более четырёх. Часто, многоядерный процессор содержит общий кэш для своих ядер, или на кристалле процессора размещаются другие, общие для всех ядер элементы, например, контроллеры памяти. Такая компоновка сокета тоже вносит неоднородности в доступе к ресурсам, но это не представляется системе, как NUMA архитектура. Т.е. система не получает таблицу SRAT. По сути, Soft-NUMA становится единственным инструментом администратора, позволяющим указать SQL Server на присутствующую неоднородность. Давайте посмотрим, как это делается, на примерах.
                    22 ноября 2005г. сервер IBM eServer xSeries 460 16P c/s показал результат TPC-C равный 492307 tpmC. Сервер был оснащён двуядерными процессорами Intel Xeon Processor 7040 3.00GHz/2x2MB L2, каждое ядро которых использовало технологию гипертрейдинг. Таким образом, каждый физический процессор в операционной системе представлялся четырьмя логическими процессорами. Для того чтобы обозначить домены близости ресурсов каждого физического процессора, специалисты в IBM объединили четвёрки логических процессоров в Soft-NUMA узлы. Это позволяет учитывать реальное секционирование процессорных кэшей и оптимизировать число потоков завершения ввода-вывода.

                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\90\NodeConfiguration\Node0]
                      "CpuMask"=hex:00,00,00,00,00,00,00,0f
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\90\NodeConfiguration\Node1]
                      "CpuMask"=hex:00,00,00,00,00,00,00,f0
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\90\NodeConfiguration\Node2]
                      "CpuMask"=hex:00,00,00,00,00,00,0f,00
                      * * *
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\90\NodeConfiguration\Node13]
                      "CpuMask"=hex:00,f0,00,00,00,00,00,00
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\90\NodeConfiguration\Node14]
                      "CpuMask"=hex:0f,00,00,00,00,00,00,00
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\90\NodeConfiguration\Node15]
                      "CpuMask"= hex:f0,00,00,00,00,00,00,00

                    Когда SQL Server запущен на многопроцессорной конфигурации без NUMA, это практически единственный способ включить оптимизацию работы с неоднородными ресурсами, без которой работа сервера баз данных в подобной конфигурации могла бы быть неоптимальной и не учитывала бы аппаратные особенности сервера.
                    Другой пример, это сервер Unisys ES7000 Enterprise Server (8P), на котором была установлена операционная система Microsoft Windows Server 2003, Datacenter x64 Edition и Microsoft SQL Server 2005 Enterprise x64 Edition. 22 февраля 2006г. На этом сервере был представлен результат TPC-C равный: 347854 tpmC. Сервер был оснащён восемью процессорами Intel® Dual-Core Xeon® Processor 7041 3.0GHz, 2x2MB Lvl 2 Cache. Каждый процессор, как и в предыдущем примере, имел по два ядра на кристалл, и каждое ядро работало в режиме гипертрейдинга. Таким образом, на восьми кристаллах было размещено шестнадцать процессоров, которые из-за включения режима гипертрейдинга были представлены в операционной системе, как тридцать два логических процессора.
                    Для того чтобы сервер баз данных мог учитывать реальное секционирование процессорных кэшей, специалисты из Unisys создали представленную ниже топологию Soft-NUMA узлов, объединив в каждый узел по два процессорных кристалла, т.е. по четыре физических процессора или по восемь логических процессоров. Поскольку они посчитали достаточным число узлов равное четырём, можно привести полный набор соответствующих ключей реестра, которые были представлены в отчёте по тесту:

                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\90\NodeConfiguration\Node0]
                      "CPUMask"=dword:0xff
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\90\NodeConfiguration\Node1]
                      "CPUMask"=dword:0xff00
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\90\NodeConfiguration\Node2]
                      "CPUMask"=dword:0xff0000
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\90\NodeConfiguration\Node3]
                      "CPUMask"=dword:0xff000000

                    К оглавлению

                    NUMA для большого числа процессоров

                    До сих пор мы рассматривали примеры, в которых задействовалось сравнительно небольшое количество процессорных ядер. Однако с выходом Windows Server 2008 R2, возможности операционной системы по обслуживанию большого количества процессорных ядер и, соответственно, программных узлов значительно выросли. Есть некоторые особенности настройки Soft-NUMA для систем, число ядер которых превышает 32. С этими особенностями можно ознакомиться в статье блога “SQL Server SQLOS team“: How to configure Soft-NUMA on a system with > 32 processors?
                    Следуя этим рекомендациям, например, для создания восьми soft-NUMA узлов на 48-ми процессорном сервере потребуется:

                    1. Привязать процессоры:
                        EXEC sp_configure 'show advanced options', 1
                        RECONFIGURE
                        GO
                        EXEC sys.sp_configure N'affinity mask', N'-1'
                        GO
                        EXEC sys.sp_configure N'affinity64 mask', N'65535'
                        GO
                        RECONFIGURE WITH OVERRIDE
                        GO
                        EXEC sp_configure
                        GO
                        EXEC sp_configure 'show advanced options', 0
                        RECONFIGURE
                        GO
                    2. Выполнить необходимые изменения в системном реестре:
                        Windows Registry Editor Version 5.00
                        [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\100\NodeConfiguration]
                        [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\100\NodeConfiguration\Node0]
                        "CPUMask"=hex:00,00,00,00,00,fc,00,00
                        [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\100\NodeConfiguration\Node1]
                        "CPUMask"=hex:00,00,00,00,f0,03,00,00
                        [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\100\NodeConfiguration\Node2]
                        "CPUMask"=hex:00,00,00,c0,0f,00,00,00
                        [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\100\NodeConfiguration\Node3]
                        "CPUMask"=hex:00,00,00,3f,00,00,00,00
                        [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\100\NodeConfiguration\Node4]
                        "CPUMask"=hex:00,00,fc,00,00,00,00,00
                        [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\100\NodeConfiguration\Node5]
                        "CPUMask"=hex:00,f0,03,00,00,00,00,00
                        [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\100\NodeConfiguration\Node6]
                        "CPUMask"=hex:c0,0f,00,00,00,00,00,00
                        [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\100\NodeConfiguration\Node7]
                        "CPUMask"=hex:3f,00,00,00,00,00,00,00

                    В этом примере выбрана реверсивная схема определения программных узлов, т.е. порядок логических узлов – обратный физическому порядку объединения ядер в процессорных сокетах. Есть одно отличие от рекомендаций в указанной только что статье. В статье рекомендуется использовать для ключей реестра тип QWORD, однако, мне удавалось добиться правильной работы узлов Soft-NUMA только при типе ключа: DWORD. Использование именно такого типа подтверждается и той информацией, которая следует далее, а также представлена в статье: “Как настроить сервер SQL Server на использование программной архитектуры NUMA“. Более подробный пример можно по настройке Soft-NUMA можно найти в статье: “Пример настройки Soft-NUMA“.
                    В SQL Server 2008 R2 появились возможность работать с числом процессоров больше шестидесяти четырёх. Для этого в этой версии СУБД добавлено понятие группы soft-NUMA узлов. Вот как это выглядит в опубликованном 2 ноября 2009г. компанией UNISYS результате теста TPC-E, выдавшем с помощью сервера Unisys ES7000 Model 7600R Enterprise Server (16s) результат: 2012.77 tpsE. В этой конфигурации использовалось 16 шестиядерных процессоров Intel Hex-core Xeon X7460. 96 ядер были распределены по узлам и сгруппированы следующим образом:

                      Windows Registry Editor Version 5.00
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\105\NodeConfiguration]
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\105\NodeConfiguration\Node0]
                      "CPUMask"=hex:3f,00,00,00,00,00,00,00
                      "Group"=dword:00000000
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\105\NodeConfiguration\Node1]
                      "CPUMask"=hex:c0,0f,00,00,00,00,00,00
                      "Group"=dword:00000000
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\105\NodeConfiguration\Node2]
                      "CPUMask"=hex:00,f0,03,00,00,00,00,00
                      "Group"=dword:00000000
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\105\NodeConfiguration\Node3]
                      "CPUMask"=hex:00,00,fc,00,00,00,00,00
                      "Group"=dword:00000000
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\105\NodeConfiguration\Node4]
                      "CPUMask"=hex:00,00,00,3f,00,00,00,00
                      "Group"=dword:00000000
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\105\NodeConfiguration\Node5]
                      "CPUMask"=hex:00,00,00,c0,0f,00,00,00
                      "Group"=dword:00000000
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\105\NodeConfiguration\Node6]
                      "CPUMask"=hex:00,00,00,00,f0,03,00,00
                      "Group"=dword:00000000
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\105\NodeConfiguration\Node7]
                      "CPUMask"=hex:00,00,00,00,00,fc,00,00
                      "Group"=dword:00000000
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\105\NodeConfiguration\Node8]
                      "CPUMask"=hex:3f,00,00,00,00,00,00,00
                      "Group"=dword:00000001
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\105\NodeConfiguration\Node9]
                      "CPUMask"=hex:c0,0f,00,00,00,00,00,00
                      "Group"=dword:00000001
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\105\NodeConfiguration\Node10]
                      "CPUMask"=hex:00,f0,03,00,00,00,00,00
                      "Group"=dword:00000001
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\105\NodeConfiguration\Node11]
                      "CPUMask"=hex:00,00,fc,00,00,00,00,00
                      "Group"=dword:00000001
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\105\NodeConfiguration\Node12]
                      "CPUMask"=hex:00,00,00,3f,00,00,00,00
                      "Group"=dword:00000001
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\105\NodeConfiguration\Node13]
                      "CPUMask"=hex:00,00,00,c0,0f,00,00,00
                      "Group"=dword:00000001
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\105\NodeConfiguration\Node14]
                      "CPUMask"=hex:00,00,00,00,f0,03,00,00
                      "Group"=dword:00000001
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\105\NodeConfiguration\Node15]
                      "CPUMask"=hex:00,00,00,00,00,fc,00,00
                      "Group"=dword:00000001

                    Тут мы видим две группы, в которых топология Soft-NUMA узлов повторяется.
                    Есть некоторые особенности привязки процессоров, если число ядер сервера больше 64-х. Для настройки SQL Server становятся неприменимы такие параметры глобальной конфигурации, как: affinity mask и affinity mask 64, поскольку с их помощью можно привязать не больше 64-х логических процессоров. Вместо них в SQL Server 2008 R2 появилась новая опция команды настройки сервера: ALTER SERVER CONFIGURATION SET PROCESS AFFINITY. Она даёт возможность привязать потоки процесса SQL Server к заданным NUMA-узлам или процессорам. Это позволяет управлять тем, какие процессорные группы будут доступны для обслуживания потоков процесса SQL Server 2008 R2. Например, вот так можно ограничить работу только первыми 64-мя процессорами:

                      ALTER SERVER CONFIGURATION SET PROCESS AFFINITY CPU=0 TO 63;

                    В электронной документации к SQL Server рекомендуется включать автоматическую привязку, как это показано ниже:

                      ALTER SERVER CONFIGURATION SET PROCESS AFFINITY AUTO

                    Вместо логических процессоров можно привязывать сразу NUMA-узлы:

                      ALTER SERVER CONFIGURATION SET PROCESS AFFINITY NUMANODE=8, 12

                    Стоит отметить, что в SQL Server 2008 R2 Management Studio изменился интерфейс мастера свойств сервера. На закладке привязки процессоров и ввода-вывода процессоры теперь сгруппированы по NUMA-узлам. Причём, если сервер Non-NUMA, все процессоры группируются в одном NUMA-узле.
                    Более подробную информацию о привязке большого числа процессоров можно получить в статье: “Рекомендации по использованию SQL Server на компьютерах, которые имеют более 64 ЦП“. О поддержке большого числа процессоров операционной системой написано в статье: “Supporting Systems That Have More Than 64 Processors“.

                    К оглавлению

                    Привязка портов сетевых интерфейсов к процессорам

                    Для обслуживания клиентских сетевых запросов в SQL Server 2000 существовала возможность использования нескольких портов сетевых протоколов TCP/IP или VIA. Эти порты могли открываться на одной или нескольких сетевых платах, а также, транслироваться для прослушивания на прокси-сервере. По умолчанию, использовались порт TCP 1433 и порт UDP 1434. Также, существовала возможность динамического выделения портов экземплярам SQL Server.
                    Начиная с SQL Server 2005, предлагается более гибкая система закрепления портов сетевых интерфейсов. Порты теперь можно закреплять за процессорами, выделенными для работы экземпляру SQL Server. Порты TCP/IP или VIA можно закрепить за Soft-NUMA узлами. Такая привязка процессорных узлов портам сетевых интерфейсов получила название NUMA affinity. Появление этой возможности позволяет административно балансировать сетевые запросы не только между процессорами разных экземпляров SQL Server, но и между процессорами одного экземпляра. Например, можно предоставить доступ к разным портам клиентам с разными типами запросов (отделить аналитические запросы от коротких транзакций). Этим можно снизить влияние продолжительных, тяжёлых запросов на запросы OLTP приложений.
                    Можно привязать один порт сетевого интерфейса ко всем узлам Soft-NUMA. Такая привязка используется по умолчанию и подразумевает, что SQL Server будет сам балансировать сетевые запросы между программными узлами. При этом, если запрос принят для обслуживания каким-либо узлом и для его исполнения достаточно процессоров одного узла, он будет обслуживаться на нём до своего завершения, а процессоры других узлов задействованы не будут.
                    Можно привязать каждому процессорному узлу свой, уникальный порт. Такая привязка позволяет обслуживать получаемые портом узла запросы рабочими потоками этого узла, что позволяет задействовать пары процессорный узел / порт для разных клиентских приложений. Это позволяет развести по разным узлам их рабочую нагрузку. В случае такой привязки портов к узлам, приложения получат возможность частично ограждать локальную память физических NUMA узлов и буферы доступа от использования другими приложениями, которые подключаются к серверу через другие порты. Кроме того, сбои в работе приложения или чрезмерная утилизация приложением процессоров не будет влиять на работу других приложений, которые работают с другими процессорами. Однако привязка портов подобным образом может породить перекосы в утилизации процессоров, входящих в разные Soft-NUMA узлы, а также, если какой-нибудь из узлов захватит большую часть физической памяти, другие узлы могут испытывать её нехватку. Это может привести к существенному падению производительности приложений, обслуживаемых через закреплённые за ним порты сетевых интерфейсов.
                    Можно комбинировать оба представленных выше способа привязки портов к процессорным узлам (один порт ко всем узлам и индивидуальные порты узлов), а также можно привязать к одному программному узлу несколько портов. Разработчик приложения, зная какие порты к каким программным узлам привязаны, теперь может осмысленно выбирать место подключения для решения разных задач. Важным в этом выборе является то, что программные узлы, привязанные к выбираемому для каждой задачи порту, будут иметь горячий кэш именно для нужной приложению задачи, что может способствовать повышению производительности приложений.
                    Для создания программной абстракции процессора или нескольких процессоров необходимо в системном реестре операционной системы создать специальный раздел, внутри которого определить ключи для каждой абстракции процессора или группы процессоров. Именно это было продемонстрировано в предыдущем разделе.
                    Давайте рассмотрим на примере образец использования привязки портов к процессорным узлам. Этот вариант настройки можно было увидеть в подробном описание теста TPC-C, опубликованного компанией Hewlett-Packard 22 мая 2006г. Тест выполнялся на сервере HP ProLiant ML370 G5 SAS 3.0 GHz Dual Core, использовались Microsoft SQL Server 2005 Enterprise x64 Edition SP1 и Windows Server 2003 Enterprise x64 Edition SP1. У сервера было всего два процессорных сокета, и в каждом был двуядерный процессор. Компания HP решила определить два NUMA-узла и привязать к каждому из них по одному порту сетевого интерфейса.
                    Давайте рассмотрим, как это выглядело в виде ключей системного реестра, подробное описание которого компания предоставила в отчёте. Первый, сокращённый пример ключа показывает, как к сетевому интерфейсу с адресом 130.168.211.101 привязывается порт 2000:

                      HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL.1\MSSQLServer\SuperSocketNetLib\Tcp\IP1

                      Value 2
                      Name: TcpPort
                      Type: REG_SZ
                      Data: 2002

                      Value 5
                      Name: IpAddress
                      Type: REG_SZ
                      Data: 130.168.211.101

                    Ко второму интерфейсу с адресом 130.120.211.100 был привязан порт 2001:

                      HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL.1\MSSQLServer\SuperSocketNetLib\Tcp\IP2

                      Value 2
                      Name: TcpPort
                      Type: REG_SZ
                      Data: 2001

                      Value 5
                      Name: IpAddress
                      Type: REG_SZ
                      Data: 130.120.211.100

                    Стандартный порт общения с SQL Server привязывался к адресу-заглушке 127.1:

                      HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL.1\MSSQLServer\SuperSocketNetLib\Tcp\IP3

                      Value 2
                      Name: TcpPort
                      Type: REG_SZ
                      Data: 1433

                      Value 5
                      Name: IpAddress
                      Type: REG_SZ
                      Data: 127.0.0.1

                    Следующий ключ системного реестра указывает привязку портов к существующим NUMA-узлам:

                      HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL.1\MSSQLServer\SuperSocketNetLib\Tcp\IPAll

                      Value 0
                      Name: TcpPort
                      Type: REG_SZ
                      Data: 2001[0x1],2002[0x2]

                    Такая привязка портов к процессорным узлам позволила очень простыми средствами балансировать нагрузку внешних клиентов между процессорами системы. Она позволяет более равномерно нагружать процессоры запросами от большого числа клиентов. В этом тесте, компания очень близко следовала рекомендациям из блога одного из разработчиков SQLOS Славы Окс, которые он изложил в статье: Тюнинг SQL Server 2005 для программной поддержки NUMA.
                    Ещё один интересный пример можно найти в описании теста TPC компании IBM, который был опубликован 12 июня 2006г., и для которого использовались сервера IBM System x3950. Для восьми узлов в системном реестре было определено девять разных портов, а один порт – 1433 был привязан ко всем NUMA-узлам:

                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL.1\MSSQLServer\SuperSocketNetLib\Tcp\IPAll]

                      "TcpPort"="1433,1434[4],1436[2],1438[1],1440[8],1442[16],1444[32],1446[64],1448[128],1450[256]"

                    29 августа 2008г. компаний INSPUR Group был опубликован результат теста TPC-E, в котором использовалась система с сервером INSPUR NF520D2, использовавшая Microsoft SQL Server 2008 Enterprise x64 Edition на платформе Microsoft Windows Server 2008 Enterprise x64 Edition. В описании конфигурации можно обнаружить использование для указанных выше ключей системного реестра следующей привязки портов к четырём NUMA-узлам: 1433,2001[0x1],2002[0x2],2003[0x4],2004[0x8].
                    И напоследок, давайте вернёмся к описанному в предыдущей главе результату UNISYS от 2 ноября 2009г. Вот какую привязку узлов к портам использовали они в TPC-E для SQL Server 2008 R2.

                      Windows Registry Editor Version 5.00
                      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQLServer\SuperSocketNetLib\Tcp\IPAll]
                      "TcpPort"="1401[0x1],1402[0x2],1403[0x4],1404[0x8],1405[0x10],1406[0x20],1407[0x40],1408[0x80],1409[0x100],1410[0x200],1411[0x400],1412[0x800],1413[0x1000],1414[0x2000],1415[0x4000],1416[0x8000],1433"
                      "TcpDynamicPorts"=""
                      "DisplayName"="Any IP Address"

                    Чтобы убедиться, что порты сетевого интерфейса были успешно привязаны к процессорным узлам, можно открыть журнал ошибок SQL Server, в котором должны появиться строки, подобные этим:

                      2010-03-23 16:37:16.57 Server Server is listening on [ 'any' <ipv4> 1433].
                      2010-03-23 16:37:16.62 Server Server is listening on [ 'any' <ipv4> 2433].
                      2010-03-23 16:37:16.62 Server SQL Network Interfaces initialized listeners on node 0 of a multi-node (NUMA) server configuration with node affinity mask 0x0000000000000001.
                      This is an informational message only. No user action is required.
                      2010-03-23 16:37:16.59 Server Server is listening on [ 'any' <ipv4> 3433].
                      2010-03-23 16:37:16.59 Server SQL Network Interfaces initialized listeners on node 1of a multi-node (NUMA) server configuration with node affinity mask 0x0000000000000002.
                      This is an informational message only. No user action is required.

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

                    К оглавлению

                    Максимальный уровень параллелизма

                    Само по себе разделение ресурсов по узлам NUMA ещё не гарантирует, что каждый отдельный запрос будет ограничиваться ресурсами только одного узла. Вполне возможна ситуация, когда в силу хороших возможностей для распараллеливания запроса, он может исполняться на процессорах нескольких NUMA узлов. Т.е. если для сервера будет существовать возможность создания такого числа потоков, которое превышает число ядер выбранного для запроса узла, то нагрузка будет размещена и на процессоры, которые не входят в число процессоров выбранного процессорного узла. Механизмы планирования задач операционной системы и SQL Server устроены так, что планирование потоков не привязывается жёстко к схеме процессорных узлов. Если в системе есть свободные процессоры, и не наложено никаких ограничений на число обслуживающих запрос потоков, то нет препятствий задействовать столько процессоров, сколько доступно для экземпляра SQL Server. Это поведение одинаково относительно Soft-NUMA или NUMA-узлов.
                    Если запрос распараллеливается на число ядер, превышающие число ядер в одном узле NUMA, это может стать причиной обращений к ресурсам из домена близости соседних узлов. Чтобы исключить такие проявления, можно ограничить уровень параллелизма на уровне запроса (используя подсказку оптимизатору) или на уровне сервера, задав отличное от нуля значение параметру глобальной конфигурации сервера: max degree of parallelism.
                    Пример использования ограничений параллелизма для локализации запроса в рамках одного узла представлен в статье: “Пример настройки Soft-NUMA“.
                    В большинстве случаев, особенности реализации архитектур NUMA-like заставляют искусственно ограничивать параллелизм до числа потоков, не превышающего числа ядер одного шасси, или даже одного сокета. Однако некоторые нагрузки, характерные для задач обслуживания SQL Server, потенциально получают большой выигрыш от распараллеливания. Очень часто, хорошо распараллеливаемые задачи обслуживаются сервером одновременно с задачами, которым выгодна меньшая степень параллелизма. Если максимальная степень параллелизма не ограничена, может оказаться, что такие задачи, как например построение индексов, могут исполняться на процессорах из нескольких шасси. В таком случае, за счёт потерь на межсоединениях, подобные задачи могут работать на большом числе процессоров хуже, чем, если бы они исполнялись на меньшем числе процессоров одного шасси.
                    Есть уловка, позволяющая временно ограничить число выделенных экземпляру SQL Server процессоров на время выполнения операций обслуживания данных. Для этого можно с помощью системной хранимой процедуры sp_configure изменить маску привязки процессоров (affinity mask), оставив привязку экземпляра только к процессорам одного шасси. Изменение привязки не требует перезапуска службы. После завершения операций обслуживания, можно вернуть исходное состояние привязки.
                    Высокие показатели производительности и масштабируемости достигаются не только путём использования предоставляемых NUMA возможностей. В приложении баз данных необходимо уделять внимание секционированию кэшей данных и управляющих структур, чтобы их было легче распределять из локальных ресурсов NUMA-узла. Кроме того, необходимо учитывать негативное влияние блокировок больших областей данных, т.к. эти запрашиваемые приложением данные могут обслуживаться несколькими потоками, исполнение которых возможно на разных узлах.

                    К оглавлению

                    Выводы

                    В SQL Server 2005 был добавлен новый, усовершенствованный и более приспособленный для работы на современных серверных платформах механизм планирования потоков и распределения ресурсов. В последующих версиях SQL Server 2008 и SQL Server 2008 R2 эти новшества получили дальнейшее развитие. Также, наиболее выигрышны по уровню поддержки NUMA версии операционных систем: Windows 2008 и Windows 2008 R2 Datacenter Edition. Последние версии СУБД и ОС позволяют использовать до 256 процессорных ядер. Всё это делает выбор этих версий более предпочтительным, чем их предшественники. Сегодня, разработчикам и администраторам приложений SQL Server для многопроцессорных архитектур предоставляются следующие выгоды:

                    1. Обеспечен учет аппаратных особенностей современных многопроцессорных архитектур. Внутренняя оптимизация SQL Server позволяет в большинстве случаев обойтись без дополнительных настроек сервера и операционной системы для работы на многопроцессорных системах.
                    2. Обеспечена возможность перераспределения нагрузки и процессорных ресурсов для достижения более высоких показателей производительности или для изоляции ресурсов разных приложений. Для этого не требуется программирования.
                    3. Разработчики баз данных и приложений баз данных могут использовать программные интерфейсы операционной системы и сервера баз данных для исполнения операций манипуляции данными или операций модификации данных на заданных процессорах или процессорных узлах. Это могут быть реальные Soft-NUMA или NUMA-узлы.
                    4. Секционирование памяти по NUMA узлам позволяет управлять актуальностью данных в кэше, причём, приложение может выбирать такой NUMA узел, кэш которого наиболее оптимален для текущего запроса.
                    5. Привязка к узлам Soft-NUMA портов сетевых интерфейсов позволяет балансировать сетевую нагрузку и трафик в сегментах сети. Увеличение числа потоков завершения ввода-вывода за счёт увеличения числа узлов Soft-NUMA для многих типов рабочей нагрузки также позволяет поднять производительность исполнения запросов.
                    6. Богатая коллекция административных динамических представлений позволяет организовать оперативную диагностику работы планировщиков непривилегированного режима, менеджеров памяти и других внутренних сущностей и объектов ядра сервера баз данных. Результаты этой диагностики могут использоваться для выбора наиболее удачной схемы утилизации процессорных ресурсов, чтобы наиболее равномерно распределять нагрузку между процессорами и добиться максимально равномерного распределения памяти.
                    7. Абстракции процессорных узлов или групп процессоров, позволяют оптимизировать распределение задач по процессорам и балансировать сетевые запросы не только на серверах со специализированной процессорной архитектурой, такой, как NUMA, но и на массовых, бюджетных серверах с SMP архитектурой.

                    Ваши отзывы, пожелания и замечания направляйте, пожалуйста автору на адрес mssqlhelp@rambler.ru

                    К оглавлению

                    Благодарности

                    В первую очередь, хочу сказать спасибо Ирине Наумовой, критические замечания и рекомендации которой помогли сделать материал понятней, а текст более “читабельным”. Хочу поблагодарить сотрудников Майкрософт, Славу Окс – за то что вдохновил меня и помог с написанием первых вариантов этой статьи в 2006 году. Алексея Халяко – за экспертизу и продуктивную критику, а также за мудрые советы при написании этого варианта статьи. Владислава Щербинина – за то что он как крот искал в тексте плагиат и непростительные ошибки.

                    Александр Гладченко 2010г.

                     [print_link]

                  • Главная SQL, Без рубрики, Новое SQL, SQL 2008 R2, Конкурс
                    • SQL Server 2008 R2. Data-tier Application. Разработка, внедрение, модернизация.

                      texas california lemon database Создание клиент-серверных приложений, даже в их простейшем двухуровневом варианте, где в качестве сервера выступает компьютер обеспечивающий доступ к общим для всех клиентов данным и к которому, собственно, и подключаются все клиенты с запросами этих самых данных, никогда не было сверхлегкой задачей. Одна из глобальных проблем стоящая перед командой берущейся за такой проект заключается в следующем. Код создающий базу данных необходимой структуры и конфигурации и наполняющий ее функционалом для удобного взаимодействия с клиентом (прежде всего хранимые процедуры и функции) пишется одним человеком, обычно называемым разработчиком баз данных (БД). И работает он, как правило, в своем окружении, со своими инструментами вроде Visual Studio. А после того как такой код написан и готов к внедрению, непосредственно внедрять и поддерживать его приходится совсем другому человеку, обычно называемому администратором БД. Помимо того что у разработчика было свое окружение (тестовое), а у администратора свое, «боевое» (production), так еще первому нечего было передать второму в качестве единого инсталляционного пакета.