UUID(Universally Unique Identifier,通用唯一识别码),是一种软件建构的标准,亦为开放软件基金会组织在分布式计算环境领域的一部分。

UUID 设计的目标是让分布式系统((Distributed Computing Environment,DCE))中的所有元素,都能有唯一的辨识信息,而不需要通过中央控制端来做辨识信息的指定,实现去中心化依赖。如此一来,每个人都可以创建不与其它人冲突的UUID。在这样的情况下,就不需考虑数据库创建时的名称重复问题。

目前最广泛应用的UUID,是微软公司的全局唯一标识符(GUID),而其他重要的应用,则有 Linux ext2/ext3文件系统、LUKS加密分区、GNOME、KDE、Mac OS X、唯一请求Id(rquestId)等等,另外我们也可以在e2fsprogs包中的UUID库找到实现。

 

UUID 算法原理

UUID是国际标准化组织(ISO)提出的一个概念。

UUID是一个128比特的数值,每4比特组成一个16进制数,即共 32个16进制,形如  C950EA01-1567-4CCA-8A22-43478F881DA4,长度段为 8-4-4-4-12,总计32位的十六进制数,这个数值可以通过一定的算法计算出来。为了提高效率,常用的UUID可缩短至16位。

UUID用来识别属性类型,在所有空间和时间上被视为唯一的标识。

一般来说,可以保证这个值是真正唯一的任何地方产生的任意一个UUID都不会有相同的值。

使用UUID的一个好处是可以为新的服务创建新的标识符。

这样一来,客户端在查找一个服务时,只需要在它的服务查找请求中指出与某类服务(或某个特定服务)有关的UUID,例如:每一次请求的 requestId,方便在后台追溯调用了哪些服务和函数的全过程,如果服务的提供者能将可用的服务与这个UUID相匹配,就返回一个响应。

UUID是基于当前时间、计数器(counter)、硬件标识(通常为无线网卡的MAC地址)等数据计算生成的。

UUID可以被任何人独立创建,并按需发布。UUID没有集中管理机构,因为它们是不会被复制的独特标识符。

属性协议允许设备使用UUID识别属性类型,从而不需要用读/写请求来识别它们的本地句柄。

 

UUID 组成参数

UUID是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的,通常平台会提供生成的API。

按照开放软件基金会(OSF)制定的标准计算,用到了以太网卡地址、纳秒级时间、芯片ID码和随机数

UUID由以下几部分的组合:

1)当前日期和时间,UUID的第一个部分与时间有关,如果你在生成一个UUID之后,过几秒又生成一个UUID,则第一个部分不同,其余相同。

2)时钟序列,相当于给了一个随机数。

3)全局唯一的IEEE机器识别号,如果有网卡,从网卡MAC地址获得,没有网卡以其他方式获得,因此会看到 UUID 有些部分是相同的。

UUID的唯一缺陷在于生成的结果串会比较长,共有32位十六进制数字(0-F)。

关于UUID这个标准使用最普遍的是微软的GUID(Globals Unique Identifiers)。在ColdFusion中可以用CreateUUID()函数很简单地生成UUID,其格式为:xxxxxxxx-xxxx- xxxx-xxxxxxxxxxxxxxxx(8-4-4-16),其中每个 x 是 0-9 或 A-F 范围内的一个十六进制的数字。

标准的UUID格式为:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12),可以从cflib 下载CreateGUID() UDF进行转换。

4)在 hibernate(Java orm框架)中, 采用 IP-JVM启动时间-当前时间右移32位-当前时间-内部计数(8-8-4-8-4)来组成UUID

 

UUID 编码规则

UUID(Universally Unique Identifier)全局唯一标识符,定义为一个字符串主键,采用32位数字组成,编码采用16进制,定义了在时间和空间都完全惟一的系统信息。

UUID的编码规则:

1)1~8位采用系统时间,在系统时间上精确到毫秒级保证时间上的惟一性;

2)9~16位采用底层的IP地址,在服务器集群中的惟一性;

