如何设计一个订单号生成服务?

news2025/1/12 15:46:16

  • 一、数据量的大小
  • 二、有意义的ID
  • 三、高可用
  • 四、高性能
  • 五、唯一性
  • 六、包含分库分表的业务字段
  • 七、实战
    • 1) Redis
    • 2) Leaf-segment 数据库生成
    • 3)Leaf-snowflake方案
    • 4) UUID

如何设计要给订单号生成服务

一、数据量的大小

在设计订单号的生成服务时候,我们首先要考虑的就是数据量的大小,从而根据数据量的大小来设计出多少位数的ID,以免出现位数少,造成存储不下的问题,也避免了位置大从而影响索引的检索效率问题。

二、有意义的ID

为了方便排查问题,和易于理解和记忆,需要充分的考虑到订单号的格式和组成方式,例如使用时间戳、随机数、用户ID等信息来构造订单号

三、高可用

订单号生成服务需要保证高可用,利用多节点部署、负载均衡、健康检查等技术来提高可靠性和稳定性。作为其他服务的基础服务,必须做到高可用,否则就会阻塞其他的服务。

四、高性能

在TOB的场景下,接口的性能是至关重要的,而生成ID的服务是其他服务的基础服务,如果当前服务性能比较低,就会影响其他服务的性能效率的问题,所以可以采用内存缓存、异步处理等技术来优化性能。

五、唯一性

订单号必须保证唯一性,否则会出现订单冲突和数据不一致等问题。可以使用一些常见的唯一性生成算法,例如UUID、Snowflake、数据库生成、Redis等。

雪花算法(Snowflake)雪由Twitter研发的的一种分布式ID生成算法,它可以生成全局唯一且递增的ID。它的核心思想是将一个64位的ID划分成多个部分,每个部分都有不同的含义,包括时间戳、数据中心标识、机器标识和序列号等。

具体来说,雪花算法生成的ID由以下几个部分组成:

  1. 符号位(1bit):预留的符号位,始终为0,占用1位。
  2. 时间戳(41bit):精确到毫秒级别,41位的时间戳可以容纳的毫秒数是2的41次幂,一年所使用的毫秒数是:365 * 24 * 60 * 60 * 1000,算下来可以使用69年。
  3. 数据中心标识(5bit):可以用来区分不同的数据中心。
  4. 机器标识(5bit):可以用来区分不同的机器。
  5. 序列号(12bit):可以生成4096个不同的序列号。
alt

雪花算法的优势

  • 首先,时间戳位于ID的最高位,保证新生成的ID比旧的ID大,在不同的毫秒内,时间戳肯定不一样。

  • 其次,引入数据中心标识和机器标识,这两个标识位都是可以手动配置的,帮助业务来保证不同的数据中心和机器能生成不同的ID。

  • 还有就是,引入序列号,用来解决同一毫秒内多次生成ID的问题,每次生成ID时序列号都会自增,因此不同的ID在序列号上有区别。

所以,基于时间戳+数据中心标识+机器标识+序列号,就保证了在不同进程中主键的不重复,在相同进程中主键的有序性。

  • 高性能高可用:生成时不依赖于数据库,完全在内存中生成

  • 高吞吐:每秒钟能生成数百万的自增 ID

  • ID 自增:在单个进程中,生成的ID是自增的,可以用作数据库主键做范围查询。但是需要注意的是,在集群中是没办法保证一定顺序递增的。

雪花算法的劣势

  • 某个节点出现故障了,就需要改其对应机器ID或者数据中心的Id,也就需要重新部署系统。

  • 还有就是如果某个机器部署的时候出现了一摸一样的机器ID和数据中心的ID那么就有可能出现重复的ID,这样就会导致系统的错误和异常。

  • 依赖于系统的时间一致性,如果系统的时间被回拨,或者不一致,可能就会造成ID的重复

时间回拨是指系统在运行过程中,可能由于网络时间校准或者人工设置,导致系统时间主动或被动地跳回到过去的某个时间 解决方案:一旦发生这种情况,简单粗暴的做法是抛异常,发现时钟回调了,就直接抛异常出来。另外还有一种做法就是发现时钟变小了,就拒绝ID生成请求,等到时钟恢复到上一次的ID生成时间点后,再开始生成新的ID。

  • 需要Zookeeper来协调个节点的ID生成,但是ZK的部署其实是挺大的成本的,并且Zookeeper本身也可能是系统的瓶颈。

