一、简介
UUID,是Universally Unique Identifier的缩写,UUID出现的目的,是为了让分布式系统可以不借助中心节点,就可以生成UUID来标识一些唯一的信息;
GUID,是Globally Unique Identifier的缩写,跟UUID是同一个东西,只是来源于微软。
二、格式
UUID是由一组32位数的16进制数字所构成,是故UUID理论上的总数为16^32 = 2^128,约等于3.4 x 10^38。也就是说若每纳秒产生1兆个UUID,要花100亿年才会将所有UUID用完。
UUID的标准型式包含32个16进制数字,以连字号分为五段,形式为8-4-4-4-12的32个字符,如:550e8400-e29b-41d4-a716-446655440000。
三、组成:
UUID是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。通常平台会提供生成的API。按照开放软件基金会(OSF)制定的标准计算,用到了以太网卡地址、纳秒级时间、芯片ID码和许多可能的数字。
(1)当前日期和时间,UUID的第一个部分与时间有关,如果你在生成一个UUID之后,过几秒又生成一个UUID,则第一个部分不同,其余相同。
(2)时钟序列。
(3)全局唯一的IEEE机器识别号,如果有网卡,从网卡MAC地址获得,没有网卡以其他方式获得。
UUID的唯一缺陷在于生成的结果串会比较长。关于UUID这个标准使用最普遍的是微软的GUID(Globals Unique Identifiers)。
四、UUID的版本
标准格式
UUID的格式是这样的:xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
N那个位置,只会是8,9,a,b
M那个位置,代表版本号,由于UUID的标准实现有5个版本,所以只会是1,2,3,4,5
版本1:基于时间的UUID
通过当前时间戳、机器MAC地址生成;
由于在算法中使用了MAC地址,这个版本的UUID可以保证在全球范围的唯一性。
但与此同时,因为它暴露了电脑的MAC地址和生成这个UUID的时间,这就是这个版本UUID被诟病的地方。
版本2:DCE安全的UUID
DCE安全的UUID和基于时间的UUID算法相同,但会把时间戳的前4位置换为POSIX的UID或GID。
不过,在UUID的规范里面没有明确地指定,所以基本上所有的UUID实现都不会实现这个版本。
版本3:基于名字空间的UUID(MD5)
由用户指定1个namespace和1个具体的字符串,通过MD5散列,来生成1个UUID;
根据规范描述,这个版本的存在是为了向后兼容?平时这个版本我们也很少用到
版本4:基于随机数的UUID
根据随机数,或者伪随机数生成UUID。这种UUID产生重复的概率是可以计算出来的,但随机的东西就像是买彩票:你指望它发财是不可能的,但狗屎运通常会在不经意中到来。这个版本应该是平时大家无意中用得最多的版本了;
版本5:基于名字空间的UUID(SHA1)
和版本3一样,不过散列函数换成了SHA1
五、版本选择
- V4 - 首选
- V1 - 如果需要反向解析主机 Mac 地址
- V5 - 如果需要根据特定的值生成,而且在值不变的情况下生成的 UUID 不变。通知值很难破解出密码原文。
- V3 - 不推荐,用 V5 替代
- V2 - 一般不会用到
六、php代码实现
<?php
/**
* Class UUID
*/
class UUID {
/**
* v3版本
* 基于名字空间(md5)
* @param $namespace
* @param $name
* @return bool|string
*/
public static function v3($namespace, $name) {
if (!self::is_valid($namespace)) return false;
// Get hexadecimal components of namespace
$nhex = str_replace(['-', '{', '}'], '', $namespace);
// Binary Value
$nstr = '';
// Convert Namespace UUID to bits
for ($i = 0; $i < strlen($nhex); $i += 2) {
$nstr .= chr(hexdec($nhex[$i] . $nhex[$i + 1]));
}
// Calculate hash value
$hash = md5($nstr . $name);
return sprintf('%08s-%04s-%04x-%04x-%12s',
// 32 bits for "time_low"
substr($hash, 0, 8),
// 16 bits for "time_mid"
substr($hash, 8, 4),
// 16 bits for "time_hi_and_version",
// four most significant bits holds version number 3
(hexdec(substr($hash, 12, 4)) & 0x0fff) | 0x3000,
// 16 bits, 8 bits for "clk_seq_hi_res",
// 8 bits for "clk_seq_low",
// two most significant bits holds zero and one for variant DCE1.1
(hexdec(substr($hash, 16, 4)) & 0x3fff) | 0x8000,
// 48 bits for "node"
substr($hash, 20, 12)
);
}
/**
* v4 版本
* 基于随机数
* @return string
*/
public static function v4() {
return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
// 32 bits for "time_low"
mt_rand(0, 0xffff), mt_rand(0, 0xffff),
// 16 bits for "time_mid"
mt_rand(0, 0xffff),
// 16 bits for "time_hi_and_version",
// four most significant bits holds version number 4
mt_rand(0, 0x0fff) | 0x4000,
// 16 bits, 8 bits for "clk_seq_hi_res",
// 8 bits for "clk_seq_low",
// two most significant bits holds zero and one for variant DCE1.1
mt_rand(0, 0x3fff) | 0x8000,
// 48 bits for "node"
mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
);
}
/**
* v5版本
* 基于名字空间(SHA1)
* @param $namespace
* @param $name
* @return bool|string
*/
public static function v5($namespace, $name) {
if (!self::is_valid($namespace)) return false;
// Get hexadecimal components of namespace
$nhex = str_replace(['-', '{', '}'], '', $namespace);
// Binary Value
$nstr = '';
// Convert Namespace UUID to bits
for ($i = 0; $i < strlen($nhex); $i += 2) {
$nstr .= chr(hexdec($nhex[$i] . $nhex[$i + 1]));
}
// Calculate hash value
$hash = sha1($nstr . $name);
return sprintf('%08s-%04s-%04x-%04x-%12s',
// 32 bits for "time_low"
substr($hash, 0, 8),
// 16 bits for "time_mid"
substr($hash, 8, 4),
// 16 bits for "time_hi_and_version",
// four most significant bits holds version number 5
(hexdec(substr($hash, 12, 4)) & 0x0fff) | 0x5000,
// 16 bits, 8 bits for "clk_seq_hi_res",
// 8 bits for "clk_seq_low",
// two most significant bits holds zero and one for variant DCE1.1
(hexdec(substr($hash, 16, 4)) & 0x3fff) | 0x8000,
// 48 bits for "node"
substr($hash, 20, 12)
);
}
public static function is_valid($uuid) {
return preg_match('/^\{?[0-9a-f]{8}\-?[0-9a-f]{4}\-?[0-9a-f]{4}\-?' .
'[0-9a-f]{4}\-?[0-9a-f]{12}\}?$/i', $uuid) === 1;
}
}
// Usage
// Named-based UUID.
$v3uuid = UUID::v3('1546058f-5a25-4334-85ae-e68f2a44bbaf', 'SomeRandomString');
$v5uuid = UUID::v5('1546058f-5a25-4334-85ae-e68f2a44bbaf', 'SomeRandomString');
var_dump($v3uuid);
var_dump($v5uuid);
// Pseudo-random UUID
$v4uuid = UUID::v4();
var_dump($v4uuid);
/*
string(36) "643dc989-58e9-3e72-9fd5-e3b4a09b62e9"
string(36) "c8333619-8220-5b21-b5d9-2bd56713675a"
string(36) "8cfb2479-5cbf-428d-8894-3c4f7f33f173"
*/