Чи може SQL Server створювати зіткнення у створених системою іменах обмежень?


14

У мене є додаток, який створює мільйони таблиць у базі даних SQL Server 2008 (не кластеризований). Я шукаю оновлення до SQL Server 2014 (кластеризовано), але я потрапляю на повідомлення про помилку під час навантаження:

"У базі даних вже є об'єкт з назвою" PK__tablenameprefix__179E2ED8F259C33B "

Це ім'я обмеження, породжене системою. Це схоже на випадкове генерування 64-бітного числа. Чи можливо я бачу зіткнення через велику кількість таблиць? Якщо припустити, що у мене є 100 мільйонів таблиць, я обчислюю менше шансу зіткнення 1 в 1 трильйон при додаванні наступної таблиці, але це передбачає рівномірне розподіл. Чи можливо SQL Server змінив алгоритм генерації імен між версіями 2008 та 2014 рр., Щоб збільшити шанси зіткнення?

Інша суттєва відмінність полягає в тому, що мій екземпляр 2014 року є кластерною парою, але я намагаюся сформувати гіпотезу, чому це може призвести до наведеної вище помилки.

PS Так, я знаю, що створення мільйонів таблиць є божевільним. Це чорний код стороннього коду, над яким я не маю контролю. Незважаючи на божевілля, він працював у версії 2008, а зараз не у версії 2014.

Редагувати: при більш детальному огляді генерований суфікс завжди починається з 179E2ED8 - це означає, що випадкова частина насправді є лише 32-бітним числом, а шанси зіткнень є лише 1 на 50 щоразу, коли додається нова таблиця, яка набагато ближче до рівня помилок, які я бачу!


Назви таблиць різні, але вони використовують умову іменування, що призводить до того, що принаймні перші 11 символів є однаковими, і, здається, всі SQL Server використовує при створенні імені обмеження.
jl6

Базове обладнання відрізняється (новіше покоління DL380), але не значно вищою продуктивністю. Мета вправи - замінити підтримку SQL Server 2008, а не покращити пропускну здатність, а обладнання було забезпечено відповідно.
jl6

Відповіді:


16

Чи може SQL Server створювати зіткнення у створених системою іменах обмежень?

Це залежить від типу обмеження та версії SQL Server.

CREATE TABLE T1
(
A INT PRIMARY KEY CHECK (A > 0),
B INT DEFAULT -1 REFERENCES T1,
C INT UNIQUE,
CHECK (C > A)
)

SELECT name, 
       object_id, 
       CAST(object_id AS binary(4)) as object_id_hex,
       CAST(CASE WHEN object_id >= 16000057  THEN object_id -16000057 ELSE object_id +2131483591 END AS BINARY(4)) AS object_id_offset_hex
FROM sys.objects
WHERE parent_object_id = OBJECT_ID('T1')
ORDER BY name;

drop table T1

Приклад результатів 2008 року

+--------------------------+-----------+---------------+----------------------+
|           name           | object_id | object_id_hex | object_id_offset_hex |
+--------------------------+-----------+---------------+----------------------+
| CK__T1__1D498357         | 491357015 | 0x1D498357    | 0x1C555F1E           |
| CK__T1__A__1A6D16AC      | 443356844 | 0x1A6D16AC    | 0x1978F273           |
| DF__T1__B__1B613AE5      | 459356901 | 0x1B613AE5    | 0x1A6D16AC           |
| FK__T1__B__1C555F1E      | 475356958 | 0x1C555F1E    | 0x1B613AE5           |
| PK__T1__3BD019AE15A8618F | 379356616 | 0x169C85C8    | 0x15A8618F           |
| UQ__T1__3BD019A91884CE3A | 427356787 | 0x1978F273    | 0x1884CE3A           |
+--------------------------+-----------+---------------+----------------------+

Приклад результатів 2017 року

+--------------------------+------------+---------------+----------------------+
|           name           | object_id  | object_id_hex | object_id_offset_hex |
+--------------------------+------------+---------------+----------------------+
| CK__T1__59FA5E80         | 1509580416 | 0x59FA5E80    | 0x59063A47           |
| CK__T1__A__571DF1D5      | 1461580245 | 0x571DF1D5    | 0x5629CD9C           |
| DF__T1__B__5812160E      | 1477580302 | 0x5812160E    | 0x571DF1D5           |
| FK__T1__B__59063A47      | 1493580359 | 0x59063A47    | 0x5812160E           |
| PK__T1__3BD019AE0A4A6932 | 1429580131 | 0x5535A963    | 0x5441852A           |
| UQ__T1__3BD019A981F522E0 | 1445580188 | 0x5629CD9C    | 0x5535A963           |
+--------------------------+------------+---------------+----------------------+

