聊聊支付那些事系列
目录
从前几章的介绍我们知道,要搭建一个支付系统中有订单、支付单、账单等等会使用到很多的单号,那我们单号怎么生成呢。本章我们来聊一聊这个问题。
大家好!在前面的章节中,我们已经深入了解了支付系统的构建,涉及到了订单、支付单、账单等多个核心组件。在这个过程中,我们不难发现,单号作为贯穿整个支付流程的关键标识,其生成与管理显得尤为重要。那么,今天我们就来专门探讨一下单号生成的问题,看看如何科学、高效地生成和管理这些单号。让我们开始吧!
思考
我们先来思考一下,我们对于这个生成器的诉求是什么
1、生成的值不重复
2、能够一直用不会挂
3、能够在扛得住系统压力 例如 【100W/S 5ms以内】
4、简单、好用
5、足够随机、不会被推测
抽象标准化要求
1、全局唯一
2、可以支持三高场景:高可用、高性能、高并发
4、接入简单
如何实现
现有多种方式对比
暂时省略。。。
雪花算法思路
- 分段:将不同含义的数据整合成一个uuid值
业务场景 | 时间戳 | 实例号 | 。。。 扩展 | 随机序列号 |
---|---|---|---|---|
SYTDD(不同业务场景可以重复) | 20240909121212 | 0001 | 0000 |
实例号和随机序列号长度可根据实际业务调整。
- 本地化
实际生成uuid的时候 可以支持系统运行时仅仅本地生成,不强依赖中心化服务。
本地随机数位数:能保证单位时钟内单机支持的最大量级 0000-9999-0000 也就是说单机每秒可以支持1W的调用,实际使用时看业务使用情况来定。
无特殊诉求支持默认的生成序列 如果同系统中不同场景需要隔离序列,那可以提供多个生成序列,每个序列化支持一个时钟周期数据,用hash来记录序列随机初始值和序列对应的前置值。就能保证序列内数据唯一
需要的数据结构 三个hash存储数据
序列名:序列号
序列名:序列前缀
序列名:随机初始序列号
伪代码
lock
序列名前缀存在 序列名:序列前缀
拿出序列化++
序列号==随机初始序列号
本地周期号用完
序列号覆盖原有值 写 序列名:序列号
序列名前缀不存在
重置生产随机序列号
写 序列名:序列号 序列名:序列前缀 序列名:随机初始序列号
unlock
return 前缀+序列号
- 管理实例号
从第一步分析可以得出,业务场景可以生成时指定、时间戳使用机器时钟即可、随机数本地保证单位时钟内不重复就行了。
所以我们需要一个实例号统一管理,来保证不具备相同的实例,这样就能保证了全局的唯一
支持接入方式
中心化uuid生成
本地化uuid生产
使用jar包接入
指定实例管理中心、接入环境、接入应用
容器启动实例化完成后 提供一个默认方法直接生成uuid,和调用本地方法区别不大
问题
-
生成的uuid在分库分表的场景上怎么保证分配不均的问题
解答:这个说的也就是随机率的问题,如果我们的数据随机率足够大,那理论上来说一般是不会出现分布不均的问题,那么这个时候,实际就是看后面几位随机数全局上怎么保证随机率的问题
一般不建议本地随机真的是完全随机,因为我们要保证的是单位时间内是不能重复的。
建议:单位时钟内随机生成一个初始值,然后依次+1,从000-999内部循环,同时记录单位时钟内生成次数不能超过随机数最大阈值。 这样在有分库分表诉求的大流量下也基本上可以保证数据的随机率。 -
为什么常见的UUID19位的居多
全局唯一ID之所以多为19位,主要归功于雪花算法(Snowflake)的广泛应用。该算法生成的是一个64位的Long型数字,作为全局唯一ID,其在分布式系统中表现出色。具体来说:
唯一性:雪花算法通过结合时间戳、机器ID和序列号,确保了ID的唯一性。
高性能:算法设计使得ID生成过程高效,能够满足高并发场景下的需求。
趋势递增:生成的ID趋势递增,有利于数据库存储和索引。
在实际应用中,如MySQL数据库,通常使用bigint类型来存储这64位的ID,而bigint类型的长度为20位,其中19位用于存储实际ID值,剩余1位可能用于其他目的或作为标识。因此,全局唯一ID多为19位的现象,主要源于雪花算法在分布式系统中的广泛应用及其生成的ID特性 -
时钟回拨问题怎么解决
这个问题的解决方式有很多种。我个人建议以下两种方式相结合来处理
本地时钟回拨缓存:
记录上一次生成ID的时间戳,当检测到时钟回拨时,使用缓存的时间戳代替当前时间戳生成ID。
使用NTP协议同步时间:
通过与NTP服务器同步时间,确保服务器时间准确无误,从而避免时钟回拨问题。