php 如何实现尽可能短的唯一 id

2014-10-10 10:45:45 +08:00
 cdffh
目前的实现方法为
md5(uniqid ( time (), true )) 生成出来有32位.因为要做到二维码里面,所以希望尽可能短,系统的数据量不会超过10亿(9位). 问下大家有没有什么比较好的方法.
15101 次点击
所在节点    PHP
40 条回复
feiyuanqiu
2014-10-10 11:31:10 +08:00
@cdffh
你想太多了,你可以把原数据保存在数据表里面,获取到每个数据的自增主键。
然后把这个自增主键转换成62进制,也就是这种大小写字母的格式就ok了。完全不需要hash什么的
akfish
2014-10-10 11:37:03 +08:00
碰撞问题并不难解决,用不重复的伪随机序列就行了,只要seed一样,生成随机id的顺序就是一样的。

分布式也没问题,把id地址空间分割成不重合的区间,分配给各个节点,每个节点在各自的区间里顺序取出id。
duzhe0
2014-10-10 11:38:18 +08:00
如果用纯随机数来做id, 一般认为uuid(128位)是安全的。
如果希望id短,可以牺牲一点扩展时的便利性,用机器id+自增id组合成id。
jarlyyn
2014-10-10 11:41:53 +08:00
看了半天感觉就是实现个uuid
按uuid的思路,可以采用自增啊。
被个设备有独立编号就可以了。
比如14位数字,前4位是设备号,后10位是自增数值。
然后base64压一下
ysjdx
2014-10-10 11:45:21 +08:00
整数遍历群模算法(貌似不是这么翻译的)

https://www.usenix.org/system/files/conference/woot14/woot14-adrian.pdf
看看随机生成ip的那个群模算法

可以随机不重复遍历某个整数区间。用这个随机不重复的id序列,应该可以解决问题?
feiyuanqiu
2014-10-10 12:09:34 +08:00
又想了下,真是不觉得有必要上那么高大上的算法,如果数据在单表,直接下面这个代码就行了,如果是多表,给每个表一个单独标识加最前面就行

<?php
$i = 1000;
while ($i--) {
var_dump(_uniqId());
}
exit;
function _uniqId()
{
static $now = 100000000;
return _10262($now++);
}

function _10262($n)
{
$b = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z');
$cn = $b[$n % 62];
if (($nn = floor($n / 62)) > 0) {
$cn .= _10262($nn);
}
return strrev($cn);
}
alex321
2014-10-10 12:14:41 +08:00
请参考短网址生成原理
cevincheung
2014-10-10 12:26:44 +08:00
二维码需要打印出来,所以必然是预先生成好的一堆数据然后才能打印。那就不怕重复了,任何一种方案在生成过程中不断的检测是否已经存在重复的ID就好啊。

比如最简单的随机数字, rand(100000000,999999999)。while(true) redis->has。难道你每天都生成一大堆?- -# 考虑一下实际应用场景。还有就是mysql的uuid [ select uuid() ]和[ select uuid_short() ]保证不重复 ....
wingoo
2014-10-10 12:53:02 +08:00
内部依旧int,外部用base62,把62个字符随机打乱下
keefo
2014-10-10 14:08:18 +08:00
首先,我觉得这个问题工程解法应重于理论解法。

推荐一个简单方法
unix_timestamp 10位 加上2位随机的字符[0-9a-z][0-9a-z]

大概意味着一秒钟内系统要生成超过1296条数据才肯定会有重复。如果你们系统负载量有这么大,可以在timestamp后面加上3位系统毫秒时间。

如果多套系统数据需要合并,最好在合并前给id加上一个domain prefix
例如 server1 server2
s1_timestamp_[0-9a-z][0-9a-z]
s2_timestamp_[0-9a-z][0-9a-z]

这样可以区分,也不会冲突,需要调用数据时候代码remove掉prefix。

无论多好的uuid算法,鲁莽的合并2个数据都不是正确做法。
keefo
2014-10-10 14:11:47 +08:00
没用[0-9a-zA-Z][0-9a-zA-Z]是因为我记得mysql默认是大小写不敏感的。如果你们数据库设置的是大小写敏感用[0-9a-zA-Z][0-9a-zA-Z]就更好了。组合数变成了3844
msg7086
2014-10-10 14:59:21 +08:00
合并两个数据库的话,id越长越不容易撞,越短越容易撞呗。

最简单的做法,id后面加一位来表示数据库编号。
0x142857
2014-10-10 15:00:51 +08:00
rtrim(base64_encode(md5(microtime())),"=")
jerray
2014-10-10 16:03:56 +08:00
Zuckonit
2014-10-10 18:49:09 +08:00
uuid4
c742435
2014-10-10 21:52:25 +08:00
自增,n个库每个分配一个唯一id 1-n
然后每次自增增量为n
nigelvon
2014-10-10 22:33:45 +08:00
自增能保证唯一且短,否则碰撞只是概率问题。至于自增涉及到不同的库,这有很多办法解决。
Actrace
2014-10-11 09:08:00 +08:00
把自增ID进行进制转换,这是以前的惯用手段.
在数据量不高的情况下,这个ID会短得让你非常满意.
如果是发邀请码这种应用就更适合这种场景了.
NCE
2014-10-11 17:34:55 +08:00
base64(guid)
qhxin
2015-12-20 09:00:09 +08:00
你说的很有道理。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/137973

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX