之前的id生成是直接用的php的uniqid,存在的问题是高并发下容易重复,1000并发,每并发产生1000次,实际输出63万uid(可能是标准输出原因未全部输出),5次重复
function getUnique()
{
list($msec, $sec) = explode(" ",microtime());
return $sec.strval($msec*1000000);
}然后想以时间戳来产生随机ID,但发现太长了,我希望的是十位的纯数字ID,压缩得话怕重复度高不知道大大们有啥算法让在高并发下尽可能降低重复度,希望大大们能写出来看看,3Q~~
function getUnique()
{
list($msec, $sec) = explode(" ",microtime());
return $sec.strval($msec*1000000);
}然后想以时间戳来产生随机ID,但发现太长了,我希望的是十位的纯数字ID,压缩得话怕重复度高不知道大大们有啥算法让在高并发下尽可能降低重复度,希望大大们能写出来看看,3Q~~
————————————————————————————————
基于CSDN论坛提供的插件扩展功能,自己做了个签名档工具,分享给大家,欢迎技术交流 :)
这里有一篇关于UUID的说明 蛮详细
http://mlxia.javaeye.com/blog/279059
以下部分内容为转载:
我唯一还算熟悉的数据库就算是MySQL了,大概使用MySQL的人,百分之九九以上的人会使用Autoincrement ID做主键,这是可以理解的,因为MySQL的自增ID效率很高,使用也很方便。那么剩下的百分之一的人使用什么做主键呢?可能是自己做的 KeyGenerator,也可能是我们下面要说的UUID。据说在Oracle的圈子里,如果谁用自增ID做主键是要被鄙视的,主键最自然的选择就是UUID。我不了解Oracle,这些道听途说的结论是否正确不做承诺。那么我们先看看什么是UUID?简单的说,UUID是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。在UUID的算法中,可能会用到诸如网卡MAC地址,IP,主机名,进程ID等信息以保证其独立性。如果你的MySQL版本不太老的话,键入 SELECT UUID(); 输出的就是UUID,如下:mysql> select uuid();
+--------------------------------------+
| uuid() |
+--------------------------------------+
| 54b4c01f-dce0-102a-a4e0-462c07a00c5e |
+--------------------------------------+
现在大家应该对UUID有一个比较直观的认识了,我们来看看UUID的优缺点分别是什么。优点:能够保证独立性,程序可以在不同的数据库间迁移,效果不受影响。
保证生成的ID不仅是表独立的,而且是库独立的,这点在你想切分数据库的时候尤为重要。缺点:比较占地方,和INT类型相比,存储一个UUID要花费更多的空间。
使用UUID后,URL显得冗长,不够友好。下面针对上述UUID的缺点说说我的看法,比较占地方这个缺点我不是很在乎,现在最不值钱的就是硬盘了,略过此条缺点无妨。至于说使用UUID后,URL显得不友好,我觉得这多少是你的INT情结造成的惯性思维,其实,和INT类型相比,UUID才是最自然的主键选择,注意,我这里用的是自然这个形容词,仔细体会一下你能理解我的意思。另外,很多时候,URL本身就不需要友好,比如,一个电子商务网站,按照INT友好的URL说法,她的订单URL大概是下面这个形式的:/order.php/id/123,我要说明的是,这样是很友好,但是有些太友好了,友好的甚至不安全,比如说,我早晨下一个订单,发现URL是/order.php/id/1000,晚上再下一个订单发现URL是/order.php/id/2000,那么我就可以估计出此网站一天的订单数大致是1000左右,甚至能大体估计出它的销售额,而这些数据往往都是重要的商业秘密。使用UUID就没有这个顾虑。效率?如果上面说的UUID的所谓缺点都不成立的话,那么是否使用UUID做主键,唯一的问题就是效率了。据说在PostgreSQL等数据库里,都有专门的UUID类型,在这样的数据库里,使用UUID做主键,效率没有任何问题,可惜在MySQL里没有这样的字段,如果想在MySQL里保存UUID做主键,一般是使用CHAR(36)来模拟,因为不是一个原生的UUID类型,所以主键的效率到底如何有待测试,另外,UUID做主键的效率和UUID本身的算法实现也有很大关系。我本来想在我自己的电脑上插入1000000条数据测试一下看看来着,可惜一测试,硬盘灯就一直亮,让我很担心它会挂,虽然硬盘不值钱,但是我重要的数据都在上面,一旦坏了,损失就大了,所以,测试只好作罢。至于在MySQL上使用UUID(用char(36)存储)做主键,效率到底如何,我也不知道,抱歉 -_-!!!
如何生成UUID?下面这种方法生成的貌似不是UUID,因为MD5实际上是可能存在重复值的(参考http://www.phpx.com/happy/archiver/tid-56636.html),况且使用随机更不能避免存在重复.所以应直接使用mysql中的uuid函数生成
function uuid($prefix = '') { $chars = md5(uniqid(mt_rand(), true)); $uuid = substr($chars,0,8) . '-'; $uuid .= substr($chars,8,4) . '-'; $uuid .= substr($chars,12,4) . '-'; $uuid .= substr($chars,16,4) . '-'; $uuid .= substr($chars,20,12); return $prefix . $uuid; }
在mysql中插入uuid使用mysql的uuid()函数
INSERT INTO Table(id,..) VALUES( UUID(), ...)
当然也可以用 SELECT UUID() 先得到一个uuid值再插入进去
题外:
可能相比较使用整型做主键,效率稍差,
另外一个问题是可能导致URL太长,比如显示某个id下的分类时
通常这样category.php?cid=2 但是现在可能是category.php?uuid=a93f16c5-9634-102c-824f-3ea0651c5b77
是否能更改为整型做主键参考文章:
UUID做主键,好还是不好?这是个问题。
http://mlxia.javaeye.com/blog/279059
uuid做主键到底好是不好?
http://www.javaeye.com/topic/143812
文章来自:老李的日志。源地址:http://www.dayanmei.com/blog.php/ID_1065.htm
<?php// guid.phpclass System { function currentTimeMillis() { list($usec, $sec) = explode(" ",microtime()); return $sec.substr($usec, 2, 3); }} class NetAddress { var $name = 'localhost'; var $ip = '127.0.0.1'; function getHost($coumputer_name, $ip) { // static $address = new NetAddress(); $address->name = $coumputer_name; $address->ip = $ip; return $address; } function toString() { return strtolower($this->name.'/'.$this->ip); }} class Random { function nextLong() { $tmp = rand(0,1)?'-':''; return $tmp.rand(1000, 9999).rand(1000, 9999).rand(1000, 9999).rand(100, 999).rand(100, 999); }} class Guid{ var $valueBeforeMD5; var $valueAfterMD5; function Guid($computer_name, $ip){ $this->getGuid($computer_name, $ip); } function getGuid($coumputer_name, $ip){ $address = NetAddress::getHost($coumputer_name, $ip); $this->valueBeforeMD5 = $address->toString().':'.System::currentTimeMillis().':'.Random::nextLong(); $this->valueAfterMD5 = md5($this->valueBeforeMD5); } function newGuid() { $Guid = new Guid(); return $Guid; } function toString() { $raw = strtoupper($this->valueAfterMD5); return substr($raw,0,8).'-'.substr($raw,8,4).'-'.substr($raw,12,4).'-'.substr($raw,16,4).'-'.substr($raw,20); }}?> 调用<?phprequire_once('guid.php');$computer_name = $_SERVER["SERVER_NAME"];$ip = $_SERVER["SERVER_ADDR"];$guid = new Guid($computer_name, $ip);print $guid->toString();//调用产生的结果可能如: 3238D32E-807C-B1C4-01C4-FD1346D32110
1818391995
1818391997
1732053592
1770229981
1742194724
1839199593
1839189205
1841400155
1197161814
1198251274
1246289862
1362607654
1237869662
1224623833
/*
* 信号量(Semaphore)。
* 这是一个包装类,用于解决不同平台下对“信号量”的不同实现方式。
* 目前这个类只是象征性的,在 Windows 平台下实际是空跑(并没有真的实现互斥)。
*/
class SemWrapper
{
private $hasSemSupport;
private $sem;
const SEM_KEY = 1; public function __construct()
{
$this->hasSemSupport = function_exists( 'sem_get' );
if ( $this->hasSemSupport ) {
$this->sem = sem_get( self::SEM_KEY );
}
} public function acquire() {
if ( $this->hasSemSupport ) {
return sem_acquire( $this->sem );
}
return true;
} public function release() {
if ( $this->hasSemSupport ) {
return sem_release( $this->sem );
}
return true;
}
}/*
* 顺序号发生器。
*/
class SeqGenerator
{
const SHM_KEY = 1; /**
* 对顺序号发生器进行初始化。
* 仅在服务器启动后的第一次调用有效,此后再调用此方法没有实际作用。
* @param int $start 产生顺序号的起始值。
* @return boolean 返回 true 表示成功。
*/
static public function init( $start = 1 )
{
// 通过信号量实现互斥,避免对共享内存的访问冲突
$sw = new SemWrapper;
if ( ! $sw->acquire() ) {
return false;
} // 打开共享内存
$shm_id = shmop_open( self::SHM_KEY, 'n', 0644, 4 );
if ( empty($shm_id) ) {
// 因使用了 'n' 模式,如果无法打开共享内存,可以认为该共享内存已经创建,无需再次初始化
$sw->release();
return true;
} // 在共享内存中写入初始值
$size = shmop_write( $shm_id, pack( 'L', $start ), 0 );
if ( $size != 4 ) {
shmop_close( $shm_id );
$sw->release();
return false;
} // 关闭共享内存,释放信号量
shmop_close( $shm_id );
$sw->release();
return true;
} /**
* 产生下一个顺序号。
* @return int 产生的顺序号
*/
static public function next()
{
// 通过信号量实现互斥,避免对共享内存的访问冲突
$sw = new SemWrapper;
if ( ! $sw->acquire() ) {
return 0;
} // 打开共享内存
$shm_id = shmop_open( self::SHM_KEY, 'w', 0, 0 );
if ( empty($shm_id) ) {
$sw->release();
return 0;
} // 从共享内存中读出顺序号
$data = shmop_read( $shm_id, 0, 4 );
if ( empty($data) ) {
$sw->release();
return 0;
} $arr = unpack( 'L', $data );
$seq = $arr[1]; // 把下一个顺序号写入共享内存
$size = shmop_write( $shm_id, pack( 'L', $seq + 1 ), 0 );
if ( $size != 4 ) {
$sw->release();
return 0;
} // 关闭共享内存,释放信号量
shmop_close( $shm_id );
$sw->release();
return $seq;
}
}$a = SeqGenerator::init( time() );
var_dump($a);for ( $i=0; $i < 10; $i++ ) {
$seq = SeqGenerator::next();
var_dump($seq);
}
————————————————————————————————
基于CSDN论坛提供的插件扩展功能,自己做了个签名档工具,分享给大家,欢迎技术交流 :)
mysql没有guid子段类型(mssql有),也没有 uuid类型,虽然有这个func,(pgsql有)
全球通用唯一ID 是 35位的,如网卡MAC,当然也有人去掉'-'为 32位
mysql 至少要用 char(32)做这个事情.有很多人拒绝看那一长串的数据,而且对性能产生怀疑(其实虽有影响,但只有你有好的缓存设计这点不是问题)。具体到要使用整型,按我个人经验,10位是不可能,至少要20位的长整型(longint,biging)
如果谈在10位内尽量不重用,就可以参考二楼的意见。内存管理递增key,不过要注意一下每次起动时的初始化的问题。