3)17~24位采用当前对象的HashCode值,在一个内部对象上的惟一性;

4)25~32位采用调用方法的一个随机数,在一个对象内的毫秒级的惟一性。

通过以上4种策略可以保证惟一性,在系统中需要用到随机数的地方都可以考虑采用UUID算法。

 

UUID 重复概率

随机产生的UUID(例如:Java编程中由java.util.UUID类别产生的)的128个比特中,有122个比特是随机产生,4个比特在此版本('Randomly generated UUID')被使用,还有2个在其变体('Leach-Salz')中被使用。

利用生日悖论,可计算出两笔UUID拥有相同值的机率约为:

以下是以 x=2^122 (122个比特是随机产生,1比特状态只有 0 与 1,电流变化的两种状态),计算出UUID后产生碰撞的机率:

n 机率
2^36 4 x 10^-16
2^41 4 x 10^-13
2^46 4 x 10^-10

UUID与被陨石击中的机率比较的话,已知一个人每年被陨石击中的机率估计为170亿分之1,也就是说机率大约是0.00000000006 (6 x 10^-11),等同于在一年内置立数十兆笔GUID并发生一次重复。换句话说,每秒产生10亿笔UUID,100年后只产生一次重复的机率是50%。如果地球上每个人都各有6亿笔GUID,发生一次重复的机率是50%。

由此可见,产生重复GUID并造成错误的情况(概率)非常低,是故大可不必考虑此问题

机率也与随机数产生器的质量有关,若要避免重复机率提高,必须要使用基于密码学上的假随机数产生器来生成值才行。

 

 

PHP 自定义生成唯一识别码 uuid (简单,但不推荐)

vim generateUUID.php

<?php
/* 生成唯一标志
 * 标准的UUID格式为:xxxxxxxx-xxxx-xxxx-xxxxxx-xxxxxxxxxx (8-4-4-4-12, 32位)
 * mimvp.com  in  2019-01-01
 */
function UUID()
{
	$chars = md5(uniqid(mt_rand(), true));
	$uuid = substr ( $chars, 0, 8 ) . '-'
			. substr ( $chars, 8, 4 ) . '-'
			. substr ( $chars, 12, 4 ) . '-'
			. substr ( $chars, 16, 4 ) . '-'
			. substr ( $chars, 20, 12 );
	return strtoupper($uuid) ;
}

echo  "<br> UUID 11 : " . UUID();
echo  "<br> UUID 22 : " . UUID();
echo  "<br> UUID 33 : " . UUID();
?>

生成的 uuid 结果:

UUID 11 : C79FA9C1-1729-C599-A7F7-DB9E4CD54C1E
UUID 22 : BB6D516B-1290-C184-FDC7-4180BD29882C
UUID 33 : 021ADBBA-158D-047D-F68D-3BAD3B4909D3

 

 

PHP 自定义生成唯一识别码 uuid 改进型

<?php
/* 生成唯一标志
 * 标准的UUID格式为:xxxxxxxx-xxxx-xxxx-xxxxxx-xxxxxxxxxx (8-4-4-4-12, 32位)
 * mimvp.com  in  2019-01-01
 */
function UUID2()
{
	mt_srand ( ( double ) microtime () * 10000 ); 				// optional for php 4.2.0 and up.随便数播种,4.2.0以后不需要了
	$chars = strtoupper ( md5 ( uniqid ( rand (), true ) ) ); 	// 根据当前时间(微秒计)生成唯一id.
	$hyphen = chr ( 45 );										// ASCII 编码为横杠 '-'
	
	$uuid = '' 	. substr ( $chars, 0, 8 ) . $hyphen 
				. substr ( $chars, 8, 4 ) . $hyphen 
				. substr ( $chars, 12, 4 ) . $hyphen 
				. substr ( $chars, 16, 4 ) . $hyphen 
				. substr ( $chars, 20, 12 );
	
	return sprintf("chars : %s , hyphen : %s ,  UUID 11 : %s",  $chars, $hyphen, $uuid);
// 	return $uuid;
}

