天天看点

唯一编号算法:生成GUIDGuid全局唯一性算法

你有过生成不重复编号的想法吗?比如做一个自动保存网页图片的工具,要保证保存的图片不互相覆盖,一个想法是使用一个计数器从1开始递增,但是这样还有问题,比如我们无法保证磁盘中以前没有可能造成重复的图片文件。

那么就来看看GUID算法吧。

GUID(全局统一标识符)是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。它使用网卡MAC、地址、纳秒级时间、芯片ID码和许多可能的数字,这样保证每次生成的GUID永远不会重复,无论是同一个计算机上还是不同的计算机。

GUID长什么样呢?

{C7B1AFCC-810E-46d0-8157-09FC488D4C71}

看起来挺古怪的吧。在 Windows 平台上,GUID 应用非常广泛:注册表、类及接口标识、数据库、甚至自动生成的机器名、目录名等。

不用担心GUID的性能问题,因为它生成过程是采用MAC地址、机器时钟等计算的,没有并发问题,所以它一点都不比自增计数器的慢,有时候甚至更快。

讲了一大堆理论,在程序中怎么生成GUID呢?

非常简单,调用CoCreateGuid函数即可,它定义在objbase.h这个头文件中。

核心调用代码:

//--生成GUID

void generate_guid(TCHAR* guid_string)

{

 GUID guid;

 strcpy(guid_string,"");

 if( CoCreateGuid(&guid) == S_OK)

 {    

   _stprintf(guid_string, TEXT("{%08X-%04X-%04x-%02X%02X-%02X%02X%02X%02X%02X%02X}"), 

    guid.Data1, 

    guid.Data2, 

    guid.Data3, 

    guid.Data4[0], 

    guid.Data4[1], 

    guid.Data4[2], 

    guid.Data4[3], 

    guid.Data4[4], 

    guid.Data4[5], 

    guid.Data4[6], 

    guid.Data4[7]);

 }

 return ;

}

CoCreateGuid需要一个GUID结构体指针作为参数,GUID中有很多域,按照我给的方法把GUID打印成字符串即可。

GUID 可以用在很多的场合下,比如防止网站文章被小偷程序采集,比如一个查看文章的页面是 article.aspx?id=1,那么我可以写一个循环,就能够把所有的文章都采集过来。而如果使用GUID的话article.aspx?id=xxxx-xxxxxxxx-xxxx-xxxx,那么简单的循环就不行了,因为我无法预测下一个GUID是什么。

Guid全局唯一性算法

下面以Lua为例实现guid的生成,再对比nginx服务器的MD5算法进行比对效率

function guid()
    local seed = {
            '0','1','2','3','4','5','6','7','8','9',
            'a','b','c','d','e','f','g','h','i','j',
            'k','l','m','n','o','p','q','r','s','t',
            'u','v','w','x','y','z'
    }

    local sid = ""
    for i=1, 32 do
        sid = sid .. seed[math.random(1,36)]
    end

    return string.format('%s-%s-%s-%s-%s',
        string.sub(sid, 1, 8),
        string.sub(sid, 9, 12),
        string.sub(sid, 13, 16),
        string.sub(sid, 17, 20),
        string.sub(sid, 21, 32)
    )
end
local s = 0
local start = socket.gettime()

while s < 100000 do
    s=s+1
    --ngx.print(ngx.md5(math.random(1,36)) .. '\r\n')
    ngx.print(guid() .. '\r\n')
end

ngx.print("execute time:" .. socket.gettime()-start .. '\r\n')
ngx.exit(200)
           

代码

上面guid方法中seed读者可以自己自行扩展,比如再加入'A-Z'大写字符,guid我以32位的字符进行输出,

在实际测试过程中, 10万级的数据生成速度不考虑写文件的IO时间,远远低于0.4秒,而同等数量使用ngx.md5()时则足足多了一倍的时间;

再从唯一性上进行分析,10万级的生成串中,测试了10次,没有发现任何一次有重复的字符串,说明自配的guid算法足以满足实际生产使用;