怎样生成分布式的流水ID
流水编号
日常在我们开发的过程中可能会用到编号的功能,如销售订单号,采购订单号,日志编号,凭证号...等等,为了保证唯一有些表的主键要么用自增长,要么用GUID值,或通过雪花ID算法生成。这此方式基本都能产生唯一的ID,但如果在分布式环境下产生流水ID,以上这几种方式可能就不太好用,如有以下场景
工作流的流水编号
工作流的编号通常会是以下格式 如2022060200001-2022060299999 到了第二天时尾数又要生00001开始编,这种编号规则有一个好处就是非常直观的通过工作流编号就可以看出来这是哪一天申请的流程,一天大概有多少流水码。那么如果我们用常规编号规则其实是比较难完成此需求的。
采购订单号
如在实际业务需求中 标准采购订单要用5000000000-5999999999 这个号进行进行编码,委外采购订单用4000000000-4999999999号段进行编码
XX业务受理单编号
在办里某业务时根据业务类型使用不同的编号 如财务收款用的流水号是 SK10000000-SK99999999,支出用的是ZC10000000-ZC99999999
根据以上的业务场景如果使用常规的编号是比较难实现的(要保证分布式环境编号不重复且按流水编码),那么HiSql
提供了比较方便的解决方案
该版本还未正式更新nuget 需要使用请下载源码
怎样开启编号规则
//如果需要使用编号那么一定要开启此功能
HiSql.Global.SnroOn = true;
//开启编号后进行初始化
sqlClient.CodeFirst.InstallHisql();//仅需执行一次 如果使用的低版本的HiSql 升级引用包手需要重新初始化安装
初始安装完成后会生成表Hi_Snro
这个编号配置表
编号配置介绍
Hi_Snro
表详细说明
字段 | 描述 | 备注 |
---|---|---|
SNRO |
编号规则名称 | 主键 字符串(10) |
SNUM |
子编号 | 主键 整数 |
IsSnow |
是否雪花ID | bool true:表示雪花id false:表示自定义流水编号 |
SnowTick |
雪花ID时间戳 | int64 IsSnow 为true时配置 大于0 都可以,其它的都可以不用配置 |
StartNum |
编号开始值 | 字符串(20) 当IsNumber 为true 时编号只能纯数字 为false时 可以以0-9 A-Z 混编 |
EndNum |
编号结束值 | 字符串(20) 当编号超过此时时将会抛出异常号段池已满 |
CurrNum |
当前编号值 | 字符串(20) 第一次配置时值要等于StartNum |
CurrAllNum |
当前完整编号 | 字符串(40) 不需要配置,产生流水时会自动生成 |
Length |
编号长度 | StartNum 和EndNum 的长度 两者的长度要一至否则会报错 |
IsNumber |
是否纯数字编号 | 当为true 编号按0-9的10进制数字编号,当为false时按0-9 A-Z 36进度数字和字母混合编号 |
IsHasPre |
是否有前辍 | 可以按到年月日时分秒作为前辍 在PreType 配置 |
PreType |
前辍编号类型 | 详细请见 PreType 前置编号类型配置 |
FixPreChar |
固定前辍 | 固定一个字符串 每个生成的码前面都加上这个值 |
PreChar |
当前前辍 | 不需要配置 在编号的过程中会将 FixPreChar 和 PreType 存在此昝 |
CacheSpace |
号段缓存 | 编号使用越频繁这个值配置越大 常的建议配置为10 值越大编号性能越好 |
CurrCacheSpace |
当前缓存池使用的数量 | 不需要配置 |
Descript |
编号描述 | 备注一下当前编号规则 |
PreType
前置编号类型配置
在PreType
是一个枚举类
字段 | 值 | 备注 |
---|---|---|
PreType.None |
0 | 表示无前置 |
PreType.Y |
1 | 表示前置为日期格式[yyyy] 即当前年份如2022 |
PreType.Y2 |
12 | 表示前置为日期格式[yy] 即当前年份后两位如22 |
PreType.YM |
2 | 表示前置为日期格式[yyyyMM] 即当前年月如202206 |
PreType.Y2M |
22 | 表示前置为日期格式[yyMM] 即当前年月如2206 |
PreType.YMD |
3 | 表示前置为日期格式[yyyyMMdd] 即当前年月日如20220602 |
PreType.Y2MD |
32 | 表示前置为日期格式[yyMMdd] 即当前年月日如220602 |
PreType.YMDH |
4 | 表示前置为日期格式[yyyyMMddHH] 即当前年月日时如2022060216 |
PreType.Y2MDH |
42 | 表示前置为日期格式[yyMMddHH] 即当前年月日时如22060216 |
PreType.YMDHm |
5 | 表示前置为日期格式[yyyyMMddHHmm] 即当前年月日时分如202206021630 |
PreType.Y2MDHm |
52 | 表示前置为日期格式[yyMMddHHmm] 即当前年月日时分如2206021630 |
PreType.YMDHms |
6 | 表示前置为日期格式[yyyyMMddHHmmss] 即当前年月日时分秒如20220602163020 |
PreType.Y2MDHms |
62 | 表示前置为日期格式[yyMMddHHmmss] 即当前年月日时分秒如220602163020 |
根据以上配置规则将数据配置到表中
配置样例
Hi_Snro
的配置可以自行做个配置界面进行配置,以下是通过程序进行配置保存.
List<Hi_Snro> list = new List<Hi_Snro>();
///工作流编号配置
///按天产生流水号 如2205061000000-2205069999999 之间
list.Add( new Hi_Snro { SNRO = "WFNO", SNUM = 1, IsSnow = false, SnowTick = 0, StartNum = "1000000", EndNum = "9999999", Length = 7, CurrNum = "1000000", IsNumber = true, PreType=PreType.Y2MD, FixPreChar="", IsHasPre = true, CacheSpace = 5, Descript = "工作流编号" });
///生成销售订单编码 每分钟从0开始编号 如20220602145800001-20220602145899999
list.Add(new Hi_Snro{ SNRO = "SALENO", SNUM = 1, IsSnow = false, SnowTick = 0, StartNum = "10000", EndNum = "99999", Length = 5, CurrNum = "10000", IsNumber = true, PreType = PreType.YMDHm, FixPreChar = "", IsHasPre = true, CacheSpace = 10, Descript = "销售订单号流水" });
///生成另外一种销售订单编码 年的是取后两位 按每秒顺序生成 如22060214581200001-22060214581299999
list.Add(new Hi_Snro { SNRO = "SALENO", SNUM = 2, IsSnow = false, SnowTick = 0, StartNum = "10000", EndNum = "99999", Length = 5, CurrNum = "10000", IsNumber = true, PreType = PreType.Y2MDHms, FixPreChar = "", IsHasPre = true, CacheSpace = 10, Descript = "销售订单号流水" });
///通过雪花ID生成
list.Add( new Hi_Snro { SNRO = "Order", SNUM = 1, IsSnow=true, SnowTick=145444, StartNum = "", EndNum = "",Length=7, CurrNum = "", IsNumber = true, IsHasPre = false, CacheSpace = 10, Descript = "订单号雪花ID" });
//保存配置到表中
sqlClient.Modi("Hi_Snro", list).ExecCommand();
编号生成代码环境配置
建议把SeriNumber 做成一个局静态变量
public static SeriNumber number = null; //定义个全局变更的流水号对象
设置连接
//sqlClient 为数据库连接对象
number = new SeriNumber(sqlClient);
如果应用进行了分布式布署 请一定要启用以下代码
HiSql.Global.RedisOn = true;//开启redis缓存
HiSql.Global.RedisOptions = new RedisOptions { Host = "172.16.80.178", PassWord = "qwe123", Port = 6379, CacheRegion = "HRM", Database = 2 };
HiSql.Global.NumberOptions.MultiMode= true;
//如果部署了分布式且也要生成雪花ID 一定要配置以下当前应用的ID 多个应用间只要不重复即可0-31之间
HiSql.Global.NumberOptions.WorkId=1;
通过以上配置则分布式编号环境配置完成,下面就可以进行编号测试了
编号测试
为了测试编号是否有重复我这里建了一个测试记录表H_nlog
,编号生成完成后可以分析该表的数据看是否有重复数据,(测试环境 为SqlServer
) sql代码如下
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[H_nlog](
[Nid] [int] IDENTITY(1,1) NOT NULL,
[Numbers] [varchar](50) NULL,
[CreateTime] [datetime] NULL,
[CreateName] [nvarchar](50) NULL,
[ModiTime] [datetime] NULL,
[ModiName] [nvarchar](50) NULL,
CONSTRAINT [PK_H_nlog] PRIMARY KEY CLUSTERED
(
[Nid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[H_nlog] ADD CONSTRAINT [DF_H_nlog_CreateTime] DEFAULT (getdate()) FOR [CreateTime]
GO
ALTER TABLE [dbo].[H_nlog] ADD CONSTRAINT [DF_H_nlog_CreateName] DEFAULT ('') FOR [CreateName]
GO
ALTER TABLE [dbo].[H_nlog] ADD CONSTRAINT [DF_H_nlog_ModiTime] DEFAULT (getdate()) FOR [ModiTime]
GO
ALTER TABLE [dbo].[H_nlog] ADD CONSTRAINT [DF_H_nlog_ModiName] DEFAULT ('') FOR [ModiName]
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'创建时间' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'H_nlog', @level2type=N'COLUMN',@level2name=N'CreateTime'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'创建人' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'H_nlog', @level2type=N'COLUMN',@level2name=N'CreateName'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'修改时间' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'H_nlog', @level2type=N'COLUMN',@level2name=N'ModiTime'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'修改人' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'H_nlog', @level2type=N'COLUMN',@level2name=N'ModiName'
GO
根据编号规则WFNO
子编号1
生成编号
List<object> lst = new List<object>();
for (int i = 0; i < 1000; i++)
{
var num = number.NewNumber("WFNO", 1);
lst.Add(new { Numbers = num });
Console.WriteLine(num);
}
sqlClient.Insert("H_nlog", lst).ExecCommand();
测试结果
启用了分布式多个应用同时对同一个编号规则产生编号不会产生重复编号。
怎样生成分布式的流水ID的更多相关文章
- 框架篇:分布式全局唯一ID
前言 每一次HTTP请求,数据库的事务的执行,我们追踪代码执行的过程中,需要一个唯一值和这些业务操作相关联,对于单机的系统,可以用数据库的自增ID或者时间戳加一个在本机递增值,即可实现唯一值.但在分布 ...
- Zookeeper命名服务——生成分布式有序且唯一id
生成分布式有序且唯一id的方法有很多种,使用zookeeper是比较简单的一种方法,只是生成的速度不高,这里只是一个借助zk的版本号生成分布式唯一且有序id的例子. ZkIdGenerator.jav ...
- 数据库分库分表(一)常见分布式主键ID生成策略
主键生成策略 系统唯一ID是我们在设计一个系统的时候常常会遇见的问题,下面介绍一些常见的ID生成策略. Sequence ID UUID GUID COMB Snowflake 最开始的自增ID为了实 ...
- 全局流水ID号生成的几种方法
这个问题源自于,我想找一个分布式下的ID生成器. 这个最简单的方案是,数据库自增ID.为啥不用咧?有这么几点原因,一是,会依赖于数据库的具体实现,比如,mysql有自增,oracle没有,得用序列, ...
- redis生成分布式id方案
分布式Id - redis方式 本篇分享内容是关于生成分布式Id的其中之一方案,除了redis方案之外还有如:数据库,雪花算法,mogodb(object_id也是数据库)等方案,对于redis来 ...
- (4.24)【mysql、sql server】分布式全局唯一ID生成方案
参考:分布式全局唯一ID生成方案:https://blog.csdn.net/linzhiqiang0316/article/details/80425437 分表生成唯一ID方案 sql serve ...
- 分布式全局唯一ID生成策略
为什么分布式系统需要用到ID生成系统 在复杂分布式系统中,往往需要对大量的数据和消息进行唯一标识.如在美团点评的金融.支付.餐饮.酒店.猫眼电影等产品的系统中,数据日渐增长,对数据库的分库分表后需要有 ...
- 常见分布式全局唯一ID生成策略
全局唯一的 ID 几乎是所有系统都会遇到的刚需.这个 id 在搜索, 存储数据, 加快检索速度 等等很多方面都有着重要的意义.工业上有多种策略来获取这个全局唯一的id,针对常见的几种场景,我在这里进行 ...
- 分库分表的 9种分布式主键ID 生成方案,挺全乎的
<sharding-jdbc 分库分表的 4种分片策略> 中我们介绍了 sharding-jdbc 4种分片策略的使用场景,可以满足基础的分片功能开发,这篇我们来看看分库分表后,应该如何为 ...
随机推荐
- IDEA安装配置Scala环境
这里有详细步骤:windows上 IntelliJ IDEA安装scala环境 详细 初学
- 关于recyclerview其item数据重复问题
查找方法(query)的list只定义对象,不实例化,等到要添加的时候,再new一个新的对象出来. 千万不要如下图这样,否则item显示出来的永远是最新数据. (这个bug找了两天,还是基本功不扎实, ...
- CCF201709-2公共钥匙盒改进版
问题描述 有一个学校的老师共用N个教室,按照规定,所有的钥匙都必须放在公共钥匙盒里,老师不能带钥匙回家.每次老师上课前,都从公共钥匙盒里找到自己上课的教室的钥匙去开门,上完课后,再将钥匙放回到钥匙盒中 ...
- vue中Promise对象用法
Promise.all([ 需要异步一起执行的方法---------先做的事 ]).then(res=>{ 后做的事(先做的事已经做好了) }) 举栗子: Promise.all([ this. ...
- 前端实现导出excel
结果: 将网页上拿到的数据导出成excel文件 实现: HTML代码 <div> <button type="button" onclick="expo ...
- Git---git的常用操作
git三种状态的转换 git状态切换时的常用命令 1. git管理工作目录 git init # 会增加.git文件夹 2. git的三种状态 工作区 暂存区 本地仓库 3. 提交到暂存区 git a ...
- 动手动脑3&课堂作业(四则运算与继承)
先上结果 Java程序会先把所有的静态模块提取出来优先执行 四则运算主程序代码 1 import java.util.Scanner; 2 3 4 public class main { 5 publ ...
- AndroidStudio中的模块依赖引入后用不了
------------恢复内容开始------------ 在Android开发中,com.android.tools.build:gradle 3.0 以下版本依赖在gradle 中的声明写法: ...
- Amazing!巧用 CSS 视差实现酷炫交互动效
本文将介绍利用 CSS 实现滚动视差效果的一个小技巧,并且,利用这个技巧来制作一些有意思的交互特效. 关于使用 CSS 实现滚动视差效果,在之前有一篇文章详细描述过具体方案 - CSS 实现视差效果, ...
- mmdetection获取最高map的epoch
自动从训练结果中获取最高的mAP所对应的epoch. <code>import json import os ''' :param work_dir 训练结果目录 :return 最好的m ...