echo  "<br> UUID 11 : " . UUID2();
echo  "<br> UUID 22 : " . UUID2();
echo  "<br> UUID 33 : " . UUID2();
?>

生成的 uuid 结果:

UUID 11 : chars : 27A607DBDFB600E340214C5BEE2404DC , hyphen : - , UUID 11 : 27A607DB-DFB6-00E3-4021-4C5BEE2404DC
UUID 22 : chars : B7F673EDBBA24EF6D8FE9802479090A4 , hyphen : - , UUID 11 : B7F673ED-BBA2-4EF6-D8FE-9802479090A4
UUID 33 : chars : B2D5362546C5CD414276BDF249508B72 , hyphen : - , UUID 11 : B2D53625-46C5-CD41-4276-BDF249508B72

 

继续,改进思路

PHP 语言,可以使用C的库libuuid,然后利用PHP编写扩展的知识将其封装为PHP的库

Github:https://github.com/hggh/php5-uuid

在程序使用的时候可以特殊判断一下:

php test.php

<?php
// 如果扩展中,生成uuid的方法为myuuid(),那么程序如下
function get_uuid()
{
    if(function_exists('myuuid'))
    {
        return myuuid();
    }
    else
    {
        //网上一般生成uuid的方法
        return 'xxx';
    }
}
?>

 

网友评价建议:

目前很多所谓PHP生成UUID的方法,都以随机数最为其算法的基础,这虽然在实践中很难出现重复的几率,但在数学理论上显然是行不通的

这里用MongoDb的ObjectId作为例子,个人觉得这种生成方法是可以引入到PHP中的。

ObjectId主要分为四块:时间戳、毫秒数、机器码、自增计数

前两者就不用细说了,PHP都可以取到,重点在后两者上:

1、机器码主要用于避免在不同主机间生成相同的UUID

机器码目前主要的生成方法是根据硬件信息进行散列计算出,而这个方法并不适用于PHP

主要因为两点:

1)PHP不能直接获得硬件信息(需要通过扩展或者执行本地命令等方法),

2)PHP请求与请求之间无法共享数据,所以每次请求都得拉取机器码,对性能影响很大。

解决这一问题有个很简单的办法,就是在不同机器先计算出机器码,之后直接写入配置文件,PHP处理时直接读取即可。

 

2、自增计数主要用于避免在并发处理中生成相同的UUID

对于自增来说,PHP由于不能在请求间共享数据,所以不能直接实现自增计数。

解决这一问题我们可以引入PHP的两个扩展,分别是Semaphore和Shared Memory,这两者都在PHP源码中集成,通过--enable-sysvsem和--enable-sysvshm就能打开

我们只需要把自增数放置在共享内存中,再通过信号量限制访问,即可达到并发请求共享自增技术的目的了。

当然,自增也能通过其他工具来实现。

 

小结

总之一句话,PHP 实现真实的 UUID,必须通过扩展来实现!

http://pecl.php.net/package/uuid

 

PHP 安装扩展来使用真正的 UUID

1、需要先安装几个库

若是 CentOS 7 系统,则安装: yum -y install uuid uuid-devel e2fsprogs-devel libuuid-devel

 

这一大坨是MacOS安装配置uuid,若您是 Linxu CentOS 安装,请跳过,继续下面的步骤 2、

若是MacOS 系统,则安装:  brew install ossp-uuidbrew install re2c (测试验证成功,详见 stackoverflow),但此方法无法提供 uuid_create 函数

$ brew install ossp-uuid
==> Downloading https://homebrew.bintray.com/bottles/ossp-uuid-1.6.2_2.high_sierra.bottle.tar.gz
######################################################################## 100.0%
==> Pouring ossp-uuid-1.6.2_2.high_sierra.bottle.tar.gz
/usr/local/Cellar/ossp-uuid/1.6.2_2: 18 files, 207.3KB

