如何設計一個訂單號生成服務?應該考慮那些問題?


如何設計一個訂單號生成服務?應該考慮那些問題?
description: 在高并發的電商系統中,生成全局唯一的訂單編號是關鍵。本文探討了幾種常見的訂單編號生成方法,包括UUID、數據庫自增、雪花算法和基于Redis的分布式組件,并詳細分析了它們的優缺點。讓你在面試過程中更進一步。
這邊先來看一個場景:

在實際開發過程中,業務中最常見的一個服務就是生成業務單號,比如電商訂單編號、入庫單號、服務單號等等。那么如果讓你創建一個訂單號,你會考慮那些問題?有什么好的設計思路嘛?

需求分析

考慮一下如果讓你設計一個服務單號,需要滿足那些基本需求呢?、

  • 全局唯一性: 訂單號需要在在整個系統獨一無二,避免重復
  • 安全性: 訂單號不能暴漏太多信息,比如流水信息、用戶信息,那么自增這種方案就不能考慮了
  • 禁用隨機碼: 隨機碼雖然可以滿足前兩個條件,但是隨機碼沒有更多信息,比如相對順序、日期信息,同時伴有一定概率會重復
  • 滿足并發需求: 像在特定場合下(如秒殺)需要做到并發場景下的訂單號的生成
  • 控制位數: 訂單號的位數盡量在 10 位 ~ 18 位之間。太短的情況下,如果交易量過大,很難做到防止重復,太長可讀性差、意義也不大。
  • 有業務含義: 訂單號盡可能包含業務信息,比如訂單時間、業務類型等

再此基礎上如何設計一個優秀的訂單號服務呢?這個時候最好梳理一下功能點,需要滿足那些要求:

設計目標:

  • 滿足高并發需求
  • 可拓展性高
  • 滿足峰值壓力
  • 支持分布式架構
  • 檢索效率,如果訂單號存儲在數據庫中,檢索效率需要考慮

設計方案

接下來會闡述一些常見的方案,再談論一下這種解決方案的優缺點。

方案一:數據庫自增ID

所謂數據庫自增,意思是在數據庫中給某個列設置為自增列,并且給該列設置一個初始值,代碼層面無需任何特殊處理,以 Mysql 的用戶表 ID 列為例,可以通過如下方式在創建表的時候生產。

