SQL Server可以在系统生成的约束名称中创建冲突吗?


14

我有一个可以在SQL Server 2008数据库(非群集)中创建数百万张表的应用程序。我希望升级到SQL Server 2014(群集),但在负载下却遇到错误消息:

“数据库中已经存在一个名为'PK__tablenameprefix__179E2ED8F259C33B'的对象”

这是系统生成的约束名称。它看起来像是随机生成的64位数字。由于表格数量众多,我是否有可能看到冲突?假设我有1亿张表,那么在添加下一张表时,我计算出发生冲突的机会要小于1万亿分之一,但这假设分布是一致的。SQL Server是否有可能在2008年和2014年之间更改其名称生成算法以增加发生冲突的可能性?

另一个明显的不同是,我的2014年实例是一个成对的,但是我正在努力形成一个假设,说明为什么会产生上述错误。

PS:是的,我知道创建数百万张表很疯狂。这是我无法控制的黑匣子第三方代码。尽管有精神错乱,但它在2008版中仍然有效,现在在2014版中不再适用。

编辑:仔细检查后,生成的后缀似乎总是以179E2ED8开头-这意味着随机部分实际上只是一个32位数字,每次添加新表时,冲突几率仅为50分之一。与我看到的错误率更接近!


表名不同,但是它们使用命名约定,这至少导致前11个字符相同,这似乎是SQL Server在生成约束名称时使用的全部。
jl6

基础硬件有所不同(较新的DL380),但性能没有明显提高。该练习的目的是替换不受支持的SQL Server 2008,而不是提高吞吐量,并且已相应地配置了硬件。
jl6

Answers:


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的十六进制版本。作为objectid保证唯一的名称也必须是唯一的。在Sybase中也使用这些tabname_colname_objectid

对于唯一约束和主键约束,Sybase使用

tabname_colname_tabindid,其中tabindid是表ID和索引ID的字符串连接

这也将保证唯一性。

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)。我想这是为了提供一些随机性来源。显然,在约束之间变化的这4个字节中,并没有使用GUID的全部16个字节。

在此处输入链接说明

我认为新算法是出于某种效率的原因而完成的,但在极端情况下(例如您的情况)却以增加碰撞的可能性为代价。

这是一个很病理的情况,因为它要求PK的表名前缀和列名(因为这会影响最后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个表之后)

消息2714,级别16,状态30,行15在数据库中已经有一个名为“ PK__abcdefgh__3BD019A8175067CE”的对象。消息1750,级别16,状态1,第15行无法创建约束或索引。请参阅先前的错误。


11

假设我有1亿张桌子,我计算出发生碰撞的几率不到1万亿分之一

请记住,这是“ 生日问题 ”。您不是要为单个给定的哈希值生成冲突,而是要测量许多值对中没有一个发生冲突的可能性。

因此,对于N个表,有N *(N-1)/ 2对,因此这里大约有10 16对。如果发生碰撞的概率为2 -64,则一对不发生碰撞的概率为1-2 -64,但是有如此多的对,则此处没有发生碰撞的概率约为(1-2 -6410 16,或更多,例如1 / 10,000。参见例如https://preshing.com/20110504/hash-collision-probabilities/

而且,如果它仅是32位哈希,则冲突概率仅在77k值处越过1/2。


2
而首先要达到77K的值而又不会遇到碰撞可能是不太可能的,因为在此之前您需要为所有之前的创作感到幸运。我想知道碰撞的累计概率达到50%的意义是什么
马丁·史密斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.