2016-02-11 3 views
2

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

Я пытаюсь создать последовательные GUID для использования в качестве первичных идентификаторов в таблице SQL Server 2008 R2. Раньше они генерировались пользовательской функцией, которая выполняла слишком много операций с строками, чтобы генерировать последовательные GUID, и это было узким местом производительности.

Чтобы уменьшить время выполнения этой функции, я пытаюсь использовать интеграцию CLR и выполнять операции в классе C#. Секвенирование GUID выполняется путем обновления последнего 12 bytes идентификатора GUID до значений, сгенерированных с системного времени.

В начальной версии системная функция SQL Server использовалась для получения времени даты и преобразования ее в тики, которые затем использовались для создания последовательных 12 байтов указателя. Мой подход состоял в том, чтобы использовать свойство .NET Framework DateTime.UtcNow.Ticks для преобразования в последние 12 байтов, но я заметил, что свойство Ticks не обновлялось, как ожидалось, и это указывало на то, что функция системного времени не вызывалась так часто, как я ожидал.

Чтобы проверить это, я создал простой метод, который возвращает клещи:

[SqlFunction()] 
public static long GetTicks() 
{ 
    return DateTime.UtcNow.Ticks; 
} 

Эту сборку для этого метода затем была создан в качестве ASSEMBLY объекта в SQL Server; обратите внимание, что интеграция CLR была включена, и сборка была создана в режиме UNSAFE, что позволило получить доступ к OS APIs. Затем я создал функцию в SQL Server и для этой функции CLR:

CREATE FUNCTION GetTicks() RETURNS BIGINT 
AS EXTERNAL NAME Sequential.Generator.GetTicks; 
GO 

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

SELECT [dbo].[GetTicks](); 
SELECT [dbo].[GetTicks](); 

Результаты были:

enter image description here

Это показывает, что последовательные вызовы функции CLR возвращали одно и то же значение TICKS. Это означает, что основной вызов ОС не выполнялся для возврата тиков для каждого вызова функции GetTicks(). Затем я выполнил второе испытание путем создания простой таблицы следующим образом:

CREATE TABLE #tmp1(
    [NUM] [INT] NULL, 
    [TICKS] BIGINT 
); 

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

UPDATE #tmp1 
SET [ticks] = [Play].[dbo].[GetTicks](); 

Я тогда измеренной разницы между [клещами ] значение последовательных строк и существует только одно изменение значений из всех 300 строк (сделано в Excel):

enter image description here

gain, это означает, что свойство Framework DateTime.UtcNow.Ticks не вызывалось для каждого вызова функции CLR Integrated GetTicks().

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

  2. Интеграция SQL CLR неразумного подхода для SQL Server для получения системного времени из ОС на основе более частого, чем функция T-SQL GetUTCDATE()?

Спасибо!

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

+1

* Кроме того, функция NEWSEQUENTIALID() неприемлема из-за проблем с конфиденциальностью. В вашем дизайне есть те же проблемы конфиденциальности. Последовательные идентификаторы в принципе предсказуемы. – SLaks

+0

Кроме того, существуют ограничения на способ вызова NEWSEQUENTIALID(). И это не мой дизайн, но спасибо, что указали это. Но опять же, какие-либо предложения по вопросам интеграции CLR отражены в вопросах? – BgRva

ответ

4

Опять же, это означает, что свойство Framework DateTime.UtcNow.Ticks не вызывается для каждого вызова функции CLR Integrated GetTicks().

Нет, это не означает, что имущество UtcNow не вызывалось. Проблема заключается в разрешении System.DateTime в .NET. Независимо от того, насколько точным является значение, значение увеличивается только каждые 10-16 миллисекунд (подобно тому, как GETDATE() в T-SQL обновляется каждые 3 миллисекунды). Вот почему значение остается постоянным для определенного количества строк, но не для всех строк, таких как функции времени и времени T-SQL (например, GETDATE() и т. Д.).

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

Функция «Интеграция с CLR» либо «включена», либо «отключена». Конфигурация этого не существует. Как упоминалось выше, это проблема с частотой, с которой обновляется значение DateTime.UtcNow.Ticks.

Интеграция SQL CLR Интеллектуальная интеграция SQL Server для нежелательного подхода к SQL Server для получения системного времени из ОС по частоте, чем функция GetUTCDATE() T-SQL?

Не так неразумно, как с помощью GUID/UNIQUEIDENTIFIER как PK (я предполагаю, что это индекс кластерного), что само по себе не является почти столь же неразумно, как пытаться смягчить проблемы с производительностью в результате этого решения тогда введя не встроенную функцию.

Как вызывается текущий UDF для генерации значения? INSERT Триггер?

Если проблема с производительностью, сделайте PK INT или BIGINT и используйте IDENTITY. Сохраните поле GUID, но сделайте его «альтернативным ключом», добавив к нему индекс NonClustered. Код приложения может использовать значение GUID, но JOINs и т. Д. Должны использовать ПК INT/BIGINT.

Но, что касается получения более часто изменяющейся стоимости для DateTime.UtcNow.Ticks, то в Windows 8/Windows Server 2012 есть новая функция ОС, GetSystemTimePreciseAsFileTime, которая будет работать для этого. Как правило, я бы добавил: «К сожалению, это требует, чтобы Ассамблея была отмечена как UNSAFE», потому что этого следует избегать, если это абсолютно необходимо, но вы уже упоминали о том, что ваша Ассамблея помечена как UNSAFE, поэтому это не меняет этого.

Для того, чтобы использовать эту новую функцию, просто возьмите код здесь:

High Resolution Clock in C#

, а затем заменить:

DateTime.UtcNow.Ticks 

с:

HighResolutionDateTime.UtcNow.Ticks 

!! ПОЖАЛУЙСТА, ОБРАТИТЕ ВНИМАНИЕ: SQL Server не гарантирует количество вызовов функции. Это означает, что возможно , даже если маловероятно, что несколько вызовов этой функции (или любая функция, включая NEWID()) возвращают то же значение, даже если оно определено как IsDeterministic = false. Однако это справедливо и для UDF T-SQL, поэтому, если процесс в настоящее время работает с UDF T-SQL, он должен продолжать работать один раз, который отключается для скалярной функции SQLCLR.

+0

Спасибо! К сожалению, использование GUID в качестве PK на данный момент не является изменчивым, и UDF для получения последовательного GUID должен быть вызван inline для вставок и обновлений. Тем не менее, я попытаюсь использовать ваш частотный код с высоким разрешением. – BgRva

+0

@BgRva Ок. Я просто добавил в конце заметку о том, что вам следует хотя бы знать, но должно быть хорошо. Тем не менее, почему вы вызываете эту функцию в инструкции 'UPDATE'? Просто чтобы переназначить текущие не последовательные значения, чтобы стать последовательными? Надеюсь, обновление значения PK не является регулярным процессом ;-) –

+0

Мне поручается клиент, который исследует проблемы производительности в существующей базе данных, в которой происходит значительное использование полей GUID. Эта функция называется inline в операциях INSERT и UPDATE. – BgRva