【问题标题】:Substitute for MySQL's UUID Version 1 function?替代 MySQL 的 UUID Version 1 功能?
【发布时间】:2017-02-21 19:25:09
【问题描述】:

上下文

Web 应用程序、PHP 5、MySQL 5.0.91

问题

我最近从使用自动递增整数转换为 UUID 作为我的一些表的主键。通过 MySQL 的 UUID() 函数生成 UUID 时,它们非常相似:

| uuid                                 |
----------------------------------------
| 1e5988da-afec-11e1-9877-5464f7aa6d24 |
| 408092aa-afad-11e1-9877-5464f7aa6d24 |
  ^------^   ^^
  1      8   11-12

如您所见,只有前 8 个字符和第 11 个和第 12 个字符不同。我了解 UUID 版本 1 使用时间戳和硬件 MAC 地址来生成 UUID。但是,由于这些相似之处(以及在我的情况下 MAC 地址永远不会改变的事实),我在使用版本 1 时犹豫不决。另外,如果 MAC 地址永远不会改变,那么大部分 UUID 都是无用的,而且是在浪费空间。

我的自定义 UUID 函数

作为一个实验,我用 PHP 编写了一个自定义 UUID 生成器:

public static function GenerateUUID()
{
    return
    substr(sha1(Account::GetUsername() . Account::GetUserID()), 18, 8) . "-" .
    substr(md5(time()), rand() % 28, 4) . "-" . 
    substr(md5(date("Y")), rand() % 28, 4) . "-" . 
    substr(sha1(rand()), 20, 4) . "-" . 
    substr(sha1(rand() % PHP_INT_MAX), 17, 12);
}

结果示例:

| uuid                                 |
----------------------------------------
| 574d18c2-5080-bac9-5597-45435f363ea1 |
| 574d18c2-30d4-8b5b-4ffd-001744d3d287 |

这里,前 8 个字符对于同一个用户是相同的。这是有意的,但不是必需的。

问题

是否有首选/推荐的方式在 MySQL 查询中生成版本 4 或版本 5 UUID

如果不是,是否可以在 PHP(如上)中生成不符合规范的自定义 UUID?

限制

  • 我正在使用具有命令行访问权限的共享主机计划,但无法修改现有的 MySQL 安装。
  • 我宁愿避免使用第三方包/库。

备注

  • 我不会也不会执行合并、同步或其他需要包含 MAC 地址的 GUID 的操作。这不是这里的问题。

【问题讨论】:

  • 只是为了更好地理解,您为什么停止使用自动编号并考虑制作某种 SHA?另外,还有 UUID_SHORT()
  • @MatthewRiches: UUID_SHORT() 在 MySQL 5.0.X 中不可用(我在我的问题中澄清了版本)。在阅读codinghorror.com/blog/2007/03/… 后,我决定从自动递增的整数切换。
  • 作为旁注:“如果MAC地址永远不会改变,大部分UUID都是无用的并且浪费空间”,这是错误的,如果你想做多服务器,MAC地址哈希是至关重要的同步。
  • @AlixAxel 但我没有,这就是为什么 MAC 地址永远不会改变。
  • @EvanMulawski:我明白了,但如果你这样做了,MAC 地址就会派上用场。 =)

标签: php mysql uuid


【解决方案1】:

您担心“大多数 UUID 无用且浪费空间”是数据类型大小所固有的。 您将永远无法在数据库中拥有尽可能多的条目,因为 16 字节的理论限制允许。

事实上,如果您将 UUID 用作表 ID,V1 UUID 比 V4 更合适 - 因为它使用 MAC 地址和时间戳来防止冲突。在 V4 中没有这样的机制,虽然实际上你也不需要太担心冲突 :) 如果你需要你的 UUID 不可预测,你应该使用 V4 UUID 而不是 V1。

另外,请注意,组合例如 4x4 字节的随机值可能与创建 16 字节的随机值不同。 与加密和随机性一样:我不建议您实现自己的 UUID::V4 例程。

如果安装在您的机器上,您可以使用php-uuid 包。

可在此处找到示例代码(可按原样在您的应用程序中使用): http://rommelsantor.com/clog/2012/02/23/generate-uuid-in-php/

像这样使用它:

$uuid = uuid_create(1);

能够在其网络服务器上安装软件包的用户可以安装所需的软件包,例如:(这里适用于 ubuntu)

apt-get install php5-dev uuid-dev
pecl install uuid