于是,还得老老实实安装 MacOS 的 libuuid-1.0.3.tar.gz (2014-08-12) 和 uuid-1.0.3.tgz (2012-06-18),

二者版本日期要相近,特别是 uuid-1.0.3.tgz 不可用最新版(2019年),否则,uuid 会编译不通过,报错

好了,MacOS 配置 uuid 详细步骤(成功的经验,搞了一天,查了十多篇博客,没有一篇成功,都是自己摸索的):

1)MacOS 下需要 libuuid 手动下载地址:https://sourceforge.net/projects/libuuid/files/ (最新版 libuuid-1.0.3.tar.gz ,2014-08-12)

2)MacOS 下载完后,默认安装即可:

tar zxvf libuuid-1.0.3.tar.gz 
cd libuuid-1.0.3
./configure 
make && make install

MacOS 

3)下载老一些的 uuid 版本,例如: uuid-1.0.3.tgz (2012-06-18)

4)编译安装,注意:是

wget  http://pecl.php.net/get/uuid-1.0.3.tgz
tar zxvf uuid-1.0.3.tgz 
cd uuid-1.0.3
/usr/local/php/bin/phpize 
./configure --with-php-config=/usr/local/php/bin/php-config 
make && make install

5)安装 uuid 成功了,热泪盈眶啊

Build complete.
Don't forget to run 'make test'.

yg-mac:uuid-1.0.3 homer$ make install
Installing shared extensions:     /usr/local/php5/lib/php/extensions/no-debug-non-zts-20131226/

6)配置 php.ini

vim /usr/local/php5/lib/php.ini

在文件最末尾添加一行(实际发现没起作用,估计哪里配置了全局路径,我忘了,不管了)

;extension=/usr/local/php5/lib/php/extensions/no-debug-non-zts-20131226/xcache.so
extension=/usr/local/php5/lib/php/extensions/no-debug-non-zts-20131226/uuid.so

7)重启 php-fpm,nginx  (二者最好都重启)

/etc/init.d/php-fpm restart
/etc/init.d/nginx restart

8)编辑、查看 phpinfo()

至此,MacOS 配置 uuid (老版本一点的,新版本报错)成功,比 Linux CentOS 难多了,主要是资料不足,成功案例几乎搜不到。

 

 

2、在官网下载http://pecl.php.net/package/uuid

米扑博客下载的测试版本: http://pecl.php.net/get/uuid-1.1.0.tgz

 

3、进行编译安装(此处讲得都是 Linux,CentOS)

wget  http://pecl.php.net/get/uuid-1.1.0.tgz
tar zxvf uuid-1.1.0.tgz 
cd uuid-1.1.0
/usr/local/php/bin/phpize 
./configure --with-php-config=/usr/local/php/bin/php-config 
make && make install

 

MacOS 下安装 uuid-1.1.0.tgz 与 Linux 类似,不同的是 phpize 路径不同(具体看自己的php下载目录)

wget http://pecl.php.net/get/uuid-1.1.0.tgz
tar zxvf uuid-1.1.0.tgz 
cd uuid-1.1.0
/usr/local/php5/bin/phpize 
./configure --with-php-config=/usr/local/php5/bin/php-config 
make && make install

 

4、配置 php.ini 文件

vim /usr/local/php/lib/php.ini

导入 extension=uuid.so,在 php.ini 文件末尾添加一行

/usr/local/php/lib/php/extensions/no-debug-zts-20170718/uuid.so

 

5、验证是否配置好 uuid.so

1)编辑一个 php 文件 ,打印出 phpinfo

vim  phpinfo.php

2)添加内容:

<?php
echo phpinfo();
?>

3)打开网页:https://mimvp.com/phpinfo.php,查看 UUID

搜索,发现没有 uuid ,神马问题?哈哈,修改了配置,没重启生效啊,傻啊

4)重启 php-pfm 和 nginx (最好二者都重启

/etc/init.d/php-fpm restart
/etc/init.d/nginx restart

5)再次刷新网页:https://mimvp.com/phpinfo.php,查看 UUID,出现了

