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

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

藏色散人
藏色散人앞으로
2019-08-21 14:36:122647검색

Twitter의 눈송이는 고유한 UUID의 분산 생성에 널리 사용됩니다. 인터넷에는 눈송이의 일부 변형을 기반으로 하는 알고리즘도 많이 있습니다. Snowflake를 사용하여 생성된 많은 UUID는 분산 시나리오에서 사용됩니다. 스레드 안전성을 고려하지 않고 PHP를 구현하는 인터넷 기사를 여러 개 읽었습니다.

이제 PHP는 Swoole의 잠금 및 코루틴을 지원하므로 스레드로부터 안전하고 동시성이 높은 시뮬레이션을 개발하는 것이 매우 편리합니다. 여기서는 Swoole과 결합된 PHP를 사용하여 가장 간단한 눈송이를 구현하는 방법을 배웁니다. 오랫동안 PHP를 작성하지 않았고 거기에 없는 것 같은 느낌이 듭니다.) IDE는 실제로 PHP를 작성할 수 없습니다.

먼저 다음 눈송이 구조를 살펴보겠습니다.

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

생성된 값은 64비트이며 4개 부분으로 나뉩니다.

● 첫 번째 비트는 부호 비트이고 가장 높은 비트는 0으로 양수를 나타냅니다.

● 두 번째 부분의 41비트는 ID가 생성될 때 타임스탬프를 밀리초 단위로 기록하는 데 사용되므로 이 부분이 나타내는 값 범위는 2^41 - 1(69년)이며, 이는 에 대한 오프셋입니다. 특정 시간

● 세 번째 부분의 10비트는 작업 노드의 ID를 나타내며 값 범위는 2^10 - 1이며 이는 1024개 노드를 지원하는 것과 같습니다

● 네 번째 부분의 12비트 각 작업 노드에서 생성된 순환 증분을 밀리초 ID로 나타냅니다. 최대 2^12 -1 ID가 생성될 수 있습니다. 0을 초과하면 다음 밀리초가 다시 증가할 때까지 기다립니다.

<?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 <= $this->timestamp) {
                    $now = $this->now();
                }
            }
        } else {
            $this->sequence = 0;
        }
        $this->timestamp = $now;    // 更新ID生时间戳
        $id = (($now - self::EPOCH) << self::TIME_SHIFT) | ($this->workerId << self::WORKER_SHIFT) | $this->sequence;
        $this->lock->unlock();  //解锁
        return $id;
    }
    /**
     * 获取当前毫秒
     * @return string
     */
    public function now()
    {
        return sprintf("%.0f", microtime(true) * 1000);
    }
}

In 사실, 논리는 복잡하지 않습니다. 코드의 비트 연산을 설명해 주세요.

-1 ^ (-1 << self::SEQUENCE_BITS)

1의 보수로 -1을 이진수로 표현한 것입니다. 이는 실제로 다음과 같습니다.

2**self::SEQUENCE_BITS - 1

마지막 부분은 왼쪽으로 이동하고

(($now - self::EPOCH) << self::TIME_SHIFT) | ($this->workerId << self::WORKER_SHIFT) | $this->sequence;

여기에는 주로 첫 번째 부호 비트를 제외한 세 부분의 왼쪽 시프트가 있습니다. 오프셋은 이를 재설정하는 데 사용되며 OR 연산을 통해 위의 눈송이 구조에 다시 통합됩니다. 3개 부분과 4비트를 사용하여 병합 작업을 보여줍니다.

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 < $n; $i++) {
    go(function () use ($snowflake, $chan) {
        $id = $snowflake->getId();
        $chan->push($id);
    });
}
go(function () use ($chan, $n) {
    $arr = [];
    for ($i = 0; $i < $n; $i++) {
        $id = $chan->pop();  // PHP Swoole的channel一定要写在go(func)的协程里面!?
        if (in_array($id, $arr)) {
            exit("ID 已存在");
        }
        array_push($arr, $id);
    }
});
$chan->close();
echo "ok";

실행하면 실제로 발생합니다. 중복된 ID는 없어야 합니다. 그런데 Golang에서도 눈송이를 구현하고 공동 프로그램 모드에서 동일한 테스트를 실행했으며 PHP를 실행했습니다. 시간은 약 12초이고 Golang에서는 1초만 걸립니다. 기사 내용에 오류가 있으면 정정해 주시면 감사하겠습니다.

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

성명:
이 기사는 learnku.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제