生成大量独特的随机组合

3
我有三个表:其中一个是唯一的“昵称”字段的用户表,还有四百多个名称,三十万多个形容词以及大量可能的组合。
当用户订阅时,可以通过将随机名称与随机形容词相结合来生成一种独特、随机且有趣的昵称。用户单击按钮,Voilà!一个令人兴奋的身份得以诞生。
我通过运行两个查询来选择随机名称和形容词:
SELECT FLOOR(RAND() * COUNT(*)) AS `offset` FROM names/adjectives

并且

SELECT * FROM names/adjectives LIMIT offset, 1

然后我会检查用户是否不幸地生成了一个已经存在的身份标识。

SELECT COUNT(nickname) FROM users WHERE nickname=:generatedNickname

如果他是的话,可怜的家伙,我会再次遍历直到找到一个未被占用的东西。
但是,正如你们可能已经发现的那样,用户基数的增长也意味着更长的循环和我的脆弱的EC2 Tier 1 Matchbox更多的汗水。所以我想出了一个绝妙的解决方案:如果我预先生成所有可能的组合并将它们塞进一个巨大的表格中会怎样? 这将允许简单的拔插操作,而我将在某个匿名海滩上轻松地享受鸡尾酒,或者不会吗?我的谦卑的LAMP实例会在庞大的表格(男性和女性)的壮丽景象面前颤抖而逃吗?有更好的解决方案吗?

你会将组合存储为文本值还是作为指向这些表的两个外键?300K * 400 = 大约1.2亿种组合。这不是一个小表,但如果它只包含两个整数,那么它是可以管理的(大约几个GB)- 但你真的期望有这么多用户吗?平均预期循环次数是否会超过2?(如果我的数学没有太生疏,这意味着拥有大约6000万用户) - jkavalik
但是如果你仍然担心这个问题,那么你可以在“离线”状态下预先生成数千个组合(例如在晚上),并将它们存储到表格中。当需要使用时,直接从表格中获取即可,无需一次性生成所有组合。 - jkavalik
@jkavalik 这些组合被存储为文本。我永远不会有那么多的用户。虽然我也想到了碰撞的可能性非常小,但向更有经验的人请教从来都没有坏处。谢谢,伙计! - Răzvan
使用 OFFSET 不是高效的做法 -- 处理过程需要读取那么多行才能找到你想要的第一行。 - Rick James
2个回答

1
  1. 给名词和形容词一个AUTO_INCREMENT id作为PRIMARY KEY。另一列(名词/形容词)应为UNIQUE
  2. 在某个方便的地方保留这两个表的COUNT(*)。如果修改了表,则重新计算这些计数。不要在下面的代码中执行SELECT COUNT(*),它将执行表扫描--代价高昂。
  3. 使用SELECT noun FROM Nouns WHERE id = CEIL(noun_count * RAND())来获取随机的“名词”。对于“形容词”也是如此。
  4. 现在我们需要检查是否有重复项。你已经将形容词-名词组合存储在用户表中了吗?并且它被INDEXed了,正确吗?因此,只需检查此组合是否已被使用。
  5. 如果是重复项,则重新开始。

这些步骤都不会花费很长时间,因此即使您必须(很少)重复该过程,也不会花费很长时间。

PS:我认为你会发现RAND()对于这个任务来说已经足够好了。


1
提前生成这些组合会导致大量数据。我不建议这样做。我的建议是使用比 RAND() 更好的随机源。根据你的估计,发生冲突的可能性只有约为 n/120000000,其中n是用户数量,因此如果你确实遇到了一个冲突,你的循环不会运行很长时间。

你刚刚证实了我的想法,即发生冲突的可能性很小,但正如我已经告诉@jkavalik的那样,询问一下也无妨。你能否建议一个更好的随机源,php的rand更好吗? - Răzvan
1
@Răzvan:PHP的rand()并不是特别优秀。如果可以的话,使用诸如openssl_random_pseudo_bytes()random_bytes()random_int()函数等。具有密码学安全性的随机性最能最大程度地减少碰撞。 - jwueller

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接