【问题标题】:Generating BIGINT Keys using Random Class C# [duplicate]使用随机类 C# 生成 BIGINT 密钥 [重复]
【发布时间】:2014-12-10 10:02:42
【问题描述】:

我的 SQLDB 中有一个主键为 BIGINT(64 位) 现在由于 C# 没有直接生成 64 位密钥的机制,我使用以下代码生成密钥。我的问题是我在我的键列中看到随机但聚集的值。

tl.ID = (this.r.Next() << 32) | this.r.Next();

其中 tl.ID 是一个 LINQ to SQL 类并且是一个 LONG。 this.r 是随机的实例

Random r = new Random((int)DateTime.Now.Ticks);

在 DB 中看到的一些值:

MAX:2147483647
Top few values:
2147478265
2147478479
2147478526
2147479034
2147479663
2147480695
2147480783
2147480887
2147480984
2147481817
2147482099
2147482607
2147483321
2147483391
2147483558
2147483644
2147483647

【问题讨论】:

  • 为什么不对数据库中的主键启用自动增量?
  • 我认为一旦创建了数据库就不可能了。
  • 不是重复的,因为我使用的是帖子中提到的相同技术。创建一个项目并移动 32 位。我的问题是这些生成的值中的大多数都非常接近。某些演员表有问题吗?
  • @confusednerd,您可以在事后使字段成为身份,您只需确保您的初始种子高于现有的最高值。也不知道为什么你认为一个键需要是随机的,它只需要是唯一的,如果它是连续的,那么你的索引会表现得更好。
  • @confusednerd 为了避免有符号无符号问题,生成 4 个 16 位整数而不是 2 个 32 位整数可能是值得的。但是在这一点上,解决方案失去了它的优雅,所以我认为最好坚持使用 Random.NextBytes 版本 [...] 它在价值分布方面看起来相当不错(从我运行的非常简单的测试来看)

标签: c# linq random


【解决方案1】:

主键首先应该是唯一的。

将标识列添加到您的数据库/自动增量,您会没事的。

如果需要,添加具有自动增量的新标识列,删除旧标识,将新标识重命名为旧名称即可

【讨论】:

  • 至少有一个合理的理由,我能想到:如果您需要在您的域(而不是 sql)中生成 PK,以批量构建层次结构(如父子)。不过,在这种情况下,我宁愿选择 GUID。
  • 嗯,是的,但在这种情况下,问题中的随机部分严重混淆了我。
  • 好吧,如果你没有从下一个最好的机会增加的池子,那就是随机进行;)这绝不是一个正确的解决方案......
【解决方案2】:

您必须在移位之前将数字转换为 long,否则这只会移出有效位。

tl.ID = ((long)this.r.Next() << 32) | this.r.Next();

请记住,这种方法并不完美,正如 cmets 中提到的问题所指出的那样,因为 Random.Next() 只返回正值,所以实际上你只能得到 62 个随机位。

【讨论】:

  • 太棒了!这就是问题所在!
  • 这种方法只能生成2^32多个id,所以不妨将1个int转换成long
  • 为什么是 2^32?我现在要指出它可以生成 2^62 个唯一 ID,因为 r.Next 只返回正值。
  • Random 是确定性的。第一次调用有 2^32 个可能,但第二次调用是完全可预测的,并且在第一次调用之后总是返回相同的值。也就是说,如果第一次调用Next() 产生1589351479 下一个Next() 调用总是636837953
  • @weston 这是我第一次听说。你有这方面的资料吗?这个缺陷是 Random.Next() 固有的,还是也适用于使用 Random.NextBytes 的解决方案?
【解决方案3】:

正如 AlexDev 所说,问题出在第一种情况下,在将其移动 32 位之前,它应该被转换为长整数。

【讨论】:

    【解决方案4】:

    问题 1: 使用随机数生成主键可能不是一个好主意,因为最终您会遇到冲突,因此使用您拥有的代码会遇到问题,除非您在使用它之前检查每个生成的键都是唯一的。 如果您确实需要随机数作为键,请使用以下解决方案。

    问题 2: Random 类方法 Next 生成一个 int,在您的情况下为 32 位,这将环绕,您将只有预期范围的一半。另一个问题是标准的 Random 类并不是一个好的随机生成器。

    问题 3: 使用由 Random 生成的两个 Int32 并将一个(转换为 long)并附加另一个并不是生成 64 位随机数的最佳方法,你的双打机会增加(这是一个超出本次讨论的加密主题,所以我会就这样吧:))

    解决方案: 要真正生成随机 Int64,您可以使用 Random 甚至更好的 RandomNumberGenerator 为您的 64 位数字填充 8 个字节的数组,并将该数组转换为 long (Int64),例如:

    using System.Security.Cryptography;
    
    var rng = RandomNumberGenerator.Create();
    //bytes to hold number
    var bytes = new byte[8];
    //randomize
    rng.GetNonZeroBytes(bytes);
    //Convert
    long random = BitConverter.ToInt64(bytes, 0);
    

    这将生成一个真正的 64 位随机数,RandomNumberGenerator 是比 Random 类更好的随机生成器(更难预测)。

    【讨论】:

    • 我不确定这是否是一个额外的问题点或已经涵盖,但由于Random 在 32 位上工作,因此只能有 2^32 种可能的结果。所以你只能通过任何 concat 方法生成 2^32 个 long。
    • 它已经在 问题 2 中介绍过 :) Random 确实有一个方法 NextBytes,它与 RandomNumberGenerator 类似,允许您使用随机数填充字节数组,因此您可以使用它.但是 RandomNumberGenerator 是一个更好的“更随机”的生成器。例如。建议用于加密目的。
    • 啊,不,如果您使用接下来的 8 个字节仍然只产生 2^32 个唯一组合。
    • 另一种思考方式:在调用方法之前,Random 类只能处于 2^32 种状态。因此只能有 2^32 种可能的结果。不管你使用 nextbytes 还是 nextint。
    • 我不同意最后的说法,一个字节由 8 位组成,所以 2^8 组合。但是,如果您使用 8 个字节,您将有 2^64 个可能的结果,而不是 2^32 个。 (256x256x256x256)。
    猜你喜欢
    • 2011-02-27
    • 1970-01-01
    • 1970-01-01
    • 2018-01-12
    • 1970-01-01
    • 1970-01-01
    • 2016-08-15
    • 2018-09-10
    • 2015-09-27
    相关资源
    最近更新 更多