六、包含分库分表的业务字段

订单系统到最后都可能会考虑分库分表,所以在最初设计订单号的时候,需要考虑将和分表有关的字段编码到订单号中,如买家ID等。

七、实战

1) Redis

通过 Redis 的 incr 命令即可实现对 id 原子顺序递增,例如:

127.0.0.1:6379> incr sequence_id_biz_type

为了提高可用性和并发,我们可以使用 Redis Cluster。

不过,我们也知道,即使 Redis 开启了持久化,不管是快照(snapshotting, RDB)、只追加文件(append-only file, AOF)还是 RDB 和 AOF 的混合持久化依 然存在着丢失数据的可能,那就意味着产生的 ID 存在着重复的概率

2) Leaf-segment 数据库生成

创建数据库

字段名称数据类型备注
biz_tagvarchar(128)业务标签
max_idbigint使用过的最大ID
stepint步长(没次生成的ID个数)
descriptionvarchar描述
update_timetimestamp更新时间

原 MySQL 方案每次获取 ID 都得读写一次数据库,造成数据库压力大。改为 批量获取,每次获取一个 segment(step 决定大小)号段的值。用完之后再去数据 库获取新的号段,可以大大的减轻数据库的压力。

各个业务不同的发号需求用 biz_tag 字段来区分,每个 biz-tag 的 ID 获取相互 隔离,互不影响。如果以后有性能需求需要对数据库扩容,不需要上述描述的复 杂的扩容操作,只需要对 biz_tag 分库分表就行。

重要字段说明:biz_tag 用来区分业务,max_id 表示该 biz_tag 目前所被分配 的 ID 号段的最大值,step 表示每次分配的号段长度。原来获取 ID 每次都需要写数据库,现在只需要把 step 设置得足够大,比如 1000。那么只有当 1000 个号被 消耗完了之后才会去重新读写一次数据库。读写数据库的频率从 1 减小到了 1/step。

优点

  • Leaf 服务可以很方便的线性扩展,性能完全能够支撑大多数业务场景。
  • ID 号码是趋势递增的 8byte 的 64 位数字,满足上述数据库存储的主键要求。
  • 容灾性高:Leaf 服务内部有号段缓存,即使 DB 宕机,短时间内 Leaf 仍能正常对外提供服务。
  • 可以自定义 max_id 的大小,非常方便业务从原有的 ID 方式上迁移过来。

缺点

  • ID 号码不够随机,能够泄露发号数量的信息,不太安全。
  • TP999 数据波动大,当号段使用完之后还是会在获取新号段时在更新数据库的 I/O 依然会存在着等待,tg999 数据会出现偶尔的尖刺。

为此,希望 DB 取号段的过程能够做到无阻塞,不需要在 DB 取号段的时候 阻塞请求线程,即当号段消费到某个点时就异步的把下一个号段加载到内存中。 而不需要等到号段用尽的时候才去更新号段。这样做就可以很大程度上的降低系 统的 TP999 指标。

  • DB 宕机会造成整个系统不可用。

3)Leaf-snowflake方案

Leaf-segment 方案可以生成趋势递增的 ID,同时 ID 号是可计算的,不适用 于订单 ID 生成场景,比如竞对在两天中午 12 点分别下单,通过订单 id 号相减 就能大致计算出公司一天的订单量,这个是不能忍受的。面对这一问题,美团提 供了 Leaf-snowflake 方案。

Leaf-snowflake 方案完全沿用 snowflake 方案的 bit 位设计,即是“1+41+10+12” 的方式组装 ID 号。对于 workerID 的分配,当服务集群数量较小的情况下,完全 可以手动配置。Leaf 服务规模较大,动手配置成本太高。所以使用 Zookeeper 持 久顺序节点的特性自动对 snowflake 节点配置 wokerID。Leaf-snowflake 是按照下 面几个步骤启动的:

