罗宁的博客

使用redis生成唯一ID

每10秒生成50万的ID 项目统一调用的ID方法

1.bigint 最大值 9223372036854775807
2.时间戳 取redis time 去最后一位 134856896
3.86400 /10 8640
4.32位最大值 4294967295
5.4294967295/8640 = 497102
6.redis hash key date(Y-m-d) 过期时间三天 field key 时间戳 去最后一位 134856896
7.hincreby field key 1 原子性
8.134856896 + skip32 (1000000001)= 134856896 + 3164636857 = 1348568963164636857

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$nowTimeTen = substr(time(), 0, 9);
$redis = Yii::$app->redis;
$redisKey = AppRedisKeyMap::getPrimaryKey();
if (!$redis->exists($redisKey)) {
$startTime = intval(substr(strtotime(date("Y-m-d 00:00:00")), 0, 9));
$hmsetData = [$redisKey];
for ($i = 0; $i < 8640; $i++) {
$hmsetData[] = $startTime + $i;
$hmsetData[] = $i * 497102;
}
if (!$redis->executeCommand("hmset", $hmsetData)) {
$redis->executeCommand("hmset", $hmsetData);
}
if (!$redis->expire($redisKey, 86400 * 3)) {
$redis->expire($redisKey, 86400 * 3);
}
}
$plusNumber = $redis->HINCRBY($redisKey, $nowTimeTen, 1);
$skip32Key = $key = pack('H20', static::SKIP_KEY); // 10 bytes key
$cipher = new Skip32Cipher($skip32Key);
$bin = pack('N', $plusNumber);
$encrypted = $cipher->encrypt($bin);
list(, $encryptedInt) = unpack('N', $encrypted);
return $nowTimeTen . str_pad($encryptedInt, 10, "0", STR_PAD_LEFT);

Skip32算法代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
class Skip32Cipher
{
/**
* @const Number of bytes in the key
*/
const KEY_SIZE = 10;
/**
* @const Number of bytes in the data.
*/
const BLOCK_SIZE = 4;
private $_ftable = array(
0xa3,0xd7,0x09,0x83,0xf8,0x48,0xf6,0xf4,0xb3,0x21,0x15,0x78,0x99,0xb1,0xaf,0xf9,
0xe7,0x2d,0x4d,0x8a,0xce,0x4c,0xca,0x2e,0x52,0x95,0xd9,0x1e,0x4e,0x38,0x44,0x28,
0x0a,0xdf,0x02,0xa0,0x17,0xf1,0x60,0x68,0x12,0xb7,0x7a,0xc3,0xe9,0xfa,0x3d,0x53,
0x96,0x84,0x6b,0xba,0xf2,0x63,0x9a,0x19,0x7c,0xae,0xe5,0xf5,0xf7,0x16,0x6a,0xa2,
0x39,0xb6,0x7b,0x0f,0xc1,0x93,0x81,0x1b,0xee,0xb4,0x1a,0xea,0xd0,0x91,0x2f,0xb8,
0x55,0xb9,0xda,0x85,0x3f,0x41,0xbf,0xe0,0x5a,0x58,0x80,0x5f,0x66,0x0b,0xd8,0x90,
0x35,0xd5,0xc0,0xa7,0x33,0x06,0x65,0x69,0x45,0x00,0x94,0x56,0x6d,0x98,0x9b,0x76,
0x97,0xfc,0xb2,0xc2,0xb0,0xfe,0xdb,0x20,0xe1,0xeb,0xd6,0xe4,0xdd,0x47,0x4a,0x1d,
0x42,0xed,0x9e,0x6e,0x49,0x3c,0xcd,0x43,0x27,0xd2,0x07,0xd4,0xde,0xc7,0x67,0x18,
0x89,0xcb,0x30,0x1f,0x8d,0xc6,0x8f,0xaa,0xc8,0x74,0xdc,0xc9,0x5d,0x5c,0x31,0xa4,
0x70,0x88,0x61,0x2c,0x9f,0x0d,0x2b,0x87,0x50,0x82,0x54,0x64,0x26,0x7d,0x03,0x40,
0x34,0x4b,0x1c,0x73,0xd1,0xc4,0xfd,0x3b,0xcc,0xfb,0x7f,0xab,0xe6,0x3e,0x5b,0xa5,
0xad,0x04,0x23,0x9c,0x14,0x51,0x22,0xf0,0x29,0x79,0x71,0x7e,0xff,0x8c,0x0e,0xe2,
0x0c,0xef,0xbc,0x72,0x75,0x6f,0x37,0xa1,0xec,0xd3,0x8e,0x62,0x8b,0x86,0x10,0xe8,
0x08,0x77,0x11,0xbe,0x92,0x4f,0x24,0xc5,0x32,0x36,0x9d,0xcf,0xf3,0xa6,0xbb,0xac,
0x5e,0x6c,0xa9,0x13,0x57,0x25,0xb5,0xe3,0xbd,0xa8,0x3a,0x01,0x05,0x59,0x2a,0x46
);
private $_key;
/**
* Cipher constructor
*
* @param string $key 10 bytes key
*/
public function __construct($key)
{
$key = unpack("C*", $key);
if (count($key) != self::KEY_SIZE) {
throw new \Exception(sprintf("Key must be %d bytes long", self::KEY_SIZE));
}
$this->_key = array_values($key);
}
/**
* Encrypt 32-bit binary data
*
* @param string $data 4 bytes block
*
* @return string 4 bytes block
*/
public function encrypt($data)
{
return $this->_skip32($data, true);
}
/**
* Decrypt 32-bit binary data
*
* @param string $data 4 bytes block
*
* @return string 4 bytes block
*/
public function decrypt($data)
{
return $this->_skip32($data, false);
}
private function _g($k, $w)
{
$g1 = ($w>>8) & 0xff;
$g2 = $w & 0xff;
$g3 = $this->_ftable[$g2 ^ $this->_key[(4 * $k) % 10]] ^ $g1;
$g4 = $this->_ftable[$g3 ^ $this->_key[(4 * $k + 1) % 10]] ^ $g2;
$g5 = $this->_ftable[$g4 ^ $this->_key[(4 * $k + 2) % 10]] ^ $g3;
$g6 = $this->_ftable[$g5 ^ $this->_key[(4 * $k + 3) % 10]] ^ $g4;
return (($g5 << 8) + $g6);
}
private function _skip32($buf, $encrypt)
{
$buf = unpack("C*", $buf);
if (count($buf) != self::BLOCK_SIZE) {
throw new \Exception(sprintf("Data must be %d bytes long", self::BLOCK_SIZE));
}
// sort out direction
if ($encrypt) {
$kstep = 1;
$k = 0;
} else {
$kstep = -1;
$k = 23;
}
// pack into words
$wl = ($buf[1] << 8) + $buf[2];
$wr = ($buf[3] << 8) + $buf[4];
// 24 feistel rounds, doubled up
for ($i = 0; $i < 24/2; ++$i) {
$wr ^= self::_g($k, $wl) ^ $k;
$k += $kstep;
$wl ^= self::_g($k, $wr) ^ $k;
$k += $kstep;
}
// implicitly swap halves while unpacking
$buf[1] = $wr >> 8;
$buf[2] = $wr & 0xFF;
$buf[3] = $wl >> 8;
$buf[4] = $wl & 0xFF;
return pack("C*", $buf[1], $buf[2], $buf[3], $buf[4]);
}
}