>  기사  >  백엔드 개발  >  PHP는 분산 고유 ID를 생성하기 위해 Snowflake를 구현합니다.

PHP는 분산 고유 ID를 생성하기 위해 Snowflake를 구현합니다.

步履不停
步履不停원래의
2019-07-01 15:15:514015검색

PHP는 분산 고유 ID를 생성하기 위해 Snowflake를 구현합니다.

트위터의 눈송이는 고유한 UUID의 분산 생성에 널리 사용됩니다. 또한 인터넷에는 눈송이의 일부 변형을 기반으로 하는 많은 알고리즘이 있습니다. 눈송이를 사용하여 생성된 많은 UUID는 분산 시나리오에서 사용됩니다. 인터넷에서 스레드 안전성을 고려하지 않는 여러 PHP 구현을 읽었습니다. 이제 PHP는 Swoole의 잠금 및 코루틴을 지원하므로 스레드 안전 및 높은 동시성 시뮬레이션을 개발하는 것이 매우 편리합니다. 여기서는 Swoole과 결합된 PHP를 사용하여 가장 간단한 눈송이를 구현하는 방법을 배웁니다. 오랫동안 PHP를 사용했는데 IDE가 없는 것 같습니다. 더 이상 PHP를 작성할 수 없습니다.

먼저 다음 눈송이 구조를 살펴보세요.
PHP는 분산 고유 ID를 생성하기 위해 Snowflake를 구현합니다.생성된 값은 64비트이며 4개 부분으로 나뉩니다.

  • 첫 번째 비트는 부호 비트이고 가장 높은 비트는 0으로 양수를 나타냅니다.
  • 두 번째 부분은 41비트 ID가 생성될 때 타임스탬프를 밀리초 단위로 기록하는 데 사용되므로 이 부분이 나타내는 값 범위는 2^41 - 1(69년)이며 이는 특정 시간을 기준으로 한 오프셋입니다
  • 세 번째 10의 일부 1비트는 작업 노드의 ID를 나타내며 값 범위가 2^10 - 1임을 나타내며 이는 1024개의 노드를 지원하는 것과 동일합니다
  • 네 번째 부분 12비트는 각 작업에서 생성된 순환 자체 증가 ID를 나타냅니다. 0을 초과하고 다음 밀리초가 다시 증가할 때까지 기다리는 경우 최대 2^12 -1 ID를 생성할 수 있습니다.

코드를 먼저 게시하세요.

<?php class Snowflake
{
    const EPOCH = 1543223810238;    // 起始时间戳,毫秒

    const SEQUENCE_BITS = 12;   //序号部分12位
    const SEQUENCE_MAX = -1 ^ (-1 << self::SEQUENCE_BITS);  // 序号最大值

    const WORKER_BITS = 10; // 节点部分10位
    const WORKER_MAX = -1 ^ (-1 << self::WORKER_BITS);  // 节点最大数值

    const TIME_SHIFT = self::WORKER_BITS + self::SEQUENCE_BITS; // 时间戳部分左偏移量
    const WORKER_SHIFT = self::SEQUENCE_BITS;   // 节点部分左偏移量

    protected $timestamp;   // 上次ID生成时间戳
    protected $workerId;    // 节点ID
    protected $sequence;    // 序号
    protected $lock;        // Swoole 互斥锁

    public function __construct($workerId)
    {
        if ($workerId < 0 || $workerId > self::WORKER_MAX) {
            trigger_error("Worker ID 超出范围");
            exit(0);
        }

        $this->timestamp = 0;
        $this->workerId = $workerId;
        $this->sequence = 0;
        $this->lock = new swoole_lock(SWOOLE_MUTEX);
    }

    /**
     * 生成ID
     * @return int
     */
    public function getId()
    {
        $this->lock->lock();    // 这里一定要记得加锁
        $now = $this->now();
        if ($this->timestamp == $now) {
            $this->sequence++;

            if ($this->sequence > self::SEQUENCE_MAX) {
                // 当前毫秒内生成的序号已经超出最大范围,等待下一毫秒重新生成
                while ($now timestamp) {
                    $now = $this->now();
                }
            }
        } else {
            $this->sequence = 0;
        }

        $this->timestamp = $now;    // 更新ID生时间戳

        $id = (($now - self::EPOCH) workerId sequence;
        $this->lock->unlock();  //解锁

        return $id;
    }

    /**
     * 获取当前毫秒
     * @return string
     */
    public function now()
    {
        return sprintf("%.0f", microtime(true) * 1000);
    }

}

사실 논리는 복잡하지 않습니다. 코드에서 비트 연산을 설명하세요:

-1 ^ (-1 <p>마지막 부분이 왼쪽으로 이동한 후 OR 연산: </p><pre class="brush:php;toolbar:false">(($now - self::EPOCH) workerId sequence;

여기에서는 주로 첫 번째 부호 비트를 제외한 세 부분을 해당 오프셋만큼 왼쪽으로 이동하여 반환합니다. 예를 들어 3명을 사용하여 병합 작업을 시연해 보겠습니다.

0000 0000 0010  --左移0位--> 0000 0000 0010
0000 0000 0100  --左移4位--> 0000 0100 0000 --或操作-->1000 0100 0010
0000 0000 1000  --左移8位--> 1000 0000 0000

Swoole의 코루틴과 채널을 사용하여 강력하게 테스트해 보겠습니다. 생성된 ID는 반복됩니다.

$snowflake = new Snowflake(1);

$chan = new chan(100000);
$n = 100000;

for ($i = 0; $i getId();
        $chan->push($id);
    });
}

go(function () use ($chan, $n) {
    $arr = [];
    for ($i = 0; $i pop();  // PHP Swoole的channel一定要写在go(func)的协程里面!?
        if (in_array($id, $arr)) {
            exit("ID 已存在");
        }
        array_push($arr, $id);
    }
});

$chan->close();

echo "ok";

실행한 후에는 실제로 ID가 중복되지 않습니다. 그런데 Golang을 사용하여 눈송이도 구현하고 공동 프로그램 모드에서 동일한 테스트를 실행했습니다. 약 12초, Golang은 1초밖에 걸리지 않습니다. 기사 내용에 오류가 있으면 정정해 주시면 감사하겠습니다.

위 내용은 PHP는 분산 고유 ID를 생성하기 위해 Snowflake를 구현합니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.