Для обмежень за замовчуванням перевірте обмеження та обмеження зовнішнього ключа, останні 4 байти автоматично створеного імені є шістнадцятковою версією об'єктиву обмеження. Як objectidгарантовано унікальне, ім'я також повинно бути унікальним. У Sybase теж такі використовуютьtabname_colname_objectid

Для унікальних обмежень та обмежень первинного ключа використовується Sybase

tabname_colname_tabindid, де tabindid - це рядкове з'єднання ідентифікатора таблиці та ідентифікатора індексу

Це теж гарантувало б унікальність.

SQL Server не використовує цю схему.

І в SQL Server 2008, і в 2017 році він використовує 8-байтовий рядок в кінці назви генерованої системи, проте алгоритм змінився щодо того, як генеруються останні 4 байти.

У 2008 рік останні 4 байти представляє цілочисельний лічильник , який відстоїть від object_idпо -16000057будь-якому від'ємного значення обертати навколо максимальні підписаних межд. (Значення 16000057полягає в тому, що це приріст, застосовуваний між послідовно створенимиobject_id ). Це все ще гарантує унікальність.

У 2012 році вгору я взагалі не бачу жодного шаблону між object_id обмеження та цілим числом, отриманим обробкою останніх 8 символів імені як шістнадцятковим поданням підписаного int.

Імена функцій у стеку викликів у 2017 році показують, що тепер він створює GUID як частину процесу генерації імен (про 2008 рік я не бачу жодної згадки MDConstraintNameGenerator). Я гадаю, це полягає в тому, щоб створити певне джерело випадковості. Зрозуміло, що він не використовує цілих 16 байт з GUID у тих 4 байтах, які змінюються між обмеженнями.

введіть опис посилання тут

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

Це досить патологічний випадок, оскільки для нього потрібен префікс назви таблиці та назва стовпця ПК (якщо це впливає на 8 символів, що передують остаточному 8), щоб вони були однаковими для десятків тисяч таблиць, перш ніж вона стане ймовірною, але може бути відтворена цілком легко за допомогою нижче.

CREATE OR ALTER PROC #P
AS
    SET NOCOUNT ON;

    DECLARE @I INT = 0;


    WHILE 1 = 1
      BEGIN
          EXEC ('CREATE TABLE abcdefghijklmnopqrstuvwxyz' + @I + '(C INT PRIMARY KEY)');
          SET @I +=1;
      END 

GO

EXEC #P

Приклад запуску на SQL Server 2017 проти новоствореної бази даних не вдався за трохи більше хвилини (після того як було створено 50 931 таблиці)

Msg 2714, рівень 16, стан 30, рядок 15 У базі даних вже є об’єкт з назвою "PK__abcdefgh__3BD019A8175067CE". Msg 1750, рівень 16, стан 1, рядок 15 Не вдалося створити обмеження чи покажчик. Дивіться попередні помилки.


11

Якщо припустити, що у мене є 100 мільйонів таблиць, я обчислюю менше шансу зіткнення 1 на 1 трильон

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

Так з N таблиць є N * (N-1) / 2 пари, тож тут приблизно 10 16 пар. Якщо ймовірність зіткнення становить 2 -64 , ймовірність того, що жодна пара не зіткнеться, становить 1-2 -64 , але з такою кількістю пар, ймовірність виникнення зіткнень тут приблизно не становить (1-2 -64 ) 10 16 , або більше, як 1/10 000. Див., Наприклад, https://preshing.com/20110504/hash-collision-probables/

І якщо це лише 32-бітний хеш, ймовірність зіткнення перетинає 1/2 при лише 77k значеннях.


2
І дійти до значень 77K в першу чергу, не стикаючись зіткненням, ймовірно, зовсім малоймовірно, оскільки вам потрібно пощастить у всіх попередніх творіннях до цього. Цікаво, в чому сенс, коли сукупна ймовірність зіткнення досягає 50%
Мартін Сміт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.