启动 Leaf-snowflake 服务,连接 Zookeeper,在 leaf_forever 父节点下检查自 己是否已经注册过(是否有该顺序子节点)。

如果有注册过直接取回自己的 workerID(zk 顺序节点生成的 int 类型 ID 号), 启动服务。

如果没有注册过,就在该父节点下面创建一个持久顺序节点,创建成功后取 回顺序号当做自己的 workerID 号,启动服务。

4) UUID

public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            String rawUUID = UUID.randomUUID().toString();
            System.out.println(rawUUID);
            //去除"-"
            String uuid = rawUUID.replaceAll("-""");
            System.out.println(uuid);
        }
    }

本文由 mdnice 多平台发布

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/915469.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【深度学习 | ResNet核心思想】残差连接 跳跃连接:让信息自由流动的神奇之道

&#x1f935;‍♂️ 个人主页: AI_magician &#x1f4e1;主页地址&#xff1a; 作者简介&#xff1a;CSDN内容合伙人&#xff0c;全栈领域优质创作者。 &#x1f468;‍&#x1f4bb;景愿&#xff1a;旨在于能和更多的热爱计算机的伙伴一起成长&#xff01;&#xff01;&…

ms-tpm-20-ref 在linux下编译

1、代码地址&#xff0c; GitHub - microsoft/ms-tpm-20-ref: Reference implementation of the TCG Trusted Platform Module 2.0 specification.Reference implementation of the TCG Trusted Platform Module 2.0 specification. - GitHub - microsoft/ms-tpm-20-ref: Refe…

企业/公司电脑图纸加密软件系统——「天锐绿盾加密软件」

天锐绿盾是一款企业级透明加密软件&#xff0c;旨在保护企业或设计院的图纸文件和电子文档&#xff0c;防止信息泄露和数据安全问题。 ​ 天锐绿盾采用了核心驱动层的透明加密 技术&#xff0c;可以在操作系统层自动对所有需要加密的文档进行加密和解密&#xff0c;用户在使用加…

交换机的基本原理与配置(二)

目录 2. 以太网交换机 2.1 交换机设备简介 2.2交换机的工作原理 2.3交换机接口的双工模式 2. 以太网交换机 下面通过交换机设备简介&#xff0c;交换机的工作原理及交换机接口模式来了解以太网交换机。 2.1 交换机设备简介 交换机的品牌众多&#xff0c;像Cisco公司&#xf…

自动设置服务器全教程

亲爱的爬虫探险家&#xff01;在网络爬虫的世界里&#xff0c;自动设置代理服务器是一个非常有用的技巧。今天&#xff0c;作为一家代理服务器供应商&#xff0c;我将为你呈上一份轻松实用的教程&#xff0c;帮助你轻松搞定爬虫自动设置代理服务器。 一、为什么需要自动设置代…

华为OD-第K长的连续字母字符串长度

题目描述 给定一个字符串&#xff0c;只包含大写字母&#xff0c;求在包含同一字母的子串中&#xff0c;长度第 k 长的子串的长度&#xff0c;相同字母只取最长的那个子串。 代码实现 # coding:utf-8 # 第K长的连续字母字符串长度 # https://www.nowcoder.com/discuss/353150…

差分走线的要求

1 等长&#xff1a;等长是指两条线的长度要尽量一样长&#xff0c;是为了保证两个差分信号时刻保持相反极性。减少共模分量。 2等宽等距&#xff1a;等宽是指两条信号的走线宽度需要保持一致&#xff0c;等距是指两条线之间的间距要保持不变&#xff0c;保持平行。 对走线要求最…

pytorch下的scatter、sparse安装

知道自己下载的torch配置 import torch print(torch.__version__) print(torch.version.cuda)进入网站&#xff0c;选择自己配置 https://pytorch-geometric.com/whl/下载相应的包 安装 pip install ******.whl

办公网络布线(三)

目录 3.布线施工设计 3.1 宿舍楼布线设计 3.布线施工设计 如图所示为宿舍楼平面图。 3.1 宿舍楼布线设计 前面讲解了布线系统的常识及具体实施的细节&#xff0c;但在实际工作中&#xff0c;作为工程的实施人员往往需要根据甲方提供的建筑平面图图纸给出网络建设方案&#xf…

