【问题标题】:Using datetime float representation as primary key使用日期时间浮点表示作为主键
【发布时间】:2009-08-23 12:27:48
【问题描述】:

根据我的经验,我了解到使用代理 INT 数据类型列作为主键 esp。与使用 GUID 或 char/varchar 数据类型列作为主键相比,IDENTITY 键列提供了更好的性能。 我尝试尽可能使用 IDENTITY 键作为主键。但最近我遇到了一种模式,其中表是水平分区的,并通过分区视图进行管理。所以表不能有 IDENTITY 列,因为这会使分区视图不可更新。解决此问题的一种方法是创建一个带有标识列的虚拟“keygenerator”表来生成主键的 ID。但这意味着每个分区视图都有一个“密钥生成器”表。 我的下一个想法是使用 float 作为主键。原因是我设计的以下关键算法

DECLARE @KEY FLOAT

SET @KEY = CONVERT(FLOAT,GETDATE())/100000.0 

SET @KEY = @EMP_ID + @KEY

Heres how it works.

CONVERT(FLOAT,GETDATE()) 

给出当前日期时间的浮点表示,因为在内部所有日期时间都由 SQL 表示为浮点值。

CONVERT(FLOAT,GETDATE())/100000.0 

将浮点表示转换为完整的十进制值,即所有数字都被推到“。”的右侧。

@KEY = @EMP_ID + @KEY

将整数形式的员工 ID 添加到此十进制值中。

逻辑是确保员工 ID 在会话中是唯一的,因为员工不能同时多次连接到应用程序。对于同一员工,每次生成密钥时,当前日期时间都是唯一的。

所有员工会话和时间的唯一键。

所以对于 Emp Ids 11 和 12,我有像 12.40046693321566357、11.40046693542361111 这样的键值

但我担心,与选择 GUID 或 char/varchar 作为主键相比,浮点数据类型作为主键是否会带来好处。同样重要的是因为分区浮点列将成为复合键的一部分。

【问题讨论】:

    标签: floating-point primary-key


    【解决方案1】:

    同样重要的是因为分区浮点列将成为复合键的一部分。

    什么?为什么?您为使这个基于员工/时间的值独一无二而付出了巨大的努力,您还需要主键中的什么?在这个问题的另一方面,您的密钥的其他组件是否已经独一无二?如果是这样,为什么不直接使用它们?

    你的计划在我嘴里留下了不好的味道。不过我不太清楚为什么,因为我想得越多,它看起来就越牢固。

    • 起初我担心性能。但是浮点数只有 8 个字节(假设您的 DBMS 使用 IEEE 754 双精度),这并不是那么大。这并不比使用 64 位整数作为键或两个 32 位整数差。您的密钥生成过程是唯一可能会减慢的事情,但即便如此也不会减慢很多。
    • 然后我担心唯一性。此方案不保证您不会两次生成相同的密钥。但是鉴于您断言 user 和 datetime 的组合将是唯一的,那么这实际上可能有效:
      • IEEE 754 double 具有 53 位精度。
      • 日期时间将使用 42 位。假设:
        • 日期时间的分辨率为 1/300 秒(3.33...毫秒)。至少 MS SQL Server 是这样。
        • 上限(log2(86400 * 300 * 100000)) = 42
      • 这会为您的员工 ID 留下 9 位。如果员工 ID 大于 511,那么您将丢失部分日期时间,但会以毫秒为单位。您的员工 ID 可以达到 131071,然后您就会失去超过一秒的准确性。
    • 然后我担心以后查找键值会很困难。鉴于 0.2 != 0.1 + 0.1 问题,浮点相等的问题总是浮现在脑海中。但是你没有理由对这个键值执行任何计算,并且可能在任何给定时间它都是 IEEE 754 双格式(无论是在表中,在存储的过程变量中,还是在你的可执行文件中的变量中),然后它永远不会改变,可以被视为唯一的 64 位值。

    在考虑了所有这些之后,您的方案看起来确实相对安全。 Edoode's suggestion 关于不对索引进行聚类是一个好方法,考虑到这一点,以及我上面关于员工 ID 大小的警告,您可以使用此方案生成主键,就像任何其他方法一样.

    不过,我仍然怀疑这是否是最好的方法,或者是否有必要。

    • 复合键的其他组件不能自己使用(即作为自然键)吗?

    • 可以按照您的建议,在另一个表中保留一个顺序键种子。而且您只需要一张表,而不是您假设的每个分区一张表。您只需要此表中的两列:一列用于分区号,另一列用于该分区的当前标识值。

    • 使用 GUID 或 varchar 主键并非不可能。许多人在许多不同的桌子上这样做。它不会扼杀你的表现。而且它可能比这个方案更直接,或者至少更容易理解。

    • 如果您的复合键已经包含员工 ID,您只需在键中添加一个日期时间列并称之为一天。或者,如果没有,您可以添加两列。没有理由将两者混合在一起。

    HTH

    【讨论】:

    • 我终于下定决心使用 PDaddy 建议的复合主键。最初我认为使用复合键可能会降低性能,但在我的情况下,使用 Int 和 datetime 字段的组合并没有太大区别。复合键是非聚集的,我在 Int 字段上创建了聚集索引
    • 忘了提到聚集索引是在 Int 字段上创建的,因为它将用于连接
    【解决方案2】:

    我不会考虑这种非正统的密钥生成模式 - 这有点糟糕。你为什么不只使用整数?有许多方法和算法来协调分布式密钥生成。从锁定整个表并在为每个客户预分配 id 范围搜索下一个空闲 id 到从客户特定信息中获取它(类似于您的员工+时间建议)。

    【讨论】:

    • 锁定整个表并搜索下一个空闲 id - 我试图避免这种方法,因为锁定具有数百万行的表并扫描它以查找下一个可用 id 可能会成为性能瓶颈预分配 id范围 - 我试图找出避免表扫描的方法是否可以通过这种方式导出
    • 它实际上只需要锁定索引。搜索一百万以上的索引还不错。毕竟,这就是它们的用途。
    • 我刚刚提到为了完整性而锁定表,但真的不推荐它(在高并发环境中)。
    【解决方案3】:

    由于您没有提到 rdbms,我将假设 SQL 服务器。创建主键时,还会在该键上创建聚集索引。表按此键的顺序排序。当使用 Guids 作为主键(带有聚集索引)时,每次插入都意味着对表进行重新排序。这也适用于您的浮点表示。除了其他问题,如果您希望使用此方案,请不要在此主键上创建聚集索引。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-07-22
      • 2019-02-17
      • 1970-01-01
      相关资源
      最近更新 更多