PHP:生成唯一数字ID的最佳方式

5
我需要生成唯一的数字id。
  • 我可以使用uniqid,但它会生成包含字母和数字的值。

  • 我也可以使用time,但不能保证它总是唯一的。

  • 我还可以利用数据库中字段的自增属性来获取唯一的id,但这需要一个数据库。

那么,生成唯一的数字id最好的方法是什么?

1
如果需要将其放入数据库中,建议在数据库中保留唯一标识符。 - Marco Mura
1
@MarcinOrlowski 我不需要随机的,我需要唯一的。 - itsazzad
你可以使用 microtime(true) 并将其小数部分附加到整数部分,这将在大多数情况下为您提供一个唯一的值,实际上。 - Tejesh
3
了解一下uniqid实际上是做什么的?你可以将十六进制值转换为十进制。 - Gumbo
我现在面临一个困境。由于存在其他不应被考虑的记录,因此我不能使用自动递增 ID。这使得我只能使用一种类似于“secondaryId”的方法。对于这个问题,最佳的解决方案是什么? - jhnferraris
显示剩余4条评论
8个回答

4

没有任何东西能保证100%的独特性。

你需要知道独特性与什么进行比较。

并使用任何算法加上检查所有已使用值列表中的每个值。

在编程世界中,你需要的是所谓的“伪随机”数字。因此它的名称实际上解释了我的意思。


我建议您在句子中将"recently used"更改为"all used",因为"recently used"不能保证唯一性。 - DRAX
1
实际上,自增整数可以保证100%的唯一性。您可能希望强调这一点。 - N.B.
@N.B. 他不打算使用数据库。 - Bogdan Burym
@BogdanBurim - 重点是什么?我还没有说过关于数据库的话。 - N.B.
@N.B. 哦,你的意思不是数据库。那么为什么不尝试自己写出答案呢? - Bogdan Burym
1
我只是在纠正你,因为你声称独特性无法实现或保证,而事实上它是可以实现的。 - N.B.

3

数据库系统在创建类似于MySQL的auto_increment这样的数字时使用独占锁,以处理并发和其他许多复杂细节。

您必须以同样的方式来解决问题 - 从PHP进程获取锁,通过某种持久存储查找当前值,将其增加1,返回并释放锁。

最简单的方法是使用一个老式的文件和独占锁。

我将用一个类进行说明(应该进行调试,因为它不完整):

class MyAutoIncrement
{
    protected $fh = null;
    protected $file_path = '';
    protected $auto_increment_offset = 1;

    public function __construct($file_path, $offset = 1)
    {
        $this->file_path = $file_path;
        $this->auto_increment_offset = $offset;
    }

    public function autoincrement()
    {
        if($this->acquire())
        {
            $current = (int)fread($this->fh);

            $next += $this->auto_increment_offset;

            fwrite($this->fh, $next);

            $this->release();

            return $next;
        }

        return null;
    }

    public function acquire()
    {       
        $handler = $this->getFileHandler();

        return flock($handler, LOCK_EX);
    }

    public function release($close = false)
    {
        $handler = $this->getFileHandler();

        return flock($handler, LOCK_UN);

        if($close) 
        {
            fclose($handler);
            $this->fh = null;
        }
    }   

    protected function acquireLock($handler)
    {
        return flock($handler, LOCK_EX);
    }

    protected function getFileHandler()
    {
        if(is_null($this->fh))
        {
            $this->fh = fopen($this->file_path, 'c+');

            if($this->fh === false)
            {
                throw new \Exception(sprintf("Unable to open the specified file: %s", $this->file_path));
            }
        }

        return $this->fh;
    }
}

使用方法:

$ai = new MyAutoIncrement('/path/to/counter/file.txt');

try
{
    $id = $ai->autoincrement();

    if(!is_null($id))
    {
        // Voila, you got your number, do stuff
    }
    else
    {
        // We went wrong somewhere
    }
}
catch(\Exception $e)
{
// Something went wrong
}

2

1
你可以使用time()getmypid()的组合来获取所需的数字唯一标识符。您可以在给定时间启动多个php进程,但它们在该给定时间内永远不会具有相同的进程ID(除非服务器进程计数器在少于一秒钟的情况下重叠,这在设置正确的kernel.pid_max时几乎不可能)。
<?
function getUniqueID() {
  return time() . '.' . getmypid();
}
?>

该函数将为每个脚本执行每个服务器生成一个唯一的ID。如果您在同一脚本中多次调用它并期望它每次返回唯一值,则会失败。在这种情况下,您可以在函数体内定义一些静态变量来跟踪它。

0
function getserial()
{
 $fn='/where_you_want/serial.dat';
 $fp = fopen($fn, "r+"); 
 if (flock($fp, LOCK_EX)) { $serial=fgets($fp);$serial++; } 
 else 
 { print('lock error, ABORT'); exit; }
 $h=fopen($fn.'.tmp','w'); fwrite($h,$serial);fclose($h);
 if (filesize($fn.'.tmp')>0) 
 { 
  system('rm -f '.$fn.'.tmp'); 
  fseek ($fp, 0); 
  fwrite($fp,$serial); 
 }
 flock($fp, LOCK_UN); fclose($fp); @chmod($fn,0777);
 return $serial;
}

这个示例将为您获取一个唯一的序列号,之后,您可以确定它只存在一个实例。请注意,在避免数据损坏的情况下,您必须首先创建文件并首先放置一个数字。(例如,写入1而不输入或其他任何内容)

这是一个非常简单的功能,但我现在已经使用它超过10年了...


请注意,您必须以独占方式锁定文件,否则您并行运行的脚本将会搞乱一切!另外,我之所以放置了.tmp写入测试,是为了避免在磁盘空间不足的情况下出现数据丢失... - Gipsz Jakab

0

你刚才提到了时间,那microtime呢?

即使你连续创建两个数字,你也会得到不同的值。当然,你需要做一些微调来使其成为一个唯一的整数,但这应该能够帮助你。


0
我建议将PHP进程ID与microtime(true)连接起来,以增加具有唯一值的可能性。

-1

你可以自己设定递增值来确保100%的唯一性,而无需使用复杂的算法:

会话唯一标识:

session_start();

$_SESSION['increment'] = 5;//i fix 5 but you need to get it in bdd,xml,...

function get_new_id(){
    $_SESSION['increment']++;
    //store new value of increment
    return $_SESSION['increment'];
}

$my_unique_id = get_new_id();
echo $my_unique_id;

全局唯一标识(不要使用这个!):
function get_new_id(){
    $increment = file_get_contents('increment.txt');
    $increment++;
    file_put_contents('increment.txt', $increment);
    return $increment;
}

$my_unique_id = get_new_id();
echo $my_unique_id;

仅适用于一个会话。 - itsazzad
那么你不使用数据库,我可以把这个ID存储在哪里?文件里吗? - Benjamin Poignant
但这并不是唯一的,如果你想要100%保证唯一性,你需要使用增量值。类似这样(没有BDD的例子,我不建议使用):function get_new_id(){ $increment = file_get_contents('increment.txt'); $increment++; file_put_contents('increment.txt', $increment); return $increment; } $my_unique_id = get_new_id(); echo $my_unique_id; - Benjamin Poignant

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