怎样生成分布式的流水ID
阅读原文时间:2022年06月03日阅读:1

流水编号

日常在我们开发的过程中可能会用到编号的功能,如销售订单号,采购订单号,日志编号,凭证号…等等,为了保证唯一有些表的主键要么用自增长,要么用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();

测试结果

启用了分布式多个应用同时对同一个编号规则产生编号不会产生重复编号。

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章