The SnowFlake algorithm and phpsnowflake Algorithm for generating unique IDs in PHP
Recently, a CMS system needs to be developed. Due to its single function and flexible requirements, the mature system like WP is abandoned and its own one is relatively simple. The URL of the article is designed to be a URL pseudo-static format: xxx.html. xxx has considered using an auto-incrementing primary key directly. However, it seems that the number of articles is somewhat exposed. Some people may say that the initial value can be higher, however, the number of articles in a period of time can still be calculated by ID difference, so an algorithm that can generate a unique ID is required.
The methods that have been considered include:
- Use timestamp directly, or a series of methods derived from this
- Mysql uuid
The above two methods can be found and will not be explained.
Finally, I chose Twitter's SnowFlake algorithm.
The benefit of this algorithm is that it can generate about different 16-bit numeric IDs per second (10 hexadecimal)
The principle is simple.
IDS consist of 64bit
The first bit is vacant.
41bit is used to store timestamp in milliseconds
10bit is used to store the machine id
12bit is used to store auto-increment IDS
In addition to the highest bit marked as unavailable, the other three groups of bit placeholder values can be floating, depending on the specific business needs. By default, the 41bit Timestamp can be used by this algorithm until January 1, 2082. the id of a 10-bit worker machine can support 1023 machines, and the serial number can generate 4095 auto-incrementing sequence IDs in 1 ms.
Below is the PHP source code
<?phpnamespace App\Services;abstract class Particle { const EPOCH = 1479533469598; const max12bit = 4095; const max41bit = 1099511627775; static $machineId = null; public static function machineId($mId = 0) { self::$machineId = $mId; } public static function generateParticle() { /* * Time - 42 bits */ $time = floor(microtime(true) * 1000); /* * Substract custom epoch from current time */ $time -= self::EPOCH; /* * Create a base and add time to it */ $base = decbin(self::max41bit + $time); /* * Configured machine id - 10 bits - up to 1024 machines */ if(!self::$machineId) { $machineid = self::$machineId; } else { $machineid = str_pad(decbin(self::$machineId), 10, "0", STR_PAD_LEFT); } /* * sequence number - 12 bits - up to 4096 random numbers per machine */ $random = str_pad(decbin(mt_rand(0, self::max12bit)), 12, "0", STR_PAD_LEFT); /* * Pack */ $base = $base.$machineid.$random; /* * Return unique time id no */ return bindec($base); } public static function timeFromParticle($particle) { /* * Return time */ return bindec(substr(decbin($particle),0,41)) - self::max41bit + self::EPOCH; }}?>
The call method is as follows:
Particle: generateParticle ($ machineId); // generates IDParticle: timeFromParticle ($ particle); // returns the time stamp of Inverse Computation.
Here I made improvements. If the machine ID is 0, the 10bit will be removed, because sometimes we may not use so many IDs.