CREATE TABLE `tb_user` (`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,`name` varchar(20) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

優點:

  • 實現簡單
  • 保證唯一性

缺點:

  • 數據庫存在性能瓶頸,特別是高并發情況下
  • 分布式數據庫,在多個實例情況下難以保證全局唯一、
  • 安全性低,自增ID容易暴露信息
  • 無業務含義

適用場景:

如果是單體服務下,并且是初始業務情況下可以使用,但需要對數據庫進行分庫分表會出現重復ID。不建議直接使用這種方式,上限較低。

方案二:UUID

UUID 是Universally Unique Indentifier的縮寫,翻譯為通用唯一識別碼,顧名思義 UUID 是一個用于記錄唯一標識一條的數據,其按照開放軟件基金會(OSF)指定的標準進行計算,用到了以太網卡地址(MAC)、納秒級時間、芯片 ID 碼和許多可能的數字。

總的來說,UUID 碼由以下三部分組成:

  • 當前日期和時間
  • 時鐘序列
  • 全局唯一的 IEEE 機器識別碼(如果有網卡從網卡獲得,沒有網卡則通過其他方式獲得)

UUID 的標準形式包含 32 個 16 進制數字,以連字號分為五段,示例:00000191-adc6-4314-8799-5c3d737aa7de

java為例,通過以下方式即可生成:

String uuid = UUID.randomUUID().toString();

優點:

  • 全局唯一性
  • 簡單易用
  • 安全性: UUID的隨機性使得它不容易被猜測或預測,增加了數據的安全性
  • 無需集中管理:由于UUID是本地生成的,不需要依賴于中心化的ID生成服務,減少了單點故障的風險。
  • 可擴展性:UUID的生成過程是分布式的,可以在多個節點上并行生成,適合大規模分布式系統。

缺點:

  • 長度較長
  • 可讀性差: 無業務含義,難以理解
  • 索引效率低: UUID的隨機性會導致插入時的索引分裂和碎片化,從而降低寫入性能,查詢效率也低
  • 排序問題: UUID不具備自然的時間順序,因此不適合用于需要按時間順序進行排序的場景

適用場景:

如果是在分布式系統中,對訂單號有著特別高的要求,并且不需要長期持久化存儲以及不需要頻繁查詢那么就可以推薦使用。

分布式文件存儲系統,該系統需要處理大量的文件上傳、下載和管理操作。這些文件可能來自不同的用戶和設備,并且需要在多個服務器之間進行分布存儲和訪問。為了確保每個文件都有一個唯一的標識符,并且能夠在全球范圍內保持唯一性,UUID是一個非常適合的選擇。

方案三: 雪花算法

Snowflake(中文簡稱:雪花算法) 是 Twitter 內部的一個 ID 生算法,可以通過一些簡單的規則保證在大規模分布式情況下生成唯一的 ID 號碼。Snowflake把 64-bit分別劃分成多段,分開來標識機器ID、時間等。其核心思想是:使用 41bit作為毫秒數,10bit作為機器的ID(5個bit是數據中心,5個bit的機器ID),12bit作為毫秒內的流水號,最后還有一個符號位,永遠是0。SnowFlake 的結構圖如下所示:

SnowFlake

可以很清晰的看出,Snowflake 由 4個部分組成:

  • 第一部分:bit 值,為未使用的符號位
  • 第二部分:由 41 位的時間戳(毫秒)構成,它的取值是當前時間相對于某一時間的偏移
  • 第三部分:表示工作機器 id,由服務節點 id 和數據中心 id 組合而成
  • 第四部分:表示每個工作機器每毫秒生成的序列號 ID,同一毫秒內最多可生成生產 4095 個 ID。

由于在 Java 中 64bit 的整數是 long 類型,因此在 Java 中 SnowFlake 算法生成的 id 就是 long 來存儲的。

SnowFlake 算法可以保證:

  • 1.所有生成的 id 按時間趨勢遞增
  • 2.整個分布式系統內不會產生重復id(因為有服務節點 id 和數據中心 id 來做區分)

需要注意的是:

  • 在分布式環境中,5 個 bit 位的 datacenter 和 worker 表示最多能部署 31 個數據中心,每個數據中心最多可部署 31 臺節點。
  • 41 位的二進制長度最多能表示2^41 -1毫秒即 69 年,所以雪花算法最多能正常使用 69 年,為了能最大限度的使用該算法,在使用的時候,應該為其指定一個開始時間,不然會發生重復!

優點

  1. 高并發下的唯一性:由于算法設計考慮到了時間戳、機器標識等因素,因此即使是在大規模分布式系統中也能保證生成的ID是唯一的。
  2. 趨勢遞增:生成的ID通常是按照時間順序遞增的,這有利于數據庫索引性能優化。
  3. 信息量豐富:ID中包含了時間戳、工作節點ID等信息,使得每個ID都攜帶了額外的信息價值。
  4. 不依賴數據庫:相比于傳統基于數據庫自增ID的方式,雪花算法不需要訪問數據庫即可生成ID,減少了數據庫的壓力。
  5. 高效:計算效率高,適合快速生成大量ID的需求。

缺點

  1. 時鐘回撥問題:如果服務器時鐘出現回撥,則可能產生重復ID的問題。不過,可以通過等待時鐘追趕上來或者拒絕服務來解決這個問題。
  2. 有限的空間:雖然64位足夠大,但理論上還是存在上限,對于某些極端情況可能不夠用。
  3. 可讀性差:生成的ID對人類來說不易于閱讀和理解。
  4. 安全性較低:因為包含時間戳,所以可能泄露一些關于數據創建時間的信息,這在某些安全敏感的應用場景下可能是不利的。

適用場景

  • 分布式系統:特別適用于需要跨多個服務器或數據中心工作的應用程序,如電商網站、社交平臺等。
  • 高并發應用:當系統需要處理大量的并發請求并要求快速響應時,比如在線游戲、實時消息傳遞服務等。
  • 需要唯一標識符的服務:任何需要生成全局唯一標識符的服務都可以采用此方法,例如訂單管理系統、用戶賬號注冊等。
  • 大數據分析:由于ID帶有時間信息,有助于進行時間序列數據分析。

方案四:借助Redis,分布式組件

要想在分布式環境下生成一個唯一的訂單編號,我們可以通過分布式組件的方式,來幫忙我們生成全局唯一的訂單號,例如我們可以采用 redis 分布式緩存組件中的incr命令,來幫我們生成一個全局自增長的序列號。

實現某個Key實現自增的代碼如下:

// 基于某個key實現自增長
String res = jedis.get(key);
if (StringUtils.isBlank(res)) {// 設置初始值,INIT_ID 是初始值jedisClient.set(key, INIT_ID);// 設置過期時間,seconds 是多少秒過期jedisClient.expire(key, seconds);
}
//存在就生成+1的訂單號
long orderId = jedis.incr(key);

這種方式生成的自增長序列號,非常的快,可以很好的滿足大流量環境下的編號要求唯一的特性!

案例分析:

在互聯網幾個大廠的訂單號分析一下:

  • 京東商城訂單號格式:157444499

  • 蘇寧易購訂單號格式:2000839647

  • 凡客誠品訂單號格式:213052230059

  • 小米訂單號格式:1111218032345170

凡客誠品和銀泰網訂單號都含有 0522,這是因為這 2 張訂單都是2013年5月22號下的訂單。

基本猜測一下,凡客的訂單規則是:業務編碼+年的后2位+月+日+訂單數;泰網的訂單號規則:年的第三位數+業務編碼+年的后1位+月+日+訂單數;而京東商城和蘇寧易購的訂單號看不出規則。

再來分析一下小米訂單編號:

1211218032345170(16位)//拆解成為四個部分
1——211218—03234—5170
  • 第一部分,1 表示購買,2 表示退貨。
  • 第二部分,表示 2021 年 12 月 18 日下的單,前面兩位省掉了。
  • 第三部分,時間戳對應00:53:54,換算成秒是03234秒。
  • 最后一部分,表示在同一秒內下的第 5170 單,也就是說,小米認為,在一秒內不會超過一萬個訂單。

總結

通過上面的示例演示,下面針對這幾種情況做一個分析與總結。盡可能的選擇一種合理的方式。

實現方案優勢劣勢
數據庫自增代碼層面無需任何特殊處理;利用MySQL特點實現數據遞增并發性能差;MySQL負擔重
UUID實現簡單、方便;重復性低可讀性低;過于冗長;數據庫查詢效率低
雪花算法基于內存、速度快;性能高;不會產生額外的網絡開銷;數據依次成遞增依賴于服務器時間,如變動服務器時間則存在重復的情況
Redis基于內存、速度庫;使用簡單;可分布數據、擴展性強需要獨立搭建一套服務、增加了維護成本;跨應用調用、存在網絡開銷

總體上來說,優先選擇使用Redis進行分布式的處理方式,如果在沒有Redis的情況下,那就優先選用雪花算法。

如果想要設計一個訂單號,需要保留業務類型、時間信息等。

比如通過2位數字表示業務類型,如交易訂單、支付單、結算單等都是不同的業務類型,可以有不同的編號。
中間的18-20位用一個唯一的ID來表示,可以用雪花算法,也可以用Leaf,總之就是他需要保證唯一性。
最后4位,基于基因法,將分表后的結果獲取到,把他也編碼到訂單號中。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/75540.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/75540.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/75540.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Springboot 集成 Flowable 6.8.0

1. 創建 Spring Boot 項目 通過 Spring Initializr(https://start.spring.io/ )創建一個基礎的 Spring Boot 項目,添加以下依賴: Spring WebSpring Data JPAMySQL DriverLombok(可選,用于簡化代碼&#x…

《TCP/IP網絡編程》學習筆記 | Chapter 22:重疊 I/O 模型

《TCP/IP網絡編程》學習筆記 | Chapter 22:重疊 I/O 模型 《TCP/IP網絡編程》學習筆記 | Chapter 22:重疊 I/O 模型理解重疊 I/O 模型重疊 I/O本章討論的重疊 I/O 的重點不在于 I/O 創建重疊 I/O 套接字執行重疊 I/O 的 WSASend 函數進行重疊 I/O 的 WSA…

搭建Redis哨兵集群

停掉現有的redis集群 因為這篇文章我是在 搭建完redis主從集群之后寫的,如果要是沒有搭建過這些,可以直接略過。要是從我上一篇 搭建redis主從集群過來的,可以執行下。 docker compose down 查找下redis相關進程 ps -ef | grep redis 可以看…

MySQL中,聚集索引和非聚集索引到底有什么區別?

文章目錄 1. 數據存儲方式2. 索引結構3. 查詢效率4. 索引數量5. 適用場景6. 示例說明7. 總結 在MySQL中,聚集索引和非聚集索引(也稱二級索引)的區別主要體現在數據存儲方式、索引結構和查詢效率等方面。以下是詳細對比: 1. 數據存…

看 MySQL InnoDB 和 BoltDB 的事務實現

BoltDB 事務實現 BoltDB 支持多讀單寫方式的并發級別 事務操作會鎖表 它的 MVCC 為 2 個版本,當前版本和正在寫的版本 多讀:可以并發讀當前版本 單寫(串行寫):寫時拷貝當前 B 樹,構建新 B 樹&#xff…

08_JavaScript數據操作方法_數組

目錄 一、創建一個數組 1.1 數組如何創建 字面量創建 構造函數創建 1.2 數組的長度 數組名.length 1.3 數組的索引 1.4 數組如何循環遍歷 for 循環遍歷 for in for of 二、數組的常用方法 (重點 面試) push 方法 unshift 方法 pop shif…

2025.3.25總結

工作:這兩天工作都沒啥產出,主要是工作狀態不太好,周日晚上兩點睡,周一晚上一點睡。熬夜傷身,但就是控制不住自己,睡前總要刷刷手機。本來想睡前看會書的,但這行為及其不穩定,抖音也…

《Python實戰進階》第33集:PyTorch 入門-動態計算圖的優勢

第33集:PyTorch 入門-動態計算圖的優勢 摘要 PyTorch 是一個靈活且強大的深度學習框架,其核心特性是動態計算圖機制。本集將帶您探索 PyTorch 的張量操作、自動求導系統以及動態計算圖的特點與優勢,并通過實戰案例演示如何使用 PyTorch 實現…

初識哈希表

一、題意 給定一個整數數組 nums 和一個目標值 target,要求你在數組中找出和為目標值的那兩個整數,并返回它們的數組下標。你可以假設每種輸入只會對應一個答案。但是,數組中同一個元素不能使用兩遍。 示例: 給定 nums [2, 7, …

23種設計模式-創建型模式-單例

文章目錄 簡介問題1. 確保一個類只有一個實例2. 為該實例提供全局訪問點 解決方案示例重構前:重構后: 拓展volatile 在單例模式中的雙重作用 總結 簡介 單例是一種創建型設計模式,它可以確保一個類只有一個實例,同時為該實例提供…

python裁剪nc文件數據

問題描述: 若干個nc文件儲存全球的1850-2014年月尺度的mrro數據(或其他數據),從1850-1到2014-12一共1980個月,要提取出最后35年1980.1~2014.12年也就是420個月的數據。 代碼實現 def aaa(input_file,output_file,bianliang,start_index,en…

深入解析 Spring Framework 5.1.8.RELEASE 的源碼目錄結構

深入解析 Spring Framework 5.1.8.RELEASE 的源碼目錄結構 1. 引言 Spring Framework 是 Java 領域最流行的企業級開發框架之一,廣泛用于 Web 開發、微服務架構、數據訪問等場景。本文將深入解析 Spring Framework 5.1.8.RELEASE 的源碼目錄結構,幫助開…

數據清洗:基于python抽取jsonl文件數據字段

基于python抽取目錄下所有“jsonl”格式文件。遍歷文件內某個字段進行抽取并合并。 import os import json import time from tqdm import tqdm # 需要先安裝:pip install tqdmdef process_files():# 設置目錄路徑dir_path r"D:\daku\關鍵詞識別\1623-00000…

Windows 下使用 Docker 部署 Go 應用與 Nginx 詳細教程

一、環境準備 1. 安裝必要軟件 Docker Desktop for Windows 下載地址:Docker Desktop: The #1 Containerization Tool for Developers | Docker 安裝時勾選"使用 WSL 2 引擎"(推薦) WSL 2(Windows Subsystem for Li…

C# .net ai Agent AI視覺應用 寫代碼 改作業 識別屏幕 標注等

C# net deepseek RAG AI開發 全流程 介紹_c# 向量處理 deepseek-CSDN博客 視覺多模態大模型 通義千問2.5-VL-72B AI大模型能看懂圖 看懂了后能干啥呢 如看懂圖 讓Agent 寫代碼 ,改作業,識別屏幕 標注等等。。。 據說是目前最好的免費圖片識別框架 通…

Docker多階段構建:告別臃腫鏡像的終極方案

Docker多階段構建:告別臃腫鏡像的終極方案 你是否遇到過這樣的問題:一個簡單的應用,Docker鏡像卻高達1GB?編譯工具、臨時文件、開發依賴全被打包進去,導致鏡像臃腫且不安全。 多階段構建(Multi-stage Build) 就是為解決這一問題而生——它像搬家時“只帶必需品”,讓生…

大模型應用開發之大模型工作流程

一:大模型的問答工作流程 1.1: 分詞和向量化 如上圖所示,我們如果讓大模型去回答問題,首先我們會輸入一些文字給到大模型,大模型本質上是個數學模型,它是理解不了人類的整句話的,所以它會把我們的對應的句…

SpringMVC 請求處理

SpringMVC 請求處理深度解析:從原理到企業級應用實踐 一、架構演進與核心組件協同 1.1 從傳統Servlet到前端控制器模式 SpringMVC采用前端控制器架構模式,通過DispatcherServlet統一處理請求,相比傳統Servlet的分散處理方式,實…

12屆藍橋杯—貨物擺放

貨物擺放 題目描述 小藍有一個超大的倉庫,可以擺放很多貨物。 現在,小藍有 nn 箱貨物要擺放在倉庫,每箱貨物都是規則的正方體。小藍規定了長、寬、高三個互相垂直的方向,每箱貨物的邊都必須嚴格平行于長、寬、高。 小藍希望所…

Reactor/Epoll為什么可以高性能?

在 Reactor 模式中使用 epoll_wait 實現低 CPU 占用率的核心原理是 ?事件驅動的阻塞等待機制,而非忙等待。以下通過分步驟解析其工作原理和性能優勢: void network_thread() {int epoll_fd epoll_create1(0);epoll_event events[MAX_EVENTS];// 添加U…