注:验证完后,一定要记得最后删除 phpinfo.php 文件,防止暴露自己的 php 配置,诱惑别人攻击你的漏洞哦

 

 

6、PHP 调用扩展的 uuid 函数

<?php
echo "UUID : " . strtoupper( uuid_create() );
?>

运行结果:

UUID : A59537D0-622E-4D90-A15E-BC1A3B40B745

 

最后,给出完整的 PHP 代码实现 (强烈推荐

/* 生成唯一标志
 * 标准的UUID格式为:xxxxxxxx-xxxx-xxxx-xxxxxx-xxxxxxxxxx (8-4-4-4-12, 32位)
 * mimvp.com  in  2019-01-01
 */
function UUID(){
    $uuid = '';

    if (function_exists('uuid_create') === true){
        $uuid = uuid_create(1);
    }else{
        $data = openssl_random_pseudo_bytes(16);
        $data[6] = chr(ord($data[6]) & 0x0f | 0x40); 
        $data[8] = chr(ord($data[8]) & 0x3f | 0x80); 
        $uuid =  vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
    }

    return strtoupper($uuid);		// 返回大写字母
}

说明:

本示例的自定义 uuid 生成代码,也可用上文 PHP 实现的 md5 ( uniqid ( rand (), true ) )

也可以使用更简单、粗暴的下面代码:

if (!function_exists('com_create_guid')) {
	function com_create_guid() {
		return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
		mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),
		mt_rand( 0, 0xffff ),
		mt_rand( 0, 0x0fff ) | 0x4000,
		mt_rand( 0, 0x3fff ) | 0x8000,
		mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff )
		);
	}
}

到这里,解决了问题的核心:

1)先判定是否存在 'uuid_create' 函数,若存在,则用之

2)若不存在,则使用自定义的 uuid 

 

 

session_create_id()

session_create_id 方法需要php7.1以上的版本才可使用。

关于session_create_id方法可参考官网说明: https://www.php.net/manual/zh/function.session-create-id.php

session_create_id ([ string $prefix ] ) : string

session_create_id() is used to create new session id for the current session. It returns collision free session id.

If session is not active, collision check is omitted.

Session ID is created according to php.ini settings.

It is important to use the same user ID of your web server for GC task script. Otherwise, you may have permission problems especially with files save handler.

 

PHP 对比 uuid_create() 和 session_create_id()

<?php
echo "<br><br> uuid_create() : " . uuid_create();
echo "<br><br> session_create_id() : " . session_create_id();
echo "<br><br> md5 ( uniqid ( rand (), true ) ) : " . md5 ( uniqid ( rand (), true ) );
?>

运行结果:

uuid_create() : 					ffbebbb5-fd98-4fee-9c3b-9c324d1c8d22
session_create_id() : 				d4kkg076md29fnp3t9p4c8e1ip
md5 ( uniqid ( rand (), true ) ) : 	a2702994267a2cbec578454da03cfc97

 

 

简单几行PHP代码实现通用唯一识别码(UUID)效果 没测试过,不推荐

<?php 
require 'vendor/autoload.php';
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\Exception\UnsatisfiedDependencyException;
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    try {
        $ver = isset($_POST['v']) ? $_POST['v'] : '';
        switch ($ver) {
            case '1':
                $uuids = Uuid::uuid1();
                break;
            case '3':
                $uuids = Uuid::uuid3(Uuid::NAMESPACE_DNS, 'blog.mimvp.com');
                break;
            case '4':
                $uuids = Uuid::uuid4();
                break;
            case '5':
                $uuids = Uuid::uuid5(Uuid::NAMESPACE_DNS, 'proxy.mimvp.com');
                break;
            case '':
                $uuids = Uuid::uuid1();
                break;
        }
        echo $uuids->toString();
    } catch (UnsatisfiedDependencyException $e) {
        echo 'Caught exception: ' . $e->getMessage() . "\n";
    }
}

 

 

uniqid — 生成一个唯一ID  (PHP 4, PHP 5, PHP 7)