Commonjs和Es6语法规范的理解

ES6 module和CommonJS到底有什么区别&#xff1f; “ES6 module是编译时加载&#xff0c;输出的是接口&#xff0c;CommonJS运行时加载&#xff0c;加载的是一个对象” 这里的“编译时”是什么意思&#xff1f;和运行时有什么区别&#xff1f;“接口”又是什么意思&#xff1f;…

Oracle-rolling upgrade升级19c

前言: 本文主要描述Oracle11g升19c rolling upgrade升级测试&#xff0c;通过逻辑DGautoupgrade方式实现rolling upgrade&#xff0c;从而达到在较少停机时间内完成Oracle11g升级到19c的目标 升级介绍&#xff1a; 升级技术: rolling upgrade轮询升级&#xff0c;通过采用跨版…

PS gif修改背景颜色(附加图片)

文章目录 问题描述.mp4转换为.gif 解决方案gif更换背景修改背景具体操作第一步第二步第三步第四步第五步第六步 总结 大家好&#xff01;不知不觉又到周二了&#xff0c;农历&#xff1a;七月七&#xff0c;本来今天可以高高兴兴的度过一天&#xff0c;唉&#xff0c;接到领导布…

强训第38天

选择 D 0作为本地宿主机&#xff0c;127作为内部回送&#xff0c;不予分配 A B C C 存储在浏览器 D A B B D 网络延迟是指从报文开始进入网络到它离开网络之间的时间 编程 红与黑 红与黑__牛客网 #include <iostream> #include <stdexcept> #include <string…

【仿写框架之仿写Tomact】一、详解Tomcat的工作流程

文章目录 1、启动阶段2、监听阶段&#xff1a;3、请求处理阶段&#xff1a;4、发送请求处理后的响应 当涉及到Java Web应用程序的部署和运行&#xff0c;Apache Tomcat无疑是一个备受欢迎的选择。Tomcat作为一个开源的、轻量级的Java Servlet容器和JavaServer Pages (JSP) 容器…

排兵布阵很重要

以正合&#xff0c;以奇胜&#xff0c;排兵布阵很重要 【安志强趣讲《孙子兵法》第17讲】 第五章&#xff1a;势篇 【全文趣讲大白话】 善于打仗&#xff0c;能恰当的分兵&#xff0c;能借势。 【原文】 孙子曰&#xff1a;凡治众如治寡&#xff0c;分数是也&#xff1b;斗众如…

SFL214-100-21-10喷嘴挡板伺服阀

SFL214-100-21-10、SFL214-150-21-15、SFL214-100-32-40、SFL214-100-21-40、SFL216-230-21-40、SFL216-230-32-40采用千式力马达和两级液压放大器结构前置级为无摩撩副的双喷嘴挡板阀性能优良&#xff0c;动态响应高适用于对位置&#xff0c;力和速度进行闭环控制可用作三通阀…

函数的使用-1函数默认参数

在C中&#xff0c;函数的形参列表中的形参是可以有默认值的。 语法&#xff1a; 返回值类型 函数名 &#xff08;参数 默认值&#xff09;{} 1. 如果某个位置参数有默认值&#xff0c;那么从这个位置往后&#xff0c;从左向右&#xff0c;必须都要有默认值 2. 如果函数声明有默…

FPGA实现10G万兆网TCP/IP 协议栈,纯VHDL代码编写,提供服务器和客户端2套工程源码和技术支持

目录 1、前言免责声明 2、我这里已有的以太网方案3、该TCP/IP协议栈性能常规性能支持多节点FPGA资源占用少数据吞吐率高低延时性能 4、TCP/IP 协议栈代码详解代码架构用户接口代码模块级细讲顶层模块PACKET_PARSING_10G模块ARP_10G模块DHCP_SERVER_10G和 DHCP_CLIENT_10G模块IG…

c语言实现双向链表

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、链表的分类1、 单向或者双向链表2、带头结点或者不带头结点的链表3、循环或者非循环链表 二、带头双向循环链表1、链表结点的结构体定义2、链表的初始化3、…