流水编号

日常在我们开发的过程中可能会用到编号的功能,如销售订单号,采购订单号,日志编号,凭证号...等等,为了保证唯一有些表的主键要么用自增长,要么用GUID值,或通过雪花ID算法生成。这此方式基本都能产生唯一的ID,但如果在分布式环境下产生流水ID,以上这几种方式可能就不太好用,如有以下场景

  1. 工作流的流水编号

工作流的编号通常会是以下格式 如2022060200001-2022060299999 到了第二天时尾数又要生00001开始编,这种编号规则有一个好处就是非常直观的通过工作流编号就可以看出来这是哪一天申请的流程,一天大概有多少流水码。那么如果我们用常规编号规则其实是比较难完成此需求的。

  1. 采购订单号

如在实际业务需求中 标准采购订单要用5000000000-5999999999 这个号进行进行编码,委外采购订单用4000000000-4999999999号段进行编码

  1. 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 编号长度 StartNumEndNum 的长度 两者的长度要一至否则会报错
IsNumber 是否纯数字编号 当为true 编号按0-9的10进制数字编号,当为false时按0-9 A-Z 36进度数字和字母混合编号
IsHasPre 是否有前辍 可以按到年月日时分秒作为前辍 在PreType 配置
PreType 前辍编号类型 详细请见 PreType 前置编号类型配置
FixPreChar 固定前辍 固定一个字符串 每个生成的码前面都加上这个值
PreChar 当前前辍 不需要配置 在编号的过程中会将 FixPreCharPreType 存在此昝
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的更多相关文章

  1. 框架篇:分布式全局唯一ID

    前言 每一次HTTP请求,数据库的事务的执行,我们追踪代码执行的过程中,需要一个唯一值和这些业务操作相关联,对于单机的系统,可以用数据库的自增ID或者时间戳加一个在本机递增值,即可实现唯一值.但在分布 ...

  2. Zookeeper命名服务——生成分布式有序且唯一id

    生成分布式有序且唯一id的方法有很多种,使用zookeeper是比较简单的一种方法,只是生成的速度不高,这里只是一个借助zk的版本号生成分布式唯一且有序id的例子. ZkIdGenerator.jav ...

  3. 数据库分库分表(一)常见分布式主键ID生成策略

    主键生成策略 系统唯一ID是我们在设计一个系统的时候常常会遇见的问题,下面介绍一些常见的ID生成策略. Sequence ID UUID GUID COMB Snowflake 最开始的自增ID为了实 ...

  4. 全局流水ID号生成的几种方法

    这个问题源自于,我想找一个分布式下的ID生成器.  这个最简单的方案是,数据库自增ID.为啥不用咧?有这么几点原因,一是,会依赖于数据库的具体实现,比如,mysql有自增,oracle没有,得用序列, ...

  5. redis生成分布式id方案

    分布式Id - redis方式   本篇分享内容是关于生成分布式Id的其中之一方案,除了redis方案之外还有如:数据库,雪花算法,mogodb(object_id也是数据库)等方案,对于redis来 ...

  6. (4.24)【mysql、sql server】分布式全局唯一ID生成方案

    参考:分布式全局唯一ID生成方案:https://blog.csdn.net/linzhiqiang0316/article/details/80425437 分表生成唯一ID方案 sql serve ...

  7. 分布式全局唯一ID生成策略

    为什么分布式系统需要用到ID生成系统 在复杂分布式系统中,往往需要对大量的数据和消息进行唯一标识.如在美团点评的金融.支付.餐饮.酒店.猫眼电影等产品的系统中,数据日渐增长,对数据库的分库分表后需要有 ...

  8. 常见分布式全局唯一ID生成策略

    全局唯一的 ID 几乎是所有系统都会遇到的刚需.这个 id 在搜索, 存储数据, 加快检索速度 等等很多方面都有着重要的意义.工业上有多种策略来获取这个全局唯一的id,针对常见的几种场景,我在这里进行 ...

  9. 分库分表的 9种分布式主键ID 生成方案,挺全乎的

    <sharding-jdbc 分库分表的 4种分片策略> 中我们介绍了 sharding-jdbc 4种分片策略的使用场景,可以满足基础的分片功能开发,这篇我们来看看分库分表后,应该如何为 ...

随机推荐

  1. MySQL8.0官方文档学习

    InnoDB架构 下面的架构里只挑选了部分内容进行学习 内存架构(In-Memory Structures) Buffer Pool Buffer Pool是内存中的一块区域,InnoDB访问表和索引 ...

  2. mysql基本操作2

    ##DDL控制表结构,不支持事务##DML控制表数据,支持事务       DQL专门做查询  ##TCL 管理事务##DCL 管理数据库权限     ##ORDER BY  子句-根据指定列对结果集 ...

  3. jboss7学习2-jboss7入门(端口和访问的ip问题)

    1.下载地址: http://www.jboss.org/jbossas/downloads ,下载Certified Java EE 6 Full Profile版本. 2.解压 jboss-as- ...

  4. this和super的区别和应用

    A:this和super都代表什么 * this:代表当前对象的引用,谁来调用我,我就代表谁 * super:代表当前对象父类的引用B:this和super的使用区别 * a:调用成员变量  * th ...

  5. 关于webpack,你想知道的都在这;

    咱也标题党一回 哈哈哈 要使用webpack优化项目打包构建速度,首先得知道问题出在哪, 要知道问题出在哪,首先得知道webpack 打包的基本原理才能针对性的去做优化,下面首先了解webpack基本 ...

  6. 基于PromiseA+规范实现一个promise

    实现如果下规范的promise Aplus规范 1,promise是一个类:有三个状态 pending/等待态 fulfilled/成功态 rejected/失败态 2,promise默认执行器立即执 ...

  7. 汇编语言实验1—Debug基础操作

    1.使用Debug,将下面的程序段写入内存,逐条执行,观察每条指令执行后CPU中相关寄存器中内容的变化. 记录1:最后一条指令执行完BX=(4026)H,AL=(66)H,检验结果. 两种写入:e命令 ...

  8. 以ARM和RISC-V为内核的单片机写寄存器

    我以为这是个很简单的问题,没想到还有一些初学者不会.可能他们也是跟我一样是直接学的如何操作单片机并没有学微机原理么. ARM和RISC-V的机器的系统架构都是哈佛结构的,意思是程序存储器.数据存储器和 ...

  9. 不借助 Javascript,利用 SVG 快速构建马赛克效果

    之前在公众号转发了好友 Vajoy 的一篇文章 -- 巧用 CSS 把图片马赛克风格化. 核心是利用了 CSS 中一个很有意思的属性 -- image-rendering,它可以用于设置图像缩放算法. ...

  10. python学习-Day18

    目录 今日内容详细 模块 循环导入问题 判断文件类型 py文件可以被分为两种类型 内置变量 __ name __ 模块的查找顺序 验证先从内存空间中查找 验证再从内置模块中查找 验证sys.path ...