Most of the projects I have developed never ran into million-level concurrency, but I have always been torn about generating ids. I usually generate a unique id from year-month-day + a random number; generally I generate a batch of spare random data without duplicates, take one value at a time, and the next day start again from the first one. It does solve the problem, but it always felt a bit cumbersome. After carefully digging into the material and studying it, here it mentions uuid, Vesta, Twitter-Snowflake, and so on. From the article, uuid already has dedicated extensions or approaches in php for generating unique strings, but the processing efficiency under large concurrent data is still not good enough. Several of the other methods use a database…… As for the snowflake algorithm, I looked into the material and found it is still worth trying out.
Basic description of the snowflake algorithm
1. The highest bit is the sign bit, always 0, unused. 2. A 41-bit time sequence, accurate to the millisecond; a 41-bit length can be used for 69 years. The time bits also have a very important role: you can sort by time. 3. A 10-bit machine identifier; a 10-bit length supports deploying up to 1024 nodes. 4. A 12-bit count sequence number; the sequence number is a series of auto-increment ids and can support generating multiple ID sequence numbers from the same node in the same millisecond; a 12-bit count sequence number supports generating 4096 ID sequence numbers per node per millisecond.
Based on the description above and with reference to 雪花算法及运用PHP, 基于php的雪花算法 and other material, my local test The code is as follows:
class snowflake { const EPOCH_OFFSET = 0; // offset timestamp const SIGN = 1; // sign bit, always 0, unused const TIMESTAMP = 41; // timestamp bits default 41 bits, usable for 69 years const DATA_CENTER = 5; // IDC id bits up to 32 nodes const MACHINE_ID = 5; // machine id bits up to 32 nodes const SEQUENCE = 12; // count sequence bits, i.e. a series of auto-increment ids; each node produces 4096 ID sequences per millisecond
protected $data_center_id; // data center id
protected $unix_id; // machine id
protected $last_time = null; // timestamp used when the id was last generated
protected $serial = 1; protected $sign_left_shift = self::TIMESTAMP + self::DATA_CENTER + self::MACHINE_ID + self::SEQUENCE; // sign left shift protected $time_left_shift = self::DATA_CENTER + self::MACHINE_ID + self::SEQUENCE; // timestamp left shift protected $data_center_left_shift = self::MACHINE_ID + self::SEQUENCE; // idc left shift protected $unix_left_shift = self::SEQUENCE; // machine id left shift bits protected $max_serial = -1 ^ (-1 << self::SEQUENCE); // max sequence number protected $max_unix = -1 ^ (-1 << self::MACHINE_ID); // max machine id protected $max_data_center = -1 ^ (-1 << self::DATA_CENTER); // max data center id
public function __construct($data_center_id, $unix_id) { if ($data_center_id > $this->max_data_center) { throw new Exception('数据中心编号取值错误,取值范围为:0-' . $this->max_data_center); } if ($unix_id > $this->max_unix) { throw new Exception('机器编号取值错误,取值范围为:0-' . $this->max_unix); } $this->data_center_id = $data_center_id; $this->unix_id = $unix_id; }