【讨论】:

  • 随着时代的变化......我想知道移动到随机 MAC 地址会增加什么影响(例如 Apple 的随机 MAC 地址移动策略,以增加来自 iBeam 和嗅探器的手机用户的匿名性)传感器)。我认为随着时间的推移,所有设备、所有制造商以及随机实例的数量……虽然数字很大,但版本 1 保证唯一性的论点现在正在逐渐消失。四年来发生了很多变化,这导致我们需要更多地思考“可能”发生的事情与我们今天所处的位置。
【解决方案2】:

欣赏“相似部分”实际上是一个相当不错的主意。它将允许您利用 MAC 地址来识别“我的哪些服务器生成了此 UUID?”......这在远程位置之间迁移数据时非常有用。你甚至可以这样说“这是我的测试数据”和“这是我的生产数据”。

PHP 有大量的 UUID 生成器库。

这是一个 PECL/PEAR 的东西(我从没用过):

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

来自 CakePHP 框架:

http://api.cakephp.org/class/string#method-Stringuuid(蛋糕 2.x) http://api13.cakephp.org/class/string#method-Stringuuid(蛋糕1.3)

最后一个生成器选项:

考虑使用 Linux 命令行 uuid 程序,该程序将具有 -v 版本控制标志和相关选项,并使用它来提供您的数据库。这有点低效,但至少您不必编写自己的生成器函数。

http://linux.die.net/man/1/uuid - 手册页

(Debian 包uuid

我注意到,对于命名空间版本,您将生成大量“长人名”以转换为 uuid。只要你和那些没有冲突,它可能会很甜蜜。例如,使用电子邮件地址注册的用户...获取该电子邮件地址的 v5 uuid...您总能找到那个人!似乎每次都吐出相同的 UUID,而 UUID 将代表 bob@bob.com 作为成员与 example.com 的唯一关系。

uuid -v5 ns:URL "http://example.com/member/bob@bob.com/"

评论:

另外,UUID,你似乎存储它们的方式,是 CHAR(36) 吗?您可能会后悔,一旦比较运算符起作用。

Postgres 将 UUID 视为 128 位值(并且可能会进行优化的二进制操作),而 MYSQL 的 CHAR(36) 解决方案正在查看 36 字节 = 288 位 ANSI 或 576 位 UTF8 加或减位/用于办公的字节(并且可能会执行更慢的多字节字符逐多字节字符字符串例程)。

我实际上已经对 MySQL 和 UUID 的问题进行了很多考虑......我的结论是,您需要编写一个存储函数,将十六进制表示转换为二进制表示进行存储,并且这将使所有“选择”语句都需要转换回十六进制表示……谁知道其中的效率会有多……所以最后只需要切换到 Postgres。呵呵

如果您确实想切换到 Postgres,如果它们是生产服务器,请在现有服务器上安装它时要非常小心。如...在实际进行迁移之前进行克隆以测试迁移过程。我不知何故设法杀死了我的系统,因为“安装此软件包将删除大量重要的其他软件包”(我不知道安装程序是如何做出这些决定的)。

或者,如果您准备最终付给他们很多钱来操作数据库...

目前使用 UUID 和 MySQL 往往是个坏主意。

【讨论】:

  • 哦...?看到@matthew-riches 的评论后,UUID_SHORT() 对于 90% 以上需要小规模唯一性的运营商来说有点酷……我仍然想要一个内置的基于整数的 128 位 UUID 生成器。跨度>
  • 根据我的卑微经验,将 UUID 作为 CHAR(36) 的成本高得离谱,并且在 1+ 百万、10+ 百万、100+ 百万行时变得愚蠢。使用集群和冗余比在“简单 uuid 行数”和“复合 uuid-外键关系数”级别进行 mysqldump 备份更具成本效益
  • 您应该将 UUID 存储为 BINARY(16) - 我从未尝试过使用 UUID 列作为 MySQL 中的主键,但我认为它可以有效地处理二进制键。令人讨厌的是,这意味着将uuid() 的输出十六进制解析为二进制,例如function to do that
  • 您可以使用uuid=UNHEX(REPLACE(?,"-","")) 将字符串转换为二进制版本。无需在那里编写存储过程。
  • 整洁。现在我想到这两个都以错误的顺序获取字节,但是只要您仅解析新生成的 UUID 并且您不尝试存储现有的 UUID 就可以了。
猜你喜欢
  • 1970-01-01
  • 2016-03-21
  • 2011-01-22
  • 2012-05-07
  • 2021-06-16
  • 2017-11-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多