您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“怎么正確設(shè)計(jì)一個(gè)訂單號(hào)”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
我們經(jīng)常提及到的訂單號(hào),大多數(shù)是在電商購(gòu)物場(chǎng)景下的一個(gè)唯一標(biāo)識(shí)字符串。實(shí)則訂單號(hào)并不僅僅指的是電商系統(tǒng),只要需要這樣的業(yè)務(wù)場(chǎng)景,我們都可以使用訂單號(hào)的模式來(lái)處理。例如我們的省份證號(hào),要求唯一可讀性強(qiáng)等特點(diǎn),也可以將之理解為一個(gè)訂單號(hào)。
1.不重復(fù)。不管你的訂單號(hào)設(shè)計(jì)的是多復(fù)雜還是多簡(jiǎn)單,首先我們需要確保的是訂單號(hào)在一個(gè)系統(tǒng)中是唯一的。
2.安全性。訂單號(hào)需要做到不容易被人為的猜測(cè)或者推測(cè)出來(lái)。例如訂單號(hào)包含系統(tǒng)的流水信息,用戶信息等保密相關(guān)的信息。
3.禁用隨機(jī)碼。隨機(jī)碼從一定程度來(lái)說(shuō),更安全、不重復(fù)性更高,但是可讀性差。例如生成類似這樣的隨機(jī)碼(sdfsad12312sfsdf201),不管是從系統(tǒng)角度還是從人為角度去讀取,完全沒(méi)法直接辨別。
4.防止并發(fā)。針對(duì)系統(tǒng)的并發(fā)業(yè)務(wù)場(chǎng)景(如秒殺),一定需要做到并發(fā)場(chǎng)景下,訂單編號(hào)生成快速、不重復(fù)等要求。
5.控制位數(shù)。訂單號(hào)的位數(shù)盡量在 10 位-20 位之間。太短的情況下,如果交易量過(guò)大,很難做到防止重復(fù),太長(zhǎng)可讀性差、意義也不大。
6.盡量使用數(shù)字。從軟件角度,數(shù)字存儲(chǔ)的訂單號(hào),占用空間小、檢索快。
上面的截圖,是個(gè)人在淘寶上面進(jìn)行充值的訂單編號(hào),這里只截取了幾個(gè)。在實(shí)際的過(guò)程中,發(fā)現(xiàn)所有訂單號(hào)都有一個(gè)相似的特點(diǎn)(紅色框出來(lái)的地方)。個(gè)人猜測(cè),這應(yīng)該是和買家相關(guān)的信息,例如買家的 ID 編號(hào)情況。
下面的的幾個(gè)規(guī)則,是淘寶訂單編號(hào)生成的規(guī)則(只具備參考意義,與實(shí)際存在差距)。
1.總共 18 位。
2.前 14 位為序號(hào)。
3.15-16 位賣家 ID 的倒數(shù) 1-2 位。。
4.17-18 位買家 ID 的倒數(shù) 3-4 位。
上面的幾個(gè)規(guī)則具備一定的參考意義。從第 3 和 4 點(diǎn),我們不難分析出來(lái),通過(guò)這樣的方式來(lái)實(shí)現(xiàn)訂單號(hào),在一定程度很難出現(xiàn)重復(fù)的訂單編號(hào)。那是為什么呢?
1.賣家的 ID 和買家 ID 的都是在下單之前生成的,具備唯一性。因?yàn)檫@兩個(gè) ID 事先生成,即使出現(xiàn)并發(fā)場(chǎng)景,通過(guò)這兩組的唯一標(biāo)識(shí)就很難生成重復(fù)的單號(hào)。
2.很大程度上滿足了一些并發(fā)高的業(yè)務(wù)場(chǎng)景下,單號(hào)重復(fù)的情況。或許你會(huì)考慮像雙十一這樣的場(chǎng)景下,實(shí)則絕大部分系統(tǒng)都無(wú)法達(dá)到這樣的業(yè)務(wù)場(chǎng)景。
前面提到了生成的規(guī)則,那要實(shí)現(xiàn)這樣的規(guī)則,該如何實(shí)現(xiàn)會(huì)比較好呢?下面總結(jié)幾種常見(jiàn)的處理方式。
UUID 是 Universally Unique Indentifier 的縮寫,翻譯為通用唯一識(shí)別碼,顧名思義 UUID 是一個(gè)用于唯一標(biāo)識(shí)一條數(shù)據(jù)、記錄的,其按照開(kāi)放軟件基金會(huì)(OSF)指定的標(biāo)準(zhǔn)進(jìn)行計(jì)算,用到了以太網(wǎng)卡地址(MAC)、納秒級(jí)時(shí)間、芯片 ID 碼和許多可能的數(shù)字。
總的來(lái)說(shuō),UUID 碼由以下三部分組成:
1.當(dāng)前日期和時(shí)間。
2.時(shí)鐘序列。
3.全局唯一的 IEEE 機(jī)器識(shí)別碼(如何有網(wǎng)卡,從網(wǎng)卡獲得,沒(méi)有網(wǎng)卡則以其他方式獲得)。
UUID 的標(biāo)準(zhǔn)形式包含 32 個(gè) 16 進(jìn)制數(shù)字,以連字號(hào)分為五段,形式為 8-4-4-4-12 的 32 個(gè)字符。示例:550e8400-e29b-41d4-a716-446655440000。關(guān)于 UUID 的更多介紹,可以參考該文章
Snowflake 是 Twitter 內(nèi)部的一個(gè) ID 生算法,可以通過(guò)一些簡(jiǎn)單的規(guī)則保證在大規(guī)模分布式情況下生成唯一的 ID 號(hào)碼。其組成為:
第一個(gè) bit 為未使用的符號(hào)位。
第二部分由 41 位的時(shí)間戳(毫秒)構(gòu)成,它的取值是當(dāng)前時(shí)間相對(duì)于某一時(shí)間的偏移量。
第三部分和第四部分的 5 個(gè) bit 位表示數(shù)據(jù)中心和機(jī)器 ID,其能表示的最大值為 2^5 -1 = 31。
最后部分由 12 個(gè) bit 組成,其表示每個(gè)工作節(jié)點(diǎn)每毫秒生成的序列號(hào) ID,同一毫秒內(nèi)最多可生成 2^12 -1 即 4095 個(gè) ID。
需要注意的是:
在分布式環(huán)境中,5 個(gè) bit 位的 datacenter 和 worker 表示最多能部署 31 個(gè)數(shù)據(jù)中心,每個(gè)數(shù)據(jù)中心最多可部署 31 臺(tái)節(jié)點(diǎn)。
41 位的二進(jìn)制長(zhǎng)度最多能表示 2^41 -1 毫秒即 69 年,所以雪花算法最多能正常使用 69 年,為了能最大限度的使用該算法,你應(yīng)該為其指定一個(gè)開(kāi)始時(shí)間。
在數(shù)據(jù)庫(kù)中可以通過(guò)給訂單列設(shè)置為自增列,并且給該列設(shè)置一個(gè)初始值。這樣通過(guò)數(shù)據(jù)庫(kù)實(shí)現(xiàn)訂單的自增、無(wú)重復(fù)情況。但通過(guò)數(shù)據(jù)庫(kù)實(shí)現(xiàn)并發(fā)能力低,單表存在只能有一個(gè)自增列的情況,后期對(duì)數(shù)據(jù)的分表處理也不夠友好。
通過(guò)分布式組件的方式,我們也可以實(shí)現(xiàn)訂單號(hào)的處理。例如使用 Redis 作為分布式組件。通過(guò) Redis 的隊(duì)列、incr 等功能來(lái)實(shí)現(xiàn)。
// 生成方式一
function uuid($prefix = '') {
$chars = md5(uniqid(mt_rand(), true));
$uuid = substr($chars,0,8) . '-';
$uuid .= substr($chars,8,4) . '-';
$uuid .= substr($chars,12,4) . '-';
$uuid .= substr($chars,16,4) . '-';
$uuid .= substr($chars,20,12);
return $prefix . $uuid;
}
echo uuid();
// output
// b2fa188c-23a8-d1b6-432d-649db4eb34c7
// 生成方式二(利用Linux內(nèi)部生成的uuid)
echo exec("cat /proc/sys/kernel/random/uuid");
// output
// b2792783-7c9f-43d0-8d31-38411e17fc2f
// 生成方式三
function uniqidReal($lenght = 13) {
if (function_exists("random_bytes")) {
try {
$bytes = random_bytes(ceil($lenght / 2));
} catch (Exception $e) {
}
} elseif (function_exists("openssl_random_pseudo_bytes")) {
$bytes = openssl_random_pseudo_bytes(ceil($lenght / 2));
} else {
throw new Exception("no cryptographically secure random function available");
}
return substr(bin2hex($bytes), 0, $lenght);
}
echo uniqidReal();
// output
// 9f39aa0ecd89d
require_once __DIR__.'/vendor/autoload.php';
$snowflake = new \Godruoyi\Snowflake\Snowflake;
echo $snowflake->id();
// output
// 199778615951360000
// 更多高級(jí)用法及實(shí)現(xiàn)原理參考原倉(cāng)庫(kù):https://github.com/godruoyi/php-snowflake/blob/master/README-zh_CN.md
// 連接Redis
$redis = new \Redis();
$redis->connect('192.168.0.112', 6379);
$cacheKey = date('Y:m:d');
$initVal = 10000;
// 實(shí)現(xiàn)方式一(使用隊(duì)列)
for ($i = 0; $i < 10; $i++) {
$redis->lPush($cacheKey, $initVal + $i);
}
$redis->rPop($cacheKey);
// 實(shí)現(xiàn)方式二(使用incr)
if ($redis->get($cacheKey)) {
// 返回新增后的值
return $redis->incr($cacheKey);
} else {
// 設(shè)置一個(gè)默認(rèn)的初始值
$redis->set($cacheKey, $initVal);
return $cacheKey;
}
數(shù)據(jù)庫(kù)直接就不演示,直接通過(guò)設(shè)置表字段屬性就行。主要設(shè)置字段值的初始值、偏移量。
mysql root@127.0.0.1:(none)> show variables like '%auto_incr%';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| auto_increment_increment | 1 |
| auto_increment_offset | 1 |
+--------------------------+-------+
2 rows in set
Time: 0.012s
通過(guò)上面的示例演示,下面針對(duì)這幾種情況做一個(gè)分析與總結(jié)。盡可能的選擇一種合理的方式。
實(shí)現(xiàn)方案 | 優(yōu)勢(shì) | 劣勢(shì) |
---|---|---|
UUID | 實(shí)現(xiàn)簡(jiǎn)單、方便;重復(fù)性低;數(shù)據(jù)庫(kù)查詢效率低 | 可讀性低;過(guò)于冗長(zhǎng) |
雪花算法 | 基于內(nèi)存、速度快;性能高;不會(huì)產(chǎn)生額外的網(wǎng)絡(luò)開(kāi)銷;數(shù)據(jù)依次成遞增 | 依賴于服務(wù)器時(shí)間,如變動(dòng)服務(wù)器時(shí)間則存在重復(fù)的情況 |
Redis | 基于內(nèi)存、速度庫(kù);使用簡(jiǎn)單;可分布數(shù)據(jù)、擴(kuò)展性強(qiáng) | 需要獨(dú)立搭建一套服務(wù)、增加了維護(hù)成本;跨應(yīng)用調(diào)用、存在網(wǎng)絡(luò)開(kāi)銷 |
數(shù)據(jù)庫(kù)自增 | 代碼層面無(wú)需任何特殊處理;利用MySQL特點(diǎn)實(shí)現(xiàn)數(shù)據(jù)遞增 | 并發(fā)性能差;MySQL負(fù)擔(dān)重 |
“怎么正確設(shè)計(jì)一個(gè)訂單號(hào)”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。