【问题标题】:PHP: Best way to Generate an unique numeric IDPHP:生成唯一数字 ID 的最佳方法
【发布时间】:2014-11-18 23:24:02
【问题描述】:

我需要生成唯一的数字 id。

  • 我可以使用uniqid,但它会生成字母数字值。

  • 我可以再次使用time,但不能保证它 永远是独一无二的。

  • 我可以再次使用字段的自动增量属性 数据库来获取唯一的 id,但是这里我们必须需要一个数据库。

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

【问题讨论】:

  • 也就是说,如果您需要放入数据库中...将唯一 ID 留在数据库中
  • @MarcinOrlowski 我不需要随机但我需要独特。
  • 您可以使用 microtime(true) 并将小数部分附加到整数部分,这将在大多数情况下为您提供唯一的,实际上
  • 那么为什么不只使用序号呢?对于您的范围而言,这将是非常独特的吗?即使是 DB 的 auto_increment 也可以
  • 了解what uniqid actually does怎么样?您始终可以将十六进制值转换为十进制。

标签: php uniqueidentifier server


【解决方案1】:

没有什么可以保证 100% 的唯一性。

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

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

在编程的世界里,你需要的是pseudo random 号码。所以它的名字实际上解释了我的意思。

【讨论】:

  • 我只是将您的句子中的“最近使用”改为“全部使用”,因为最近使用不会保证唯一性
  • 其实自增整数可以保证100%的唯一性。您可能想强调这一点。
  • @N.B.他不打算使用 DB
  • @BogdanBurim - 点存在?关于数据库,我只字未提。
  • 我有.. 我只是在纠正你,因为你声称无法实现或保证唯一性,而它可以。
【解决方案2】:

数据库系统在创建数字时使用排他锁,例如 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
}

【讨论】:

    【解决方案3】:

    如前所述。没有什么可以保证 100% 的唯一性。

    虽然这将是相当独特的:)

    $iUniqueNumber = crc32(uniqid());
    

    uniqid crc32 polynomial of a string

    【讨论】:

      【解决方案4】:

      您可以使用 time()getmypid() 的组合来获得所需的 - 一个数字唯一 ID。您可以在给定时间启动多个 php 进程,但在给定时间它们永远不会具有相同的进程 ID(除非服务器进程计数​​器在不到一秒的时间内重叠,这在 kernel.pid_max 设置正确时几乎是不可能的) .

      <?
      function getUniqueID() {
        return time() . '.' . getmypid();
      }
      ?>
      

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

      【讨论】:

        【解决方案5】:

        您可以在不使用繁重的算法的情况下,制作自己的增量值来保证 100% 的唯一性:

        会话唯一 ID:

        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;
        

        全局唯一 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;
        

        【讨论】:

        • 它仅适用于会话。
        • 所以你不使用数据库,我可以在哪里存储这个ID?在文件中?
        • 但它不是唯一的,如果你想 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;
        【解决方案6】:

        你说的是时间,那microtime呢?

        即使您连续创建两个数字,您也会得到不同的值。当然,您需要尝试一下以使其成为唯一整数,但它应该能够提供帮助。

        【讨论】:

          【解决方案7】:

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

          【讨论】:

            【解决方案8】:
            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 写入测试,以避免在没有磁盘空间的情况下丢失数据...
            猜你喜欢
            • 2011-01-25
            • 2017-08-26
            • 1970-01-01
            • 2021-02-13
            • 1970-01-01
            • 1970-01-01
            • 2013-12-05
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多