MSSQL高并发下生成连续不重复的订单号
一、确定需求
只要做过开发的基本上都有做过订单,只要做过订单的基本上都要涉及生成订单号,可能项目订单号生成规则都不一样,但是大多数规则都是连续增长。
所以假如给你一个这样的需求,在高并发下,以天为单位,生成连续不重复的订单号,比如2017年4月12日有1000条订单,那么当天的订单号是170412001至1704121000,第二天13号又有2000条订单就是170413001至1704132000。
二、实现需求
首先我们建立一个订单表
CREATE TABLE [dbo].[tbOrder](
[ID] [int] IDENTITY(1,1) NOT NULL,
[OrderNo] [varchar](50) NULL,
[InputTime] [datetime] NULL,
CONSTRAINT [PK_tbOrder] PRIMARY KEY CLUSTERED
(
[ID] 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
表中只有自增ID,订单编号,录入时间三列。
然后开始在代码里面生成订单号。
public static string GetOrderNo()
{
string result = string.Empty;
using (IDbConnection conn = SqlHelper.OpenConnection())
{
string sql = "SELECT ISNULL(COUNT(*),0)+1 FROM tbOrder WHERE DATEDIFF(DAY,InputTime,GETDATE())=0";
int num = conn.ExecuteScalar<int>(sql);
if (num < )
{
result = num.ToString().PadLeft(, '');
}
else
{
result = num.ToString();
}
}
result = DateTime.Now.ToString("yyMMdd") + result;
return result;
}
接着我们开10个线程,每个线程都执行插入100次订单表,每次插入之前都从这个方法里获取订单编号。
static void Main(string[] args)
{
for (int i = ; i < ; i++)
{
Thread thread = new Thread(new ThreadStart(InserOrder));
thread.Start();
}
} public static void InserOrder()
{
using (IDbConnection conn = SqlHelper.OpenConnection())
{
for (int i = ; i < ; i++)
{
conn.Execute("INSERT INTO tbOrder(OrderNo,InputTime)VALUES(@OrderNo,GETDATE())", new { OrderNo = GetOrderNo() });
}
}
}
运行一下,看结果如何。
结果不出所料,一塌糊涂!
三、调整战略
因此,我们要改变思路和战略,重点是订单编号不能根据当前订单总数的基础上加1那么简单了,而是必须有一个ID池,给每次请求分发ID,用后即弃。
相当于去银行办理业务,进去就会让你去机器领号,叫到你的号码的时候才可以去办理业务。
那么谁来当这个ID池呢?
这里有三个方案:
1.SQL表
2.Redis的Incr
3.队列
这里我使用的第一种。
首先我们建立一张表,用来存放ID
CREATE TABLE [dbo].[tbDocID](
[PreName] [varchar](50) NOT NULL,--标识,用于区分不同的业务
[ID] [int] NOT NULL, --用于自增的列,每次用后自增加1
CONSTRAINT [PK_tbDocID] PRIMARY KEY CLUSTERED
(
[PreName] 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
然后创建一个存储过程,存储过程主要负责根据这张表返回ID
--根据前导字符获取ID值
--参数:前导字符
--返回:字符串
CREATE PROCEDURE [dbo].[sp_GetOrderNo]
(
@PreName varchar(20)
)
AS
BEGIN TRAN
SET NOCOUNT ON
--1、定义变量
Declare @ReturnValue varchar(10),@OrderID varchar(20),@ID int,@StrID varchar(10),@IDLen int
Declare @DocLen int
Set @DocLen=10 --2、取出当前ID值+1,然后更新当前的值
Select @ID=ID+1 From [tbDocID] WITH(ROWLOCK,UPDLOCK) where PreName=@PreName
IF ISNULL(@ID,0)=0 Set @ID=0 IF @ID=0
BEGIN
INSERT INTO [tbDocID]WITH(HOLDLOCK)(PreName,ID)VALUES(@PreName,0)
SET @ID=1
END
Update [tbDocID] Set ID=ID+1 where PreName=@PreName
--3、处理ID的长度
Set @StrID=convert(varchar(10),@ID)
Set @IDLen=Len(@StrID)
Select @StrID=CASE @IDLen
WHEN 1 THEN ''+@StrID
WHEN 2 THEN ''+@StrID
ELSE @StrID
End
Set @ReturnValue=@StrID
--4、返回
Set @OrderID=@ReturnValue
Select @OrderID as DocID
COMMIT TRAN
RETURN
GO
修改获取订单编号的方法,从存储过程中获取
public static string GetOrderNo(string prefix)
{
string result = string.Empty;
DynamicParameters param = new DynamicParameters();
param.Add("@PreName", prefix);
using (IDbConnection conn = SqlHelper.OpenConnection())
{
string returnValue = conn.Query<String>("sp_GetDocID", param, null, true, null, CommandType.StoredProcedure).FirstOrDefault();
if (!string.IsNullOrEmpty(returnValue))
{
result = returnValue;
}
}
result = DateTime.Now.ToString("yyMMdd") + result;
return result;
}
四、测试
最后一波测试
static void Main(string[] args)
{
for (int i = ; i < ; i++)
{
Thread thread = new Thread(new ThreadStart(InserOrder));
thread.Start();
}
} public static void InserOrder()
{
using (IDbConnection conn = SqlHelper.OpenConnection())
{
for (int i = ; i < ; i++)
{
string perfix = string.Format("ORDER_{0}", DateTime.Now.ToString("yyMMdd"));
conn.Execute("INSERT INTO tbOrder(OrderNo,InputTime)VALUES(@OrderNo,GETDATE())", new { OrderNo = GetOrderNo(perfix) });
}
}
}
结果:
作者:黄昌
出处:http://www.cnblogs.com/h-change/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。
MSSQL高并发下生成连续不重复的订单号的更多相关文章
- MSSQL 高并发下生成连续不重复的订单号
参考: https://www.cnblogs.com/h-change/p/6699683.html 这里在数据库层面生成的,经测试确实不会重复. 附上自己修改后的版本,这里也可以预先生成一年的记录 ...
- 采用redis生成唯一且随机的订单号
项目描述 最近做的一个项目有这么一个需求:需要生成一个唯一的11位的就餐码(类似于订单号的概念),就餐码的规则是:一共是11位的数字,前面6位是日期比如2019年07月20就是190720,后面五位是 ...
- 分布式高并发下全局ID生成策略
数据在分片时,典型的是分库分表,就有一个全局ID生成的问题.单纯的生成全局ID并不是什么难题,但是生成的ID通常要满足分片的一些要求: 1 不能有单点故障. 2 以时间为序,或者ID里包含时间 ...
- 海量数据和高并发下的 Redis 业务优化实践
本文内容是我在 6 月 23 日参加的深圳 GIAC 技术大会上演讲的文字稿. 观众朋友们,我是来自掌阅的工程师钱文品,掘金小册<Redis 深度历险>的作者.今天我带来的是分享主题是:R ...
- Java生鲜电商平台-生鲜电商高并发下的接口幂等性实现与代码讲解
Java生鲜电商平台-生鲜电商高并发下的接口幂等性实现与代码讲解 说明:Java生鲜电商平台-生鲜电商高并发下的接口幂等性实现与代码讲解,实际系统中有很多操作,是不管做多少次,都应该产生一样的效果或返 ...
- Java高并发下多线程编程
1.创建线程 Java中创建线程主要有三种方式: 继承Thread类创建线程类: 定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务.因此也把run方法称为 ...
- php结合redis实现高并发下的抢购、秒杀功能
抢购.秒杀是如今很常见的一个应用场景,主要需要解决的问题有两个:1 高并发对数据库产生的压力2 竞争状态下如何解决库存的正确减少("超卖"问题)对于第一个问题,已经很容易想到用缓存 ...
- (高级篇)php结合redis实现高并发下的抢购、秒杀功能
抢购.秒杀是如今很常见的一个应用场景,主要需要解决的问题有两个:1 高并发对数据库产生的压力2 竞争状态下如何解决库存的正确减少("超卖"问题)对于第一个问题,已经很容易想到用缓存 ...
- php结合redis实现高并发下的抢购、秒杀功能 (转载)
抢购.秒杀是如今很常见的一个应用场景,主要需要解决的问题有两个: 1 高并发对数据库产生的压力 2 竞争状态下如何解决库存的正确减少("超卖"问题) 对于第一个问题,已经很容易想到 ...
随机推荐
- [codevs 1482]路线统计(矩阵乘法)
题目:http://codevs.cn/problem/1482/ 分析:很像“经过K条边的最短路径条数”.但有所不同,那就是不是边数固定,而是路径总长度固定.看似不能用矩阵乘法了……但注意到每条边的 ...
- 一个性能较好的jvm參数配置以及jvm的简单介绍
一个性能较好的webserverjvm參数配置: -server //服务器模式 -Xmx2g //JVM最大同意分配的堆内存,按需分配 -Xms2g //JVM初始分配的堆内存.一般和Xmx配置成一 ...
- Linux 学习之路:认识shell和bash
一.shell 计算机硬件的直接控制者是操作系统的内核(kernel),因为内核的重要性,所以作为用户的我们是无法直接操作内核的,所以我们需要shell调用应用程序或者双击打开安装的应用软件与内核之 ...
- win32 API函数
cozy的博文 win32 API函数大全 (2008-03-15 16:28) 分类: 个人日记 1. API之网络函数 WNetAddConnection 创建同一个网络资源的永久性连接 WN ...
- Quartz在.Net网站中的使用方法(附Demo)
现在做一个B/S项目需要用到计划任务,本来想自定写一个的,写了几句,突然想看看网上有没有现成可以用的.结果在苦寻之下找到了Quartz这个组件.看过之后感觉还不错.决定用它实现计划任务了.再找找看有没 ...
- bzoj2300
http://www.lydsy.com/JudgeOnline/problem.php?id=2300 终于对了... 平衡树又写挂了...不要忘记清空原先的root和修改root... #incl ...
- unsigned 赋值负数输出情况 & printf输出格式
%d 有符号10进制整数 %ld 长整型 %hd短整型 %i 有符号10进制整数 %o 无符号8进制整数 %u 无符号10进制整数 %x 无符号的16进制数字,并以小写abcdef表示 %X 无符号的 ...
- astgo-完整功能介绍
核心功能: Astgo最核心和强大的功能是呼叫中心模块.接入方式:中继卡.模拟卡接入,中继网关.O口网关接入.网络IP接入等.单机200个坐席,通话实时录音.灵活队列分组.开放式IVR设计,修改业务逻 ...
- 对腾讯云服务器linux系统进行分区格式化操作
- PCB SQL SERVER 位运算应用实例
在PCB行业,一个产品可能同时在多个工厂生产,举例:一个产品一条主记录,这条记录中会对应多个工厂的产地,而这个工厂产地个数不确定, 那么如何设计表结构存储这个不确定的工厂呢?这里想到了4个方式存储 一 ...