说明

uniqid ([ string $prefix = "" [, bool $more_entropy = false ]] ) : string

获取一个带前缀、基于当前时间微秒数的唯一ID。

Caution

本函数并不会生成安全加密的值,不应用于加密用途。若需要安全加密的值,考虑使用openssl_random_pseudo_bytes()

Warning

此函数不保证返回值的唯一性。 由于绝大多数系统使用 NTP 或者类似服务调整系统的时间,所以系统时间经常发生变化。 此外,进程/线程可能不会返回唯一的 ID。 用 more_entropy 来增加唯一性的概率。

参数说明:

prefix

有用的参数。例如:如果在多台主机上可能在同一微秒生成唯一ID。

prefix为空,则返回的字符串长度为13。more_entropy 为 TRUE,则返回的字符串长度为23。

more_entropy

如果设置为 TRUEuniqid() 会在返回的字符串结尾增加额外的熵(使用combined linear congruential generator)。 使得唯一ID更具唯一性。
 
 
返回值

返回字符串形式的唯一ID。

此函数努力创建唯一识别符,但它不保证返回值得唯一性。

 

代码示例:

<?php
/* A uniqid, like: 4b3403665fea6 */
printf("uniqid(): %s\r\n", uniqid());

/* We can also prefix the uniqid, this the same as 
 * doing:
 *
 * $uniqid = $prefix . uniqid();
 * $uniqid = uniqid($prefix);
 */
printf("uniqid('php_'): %s\r\n", uniqid('php_'));

/* We can also activate the more_entropy parameter, which is 
 * required on some systems, like Cygwin. This makes uniqid()
 * produce a value like: 4b340550242239.64159797
 */
printf("uniqid('', true): %s\r\n", uniqid('', true));
?>

代码注意:

在Cygwin环境下,为了使此函数能够工作,more_entropy 必须设置为 TRUE

 

 

Python 生成  uuid

一个可以产生唯一uuid的库,详见米扑博客:11个小而美的Python库

import uuid
print uuid.uuid4()
# e7bafa3d-274e-4b0a-b9cc-d898957b4b61

 

 

UUID 广泛应用场景

1、Linux 系统文件

sudo /sbin/mkswap swapfile   

homer@homer-pc:/var$ sudo /sbin/mkswap swapfile
Setting up swapspace version 1, size = 2047996 KiB
no label, UUID=1fdd9f51-165c-462d-9708-e8548fd122d9

 

2、阿里云、腾讯云、AWS云的API 接口中的请求Id(requestId)

UUID格式为:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx   ,  8-4-4-4-12  , 32位

https://baike.baidu.com/item/UUID/5921266

 

腾讯云 requestID

RequestId  :    6ef60bec-0242-43af-bb20-270359fb54a7  ,  8-4-4-4-12  , 32位

https://cloud.tencent.com/document/api/213/15728

 

阿里云 requestID

RecordId    :   16808780091645952

RequestId  :    C950EA01-1567-4CCA-8A22-43478F881DA4  ,  8-4-4-4-12  , 32位

https://help.aliyun.com/document_detail/120193.html

https://help.aliyun.com/document_detail/25621.html

 

AWS requestID

requestId  :    59dbff89-35bd-4eac-99ed-be587EXAMPLE  ,  8-4-4-4-12  , 32位

https://docs.aws.amazon.com/zh_cn/AWSEC2/latest/APIReference/API_AllocateAddress.html

 

Google Cloud

https://cloud.google.com/compute/docs/reference/rest/v1/acceleratorTypes/list

 

 

参考推荐:

PHP 生成唯一请求标识 RequestID

PHP 资源收集整理大全

11个小而美的Python库

Python 和 PHP 对腾讯云签名 hmac_sha256 算法实现

CentOS 配置swap交换区的方法 (Linux 很多地方用到了 UUID)

Linux Shell 生成随机数和随机字符串

shell 脚本的常用功能代码

Kafka 分布式消息系统架构

Java8 十大新特性详解