Поделиться через


Производительность интеграции со средой CLR

В этом разделе обсуждаются решения разработчиков, повышающие производительность при интеграции Microsoft SQL Server со средой CLR инфраструктуры Microsoft .NET Framework.

Процесс компиляции

Когда при компиляции SQL-выражений встречается ссылка на управляемую процедуру, создается заглушка на промежуточном языке Microsoft (MSIL). Заглушка содержит программный код для передачи параметров процедуры из SQL Server в среду CLR, вызова функции и возвращения результата. Этот «связующий» код основан на типе параметра и его направлении (входной, выходной, передача по ссылке).

«Связующий» код позволяет проводить оптимизацию для конкретного типа и гарантирует эффективное обеспечение семантики SQL Server — таких свойств, как обнуляемость, ограничивающие аспекты, передача параметров по значению и стандартная обработка исключений. Создавая код для конкретных типов аргументов, можно избежать преобразования типов и нагрузки по созданию объектов-оболочек (этот процесс называют также "упаковкой") при вызове, пересекающем границы процессов.

Затем созданная заглушка компилируется в собственный код и оптимизируется для конкретной аппаратной архитектуры, на которой выполняется SQL Server, с использованием служб JIT-компиляции среды CLR. JIT-службы вызываются на уровне методов и позволяют управляющему окружению SQL Server создавать единый объект компиляции между СУБД SQL Server и выполнением в среде CLR. После компиляции заглушки результирующий указатель на функцию становится реализацией этой функции времени выполнения. Такой подход к созданию кода гарантирует отсутствие лишних расходов по вызову функций, связанных с отражением или доступом к метаданным во время выполнения.

Быстрые переходы между СУБД SQL Server и средой CLR

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

Вопросы производительности

Далее проводится краткое перечисление факторов производительности, специфичных для интеграции среды CLR и SQL Server. Дополнительную информацию можно найти в статье «Использование интеграции со средой CLR в SQL Server 2005» на портале MSDN. Дополнительную информацию о производительности управляемого кода можно найти в статье «Улучшение производительности и масштабируемости приложений .NET» на портале MSDN.

Определяемые пользователем функции

Функции CLR используют ускоренный путь вызова по сравнению с пользовательскими функциями в Transact-SQL. Кроме того, производительность управляемого кода заметно выше, чем у Transact-SQL, в том, что касается процедурного кода, вычислений и операций со строками. Функции CLR, которые требуют большого объема вычислений и не требуют доступа к данным, лучше писать в управляемом коде. Однако функции Transact-SQL осуществляют доступ к данным более эффективно, чем функции интеграции CLR.

Определяемые пользователем статистические функции

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

Функции потока, возвращающие табличное значение

Часто бывает нужно, чтобы в результате вызова функции приложение вернуло таблицу. Например, в качестве части операции импорта приложение читает табличные данные из файла, и нужно преобразовать их из формата величин, разделенных запятыми, в реляционное представление. Обычно это достигается с помощью материализации и заполнения таблицы результатов до ее использования вызывающим объектом. Интеграция CLR в SQL Server использует новый механизм расширяемости — функции потока, возвращающие табличное значение (streaming table-valued function, STVF). Управляемые функции потока, возвращающие табличное значение, по производительности опережают реализации расширенных хранимых процедур.

Функции потока, возвращающие табличное значение, представляют собой функции, возвращающие интерфейс IEnumerable. У интерфейса IEnumerable есть методы для навигации набора результатов, возвращенного функцией STVF. При вызове функции потока, возвращающие табличное значение, возвращенный интерфейс IEnumerable непосредственно соединен с планом запроса. Когда плану запроса нужно получить строки, он вызывает методы интерфейса IEnumerable. Такая модель итерации позволяет провести немедленную обработку результатов сразу после получения первой строки, не ожидая заполнения всей таблицы. Она также существенно снижает затраты памяти на вызов функции.

Сравнение массивов и курсоров

Если курсорам Transact-SQL нужно перемещаться по данным, которые проще реализовать как массив, использование управляемого кода принесет существенный выигрыш в производительности.

Строковые данные

Символьные данные в SQL Server — например, varchar, — в управляемых функциях могут принадлежать к типу SqlString или SqlChars. Переменные типа SqlString создают в памяти экземпляр всего значения целиком. Переменные типа SqlChars обеспечивают потоковый интерфейс, который позволяет добиться более высокой производительности и масштабируемости, так как не создает в памяти экземпляра всего значения сразу. Это особенно важно для типов больших объектов (LOB). Кроме того, с помощью потокового интерфейса, возвращаемого методом SqlXml.CreateReader(), можно получить доступ к данным XML на сервере.

Сравнение CLR и расширенных хранимых процедур

API-интерфейсы Microsoft.SqlServer.Server, позволяющие управляемым процедурам отсылать наборы результатов обратно клиенту, имеют более высокую производительность, чем API-интерфейсы служб Open Data Services (ODS), используемые расширенными хранимыми процедурами. Более того, API-интерфейсы System.Data.SqlServer поддерживают такие типы данных, как xml, varchar(max), nvarchar(max) и varbinary(max), введенные в версии SQL Server 2005, в то время как в ODS API-интерфейсах поддержка этих типов еще не добавлена.

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

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

ПримечаниеПримечание

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

Собственная сериализация для определяемых пользователем типов

Определяемых пользователем типы (UDT) представляют собой механизм расширения скалярной системы типов. В SQL Server существует формат сериализации для UDT — Format.Native. Во время компиляции исследуется структура типа, а затем создается код MSIL, настраиваемый для данного конкретного определения типа.

Собственная сериализация используется в SQL Server по умолчанию. Сериализация, определяемая пользователем, вызывает для сериализации метод, указанный автором типа. По возможности следует использовать метод Format.Native, так как он обеспечивает наилучшую производительность.

Нормализация сравнимых типов, определяемых пользователем

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

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

Масштабируемое использование памяти

Чтобы управляемая сборка мусора в SQL Server имела высокую производительность и хорошо масштабировалась, следует избегать выделения памяти одним большим куском. Выделенные области памяти размером больше 88 КБ помещаются в кучу для больших объектов, для которой сборка мусора работает медленнее и хуже масштабируется, чем для небольших областей памяти. Например, если нужно выделить память для большого многомерного массива, лучше выделить память под массив массивов (разреженный массив).