参考资料:
一、项目背景
在公司内部系统中,有许多场景需要生成有业务意义的单号,比如,财务系统的报销申请单,单号规则是 BX + yyyyMMdd + 4位递增序号。基于指这个原因,自己封装了生成全局唯一单号的服务,类似分布式发号系统。
二、数据库方案
该方案通过数据库的唯一键保证单号唯一,结合乐观锁提高并发量。
2.1 处理流程
每个单号通过 系统编号+单据前缀+日期 进行区分,数据库的解决方案处理流程如下:
流程说明:
- 表结构设计时,把
系统编号+单据前缀+日期设计成一个唯一键 - 新增序号时,由于服务会部署多个节点,有可能存在多个节点同时插入相同的单号,这样会出现唯一键冲突。所以捕获这个特定的异常,走更新序号的逻辑
- 更新序号时,由于服务会部署多个节点,业务系统可能会并发访问,一方面为了保证单号唯一且递增,另一方面提高并发量,使用了乐观锁
2.2 压测结果
服务器信息:1台,8核,4G。
使用jmeter进行压测,线程数设置为32个线程,循环500次,得到的压测结果是 180 TPS
2.3 方案不足
数据库的查询性能和连接数有可能是性能瓶颈。
三、Redis方案
该方案利用 redis 的 incr命令保证单号唯一
3.1 处理流程
流程说明:
-
在 redis 不存在指定key时,需要读取数据库的原因是,避免由于某些原因把序号删掉,比如内存不足,驱逐部分key。
-
除了执行以上流程外,还需要启动一个
定时任务从 redis 读取最新的序号,保存到数据库。(更新条件是redis序号大于数据库序号)- 调用incr后不更新数据库的原因:每次更新序号都要把序号保存到数据库,这样会影响到接口的处理速度。如果每次都把最新序号写回数据库,那倒不如直接用数据库方案,还不用增加 redis 组件,降低维护成本
3.2 压测结果
服务器信息:1台,8核,4G
使用jmeter进行压测,线程数设置为256个线程,循环500次,得到的压测结果是 2k8 TPS
3.3 方案不足
在数据库写入最新序号前,redis 把序号被删掉,这样会导致部分序号重复。个人看法是,一方面redis内存不足的概率比较低。另外,业务系统的表结构会把这个单号设计成唯一键,插入重复单号会发生冲突。这样来看,风险不大,应该能接受。