Производительность интеграции со средой CLR
В этом разделе обсуждаются решения разработчиков, повышающие производительность при интеграции MicrosoftSQL Server со средой CLR инфраструктуры Microsoft .NET Framework.
Процесс компиляции
Когда при компиляции выражений SQL встречается ссылка на управляемую процедуру, создается заглушка на промежуточном языке Майкрософт (MSIL). Заглушка содержит программный код для упаковки параметров процедуры из SQL Server в среду CLR, вызова функции и возвращения результата. Этот связующий код основан на типе параметра и его направлении (входной, выходной, передача по ссылке).
Связующий код позволяет проводить оптимизацию для конкретного типа и гарантирует эффективное обеспечение семантики SQL Server таких свойств, как допустимость значений NULL, ограничивающие аспекты, передача параметров по значению и стандартная обработка исключений. Создавая код для конкретных типов аргументов, можно избежать приведения типов и нагрузки по созданию объектов-оболочек (этот процесс называют также «упаковкой») при вызове, пересекающем границы процессов.
Затем созданная заглушка компилируется в собственный код и оптимизируется для конкретной аппаратной архитектуры, на которой выполняется 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 есть методы для навигации по результирующему набору, возвращенному потоковой функцией с табличными значениями. При вызове возвращающей табличное значение функции потока возвращенный интерфейс 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 реализован формат сериализации для определяемого пользователем типа — Format.Native. Во время компиляции исследуется структура типа, а затем создается код MSIL, настраиваемый для данного конкретного определения типа.
Собственная сериализация используется в SQL Server по умолчанию. Сериализация, определяемая пользователем, вызывает для сериализации метод, указанный автором типа. По возможности следует использовать метод Format.Native, так как он обеспечивает наилучшую производительность.
Нормализация сравнимых определяемых пользователем типов
Операции отношения, например сортировка и сравнение определяемых пользователем типов, работают непосредственно с двоичным представлением значения. Для этого на диске хранится нормализованное (двоичное, упорядоченное) представление состояния определяемого пользователем типа.
Нормализация имеет два преимущества: ее использование значительно «удешевляет» операцию сравнения, так как избавляет от необходимости создания экземпляра типа и вызова метода; кроме того, нормализация создает двоичное представление определяемого пользователем типа, что позволяет строить гистограммы, индексы и гистограммы для значений этого типа. Поэтому производительность операций с нормализованными определяемыми пользователем типами почти такая же, как у операций, не требующих вызова методов, над встроенными типами.
Масштабируемое использование памяти
Чтобы управляемая сборка мусора в SQL Server имела высокую производительность и хорошо масштабировалась, следует избегать выделения памяти одним большим блоком. Выделенные области памяти размером больше 88 КБ помещаются в кучу для больших объектов, для которой сборка мусора работает гораздо медленнее и хуже масштабируется, чем для небольших областей памяти. Например, если нужно выделить память для большого многомерного массива, лучше выделить память под массив массивов (разреженный массив).