【问题标题】:PHP - Is uniqid("") a good practical solution to generate a unique and sequential key server side?PHP - uniqid("") 是生成唯一且连续的密钥服务器端的一个很好的实用解决方案吗?
【发布时间】:2014-08-22 17:37:20
【问题描述】:

全部,

我想在注册期间生成服务器端一个用户ID。此 id 应该是连续的以允许聚集索引。显然,它还需要是独一无二的。

我可以在多大程度上使用uniqid("")?我不是在构建 Google,我假设 2 个用户在同一微秒内注册的风险几乎为零 - 但我没有实际经验可以作为自己的基础。 SQL 将拒绝重复的 id,并且我可以在我的 php 代码中设置一个循环,以便在发生冲突/数据输入失败的情况下继续发送新的注册条目,但这只有在很少发生冲突时才是一个好方法。

或者,我可以添加一个带有uniqid("",TRUE) 的随机后记,但是如果有 2 个用户在同一微秒内注册,则密钥不再是连续的。

解决我的难题的最佳实用方法是什么?我是不是想多了?

谢谢,

JDelage

【问题讨论】:

  • 我从来没有遇到过 uniqid() 的任何问题。
  • 使用GUID

标签: php mysql optimization


【解决方案1】:

uniqid 只不过是microtime 的接口(这就是它生成顺序 ID 的原因),因此,它可以是可预测的并且可以创建一个副本。

这个 id 应该是连续的以允许聚集索引。显然,它也需要是独一无二的。 [...] 我是不是想多了?

包括 MySQL 在内的大多数数据库都包含事务安全序列生成器。 MySQL's implementation, AUTO_INCREMENT,非常原始,但也很有效。 auto-inc 主键将确保唯一性,更重要的是,并不奇怪

也就是说,只要确保表中的 id 列是主键,就可以完全防止重复 ID。

【讨论】:

  • 如果您在代码中执行此操作而不是让数据库处理它,则在涉及事务时您无法保证完整性。您将需要担心锁定、死锁和回滚。如果您让数据库为您处理它,您将无需担心任何这些。在数据库之外做自己的标识符是insanity。相信我,我继承了一个生成代码端标识符的代码库,处理由此产生的边缘情况是一场彻头彻尾的噩梦。
  • 我很欣赏真实的见证。所以你是说一个额外的 db 调用的成本(因为我计划使用用户 id 作为散列 pw 的盐)被它以后消除的风险所抵消......
  • 不是查询的成本,而是设计的成本。您必须获取下一个可用标识符,做一些工作,然后进行插入。如果另一个进程同时运行并且碰巧在做同样的事情,它实际上可能在插入之前就成功了。这意味着插入将因重复键错误而失败,如果您在事务中,则立即或提交时。然后,您的代码必须清理混乱并尝试获取下一个 id 以再次尝试插入。这里唯一的解决方法是独占表锁。 (续)
  • 所有这些工作只是避免使用数据库的内置、事务安全、保证唯一的序列生成机制?这完全不值得努力。如果您需要标识符来执行密码加盐,请将密码字段设置为可为空并使用加盐密码执行更新后插入。空密码也可以是一个方便的“不允许登录”标志。 (由于多主复制,我继承的代码库会生成自己的标识符,这在 MySQL 5.x 之前不能很好地适应自动增量。)
  • 假设从管理的角度来看,这些用户帐户可以被禁用。考虑被禁止访问网站。或者被解雇。存储“帐户处于活动状态”状态的一种方法是为此目的设置一个标志。只需重用密码字段即可达到相同的效果。毕竟,null 密码总是会阻止用户登录。请记住,这只是针对这种特定案例的一种方便。
【解决方案2】:

在数据库理论中,假设没有事件在同一时间段发生,在您的情况下为微秒。许多数据库概念都建立在这一假设之上。

我没有听说uniqid 在顺序请求中生成了相同的哈希值。因为,它的种子是微秒,而且很可能您的处理器或多或少在微秒内执行了一条机器指令,因此处理两个请求之间的时间太长(即使它们在单个 CPU 中同时运行)。

如果您认为您可能在 512 位哈希(32 个十六进制字符)中存在冲突,那么您可以构建自己的摘要函数,该函数利用更多字母和标点符号,因为正如我之前所说,我们解决了时间种子问题,你应该担心的必须是充分利用种子并降低碰撞概率。

如果您想确定,您可以以互斥方式为请求提供服务,然后在为请求提供服务后,您可以在数据库中插入一行(比如说用户注册,您向用户插入一条用户记录表)并使用 PHP 函数 mysql_insert_id() 然后您可以继续处理其他请求,以便您可以使用最近插入的记录 ID 作为种子(对于任何两个连续的请求,这总是不同的)。

应要求澄清最后一段: 正如我之前所说,根据服务理论,数据库中不会同时发生任何事情。总是,其中一个先上菜。因此,您可以在数据库中启动事务。假设您正在注册一个新用户。你会说INSERT INTO users (username, password) VALUES (?, ?)。你去了,你已经插入了一个新用户。当这个数据库查询发生时,你应该实现一个互斥锁(mutex),我不知道它是如何在 PHP 中完成的。如果您实施互斥锁,第二个请求将等到您完成第一个请求的处理。将行插入数据库后,您可以使用$i = mysql_insert_id() 检索刚刚插入的行的 ID!当您为第二个用户的 ID 说 $i+1 时,它将是唯一的。因为第一个用户是300,那么第二个用户就是301

事实上,数据库自动增量 ID 始终是唯一的。如果您想为用户提供唯一的字符串哈希,这里有一个方法。

  1. 插入用户。 (上述查询)
  2. 使用$i = mysql_insert_id()获取用户ID
  3. 然后用一些摘要算法加密这个id,比如$d = strrev(md5($i))
  4. 然后使用UPDATE user SET unique_id='$d' where id=$i 查询将该值放在users 表的unique_id 列上。
  5. 好了,每个人都有唯一的字符串。

【讨论】:

  • 很好的答案,谢谢。对阅读有关处理器速度的事情特别感兴趣。
  • 不客气。您可以在网络上阅读有关摘要哈希或随机序列的唯一性和冲突的更多信息。这也是一个学术领域。
  • 如果您有更多时间,我希望能在某个时候对您的最后一段进行澄清。
  • 我认为任何时钟速度 >1GHz 的现代处理器都能够以 纳秒 而不是微秒的数量级执行单个机器指令。
【解决方案3】:

我偏爱 UUID。这不是一个递增的数字,应该始终是 unqiue。但是,不要在 INSERT 语句中使用函数 UUID。这可能会导致问题。

【讨论】:

    猜你喜欢
    • 2012-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多