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 竞争状态下如何解决库存的正确减少("超卖"问题) 对于第一个问题,已经很容易想到 ...
随机推荐
- Java中原始数据类型存放位置理解
原始数据类型的变量存放在栈还是堆,应该由上下文去决定. 如下所示的局部方法中,定义了本地变量a,且为原始数据类型,所以存放在栈中. public void func(){ int a = 3; } 再 ...
- Android MTP 文件浏览Demo
本apk实现了MTP文件浏览的简单功能. 通过Demo apk能够浏览连接到当前设备上的MTP设备上的文件. Demo路径:http://download.csdn.net/detail/sailin ...
- C#趣味程序---九九乘法表
using System; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { for ...
- 正确移除List中对象
list是一个ArrayList的对象,哪个选项的代码填到//todo delete处.能够在Iterator遍历的过程中正确并安全的删除一个list中保存的对象?() Iterator it = l ...
- apple air装双系统(win7)
同事买了一个apple air.用不习惯,希望再装个win7,经过多次试验,得到例如以下操作方法: 1.在MAC系统里的"有用工具"中找到"Boot Camp 助理 ...
- MAC Wireshark抓包IOS
网络抓包是个基础技能,对于网络协议的掌握有一定的要求.iOS上实现网络抓包可以用Charles(针对http和https),tcpdump(快速分析网络包),和Wireshare.之前写过一篇介绍tc ...
- C++对象模型——Template中的名称决议方式 (第七章)
Template中的名称决议方式 (Name Resolution within a Template) 必须可以区分下面两种意义,一种是C++ Standard所谓的"sope of th ...
- 20170621_oracle练习
========================= 启动Oracle ========================= --->启动OracleOraDb11g_home1TNSListene ...
- 2018.2.24Test总结
T1(luogu3434) comment:水题,考试时我想的是开一个数组在读入时预处理出该长度什么时候会被拦住,但这样数组开不下,剩下只能模拟. 实际上应该把圆筒变成递减序列,再二分该长度即可. T ...
- Android Studio笔记
1. toolbar xml: <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:la ...