snowflake算法好像是订单号的妈妈

生成订单号

在下订单时,我们需要为订单生成一个 唯一 的订单号,为了能够生成一个在高并发、集群环境下依然唯一的订单号,我们可以使用 twitter 提供的 snowflake 算法。

snowflake
snowflake 算法生成的订单号是由 64位(8个字节)二进制数来保存的,它的每一位的含义如下:

PHP 版本的 snowflake

由于 PHP 语言的特点,PHP中的数据用完就会销毁,下次再执行时又是全新的数据,这就导致在 PHP 中无效保存 上一次的数据 以及 持久存在的数字序列,为了解决这两个问题,我们可以使用 redis 来保存需要持久存在的数据。Snowflake 中有两个数据需要保存到 Redis :上次生成的时间戳 和 序列ID。
所以修改之后的PHP版本的 Snowflake 为(以laravel框架实例):

1、安装 redis 扩展

1
composer require predis/predis

2、在自定义php文件里添加 snowflake 类laravel自定义全局函数看这里
这里在 app/helpers.php 文件中添加

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
107
108
109
110
111
112
113
114
115
116
117
118
<?php
use Illuminate\Support\Facades\Redis;

...

// 生成唯一订单编号
function getOrderSn()
{
$sf = new SnowFlake(0, 0);
return $sf->generateID();
}

class SnowFlake
{
/**
* Offset from Unix Epoch
* Unix Epoch : January 1 1970 00:00:00 GMT
* Epoch Offset : January 1 2000 00:00:00 GMT
*/
const EPOCH_OFFSET = 1483200000000;
const SIGN_BITS = 1;
const TIMESTAMP_BITS = 41;
const DATACENTER_BITS = 5;
const MACHINE_ID_BITS = 5;
const SEQUENCE_BITS = 12;

/**
* @var mixed
*/
protected $datacenter_id;

/**
* @var mixed
*/
protected $machine_id;

/**
* @var null|int
*/
// protected $lastTimestamp = null; 需要保存到 Redis

/**
* @var int
*/
// protected $sequence = 1; 需要保存到 Redis

protected $signLeftShift = self::TIMESTAMP_BITS + self::DATACENTER_BITS + self::MACHINE_ID_BITS + self::SEQUENCE_BITS;
protected $timestampLeftShift = self::DATACENTER_BITS + self::MACHINE_ID_BITS + self::SEQUENCE_BITS;
protected $dataCenterLeftShift = self::MACHINE_ID_BITS + self::SEQUENCE_BITS;
protected $machineLeftShift = self::SEQUENCE_BITS;
protected $maxSequenceId = -1 ^ (-1 << self::SEQUENCE_BITS);
protected $maxMachineId = -1 ^ (-1 << self::MACHINE_ID_BITS);
protected $maxDataCenterId = -1 ^ (-1 << self::DATACENTER_BITS);

/**
* Constructor to set required paremeters
*
* @param mixed $dataCenter_id Unique ID for datacenter (if multiple locations are used)
* @param mixed $machine_id Unique ID for machine (if multiple machines are used)
* @throws \Exception
*/
public function __construct($dataCenter_id, $machine_id)
{
if ($dataCenter_id > $this->maxDataCenterId) {
throw new \Exception('dataCenter id should between 0 and ' . $this->maxDataCenterId);
}
if ($machine_id > $this->maxMachineId) {
throw new \Exception('machine id should between 0 and ' . $this->maxMachineId);
}
$this->datacenter_id = $dataCenter_id;
$this->machine_id = $machine_id;
}

/**
* Generate an unique ID based on SnowFlake
* @return string
* @throws \Exception
*/
public function generateID()
{
$sign = 0; // default 0
$timestamp = $this->getUnixTimestamp();

$lastTimeStamp = Redis::get('shop:order:lastTimeStamp');

if ($timestamp < $lastTimeStamp) {
throw new \Exception('"Clock moved backwards!');
}
if ($timestamp == $lastTimeStamp) { //与上次时间戳相等,需要生成序列号
$sequence = Redis::incr('shop:order:sequence');
if ($sequence == $this->maxSequenceId) { //如果序列号超限,则需要重新获取时间
$timestamp = $this->getUnixTimestamp();
while ($timestamp <= $lastTimeStamp) {
$timestamp = $this->getUnixTimestamp();
}
Redis::set('shop:order:sequence', 1);
$sequence = 1;
}
} else {
Redis::set('shop:order:sequence', 1);
$sequence = 1;
}
Redis::set('shop:order:lastTimeStamp', $timestamp);
$time = (int)($timestamp - self::EPOCH_OFFSET);
$id = ($sign << $this->signLeftShift) | ($time << $this->timestampLeftShift) | ($this->datacenter_id << $this->dataCenterLeftShift) | ($this->machine_id << $this->machineLeftShift) | $sequence;
return (string)$id;
}

/**
* Get UNIX timestamp in microseconds
*
* @return int Timestamp in microseconds
*/
private function getUnixTimestamp()
{
return floor(microtime(true) * 1000);
}
}

添加之后就可以在项目中使用 getOrderSn 函数来生成订单号了:

1
2
3
    Route::get('testSN', function(){
return getOrderSn();
});

参考自我的课程讲义


 评论


本站使用 Material X 作为主题 , 总访问量为 次 。
隐藏