Guid.NewGuid()VS来自Random.Next()的随机字符串生成器

Geo*_*ell 25 c# random guid

我的同事和我正在讨论使用哪些方法来自动生成用户ID和发布ID以便在数据库中进行识别:

一个选项使用Random的单个实例,并采用一些有用的参数,因此它可以重用于各种string-gen案例(即从4位数字引脚到20位字母数字id).这是代码:

// This is created once for the lifetime of the server instance
class RandomStringGenerator
{
    public const string ALPHANUMERIC_CAPS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
    public const string ALPHA_CAPS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    public const string NUMERIC = "1234567890";

    Random rand = new Random();
    public string GetRandomString(int length, params char[] chars)
    {
        string s = "";
        for (int i = 0; i < length; i++)
            s += chars[rand.Next() % chars.Length];

        return s;
    }
}
Run Code Online (Sandbox Code Playgroud)

另一种选择就是使用:

Guid.NewGuid();
Run Code Online (Sandbox Code Playgroud)

在MSDN上看到Guid.NewGuid

我们都知道这Guid.NewGuid()可以满足我们的需求,但我宁愿使用自定义方法.它做同样的事情,但有更多的控制.

我的同事认为,由于自定义方法已经自己制作,因此更容易产生碰撞.我承认我并不完全了解Random的实现,但我认为它与Guid.NewGuid()一样随机.自定义方法的典型用法可能是:

RandomStringGenerator stringGen = new RandomStringGenerator();
string id = stringGen.GetRandomString(20, RandomStringGenerator.ALPHANUMERIC_CAPS.ToCharArray());
Run Code Online (Sandbox Code Playgroud)

编辑1:

  • 我们使用的Azure表没有自动增量(或类似)功能来生成密钥.
  • 这里的一些答案只是告诉我使用NewGuid()"因为这就是它的用途".我正在寻找一个更深入的理由,为什么煮熟的方法可能更容易产生碰撞,给予与Guid相同的自由度.

编辑2:

我们也使用煮熟的方法来生成帖子ID,与会话令牌不同,它需要在我们网站的网址(如http://mywebsite.com/14983336)中显示相当漂亮,所以guids不是这里的选项但是仍然要避免碰撞.

Eri*_*ert 50

我正在寻找一个更深入的理由,为什么煮熟的方法可能更容易产生碰撞,给予与Guid相同的自由度.

首先,正如其他人所指出的那样,Random不是线程安全的; 从多个线程使用它可能会导致它破坏其内部数据结构,以便它始终生成相同的序列.

其次,Random根据当前时间播种.Random在同一毫秒内创建的两个实例(回想一下毫秒是现代硬件上的几百万个处理器周期)将具有相同的种子,因此将产生相同的序列.

第三,我骗了.Random没有根据当前时间播种; 它是根据机器活动的时间量播种的.种子是一个32位的数字,由于粒度是以毫秒为单位,所以只有几个星期,直到它包裹.但那不是问题; 问题是:创建该实例的时间段Random很可能在机器启动后的几分钟内.每次你给机器重新上电,或者让一台新机器在一个集群中联机时,就会有一个小窗口,其中创建了Random实例,发生的越多,你获得种子的几率就越大你以前有过的.

正如其他人所说:如果你想要数据库的主键,那么让数据库生成一个主键 ; 让数据库完成它的工作.如果你想要一个全局唯一的标识符,那么使用guid ; 这就是他们的目的.

最后,如果您有兴趣了解更多关于guids的使用和滥用的信息,那么您可能需要阅读我的"guid guide"系列; 第一部分在这里:

http://blogs.msdn.com/b/ericlippert/archive/2012/04/24/guid-guide-part-one.aspx


Geo*_*ell 7

正如其他答案所述,我的实施有一些严重的问题:

  • 线程安全: Random不是线程安全的.
  • 可预测性:由于Random类的性质,该方法不能用于会话令牌等安全关键标识符.
  • 碰撞:即使该方法创建了20个"随机"数字,但碰撞的概率不是(number of possible chars)^20由于种子值仅为31位,而是来自不良来源.给定相同的种子,任何长度的序列都是相同的.

Guid.NewGuid() 没问题,除了我们不想在URL中使用丑陋的GUID和.NET时,不知道NewGuid()算法在会话令牌中使用加密安全 - 如果知道一点信息,它可能会给出可预测的结果.

这是我们现在使用的代码,它是安全的,灵活的,据我所知,如果给出足够的长度和字符选择,它不太可能产生冲突:

class RandomStringGenerator
{
    RNGCryptoServiceProvider rand = new RNGCryptoServiceProvider();
    public string GetRandomString(int length, params char[] chars)
    {
        string s = "";
        for (int i = 0; i < length; i++)
        {
            byte[] intBytes = new byte[4];
            rand.GetBytes(intBytes);
            uint randomInt = BitConverter.ToUInt32(intBytes, 0);
            s += chars[randomInt % chars.Length];
        }
        return s;
    }
}
Run Code Online (Sandbox Code Playgroud)