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 竞争状态下如何解决库存的正确减少("超卖"问题) 对于第一个问题,已经很容易想到 ...
随机推荐
- Asp.Net页面生命周期[转]
一.什么是Asp.Net页面生命周期 当我们在浏览器地址栏中输入网址,回车查看页面时,这时会向服务器端(IIS)发送一个request请求,服务器就会判断发送过来的请求页面, 完全识别 HTTP 页 ...
- HDU——1133 Buy the Ticket
Buy the Ticket Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) T ...
- FreeMarker与Servlet结合示例
一.最原始示例 1.引入POM依赖 <!-- https://mvnrepository.com/artifact/org.freemarker/freemarker --> <de ...
- C++中设计一个类,使其不能继承
1.C++11中已经有了final关键字:它的作用是指定类的虚函数不能被该类的继承类覆盖(override),或者是指定一个类成为一个不能被继承的类(final class) 2.派生类用调用基类的构 ...
- vim 快速搜索的快捷键
当光标在某个单词上面的时候 按 shift + #键(或 shift + * )就可以了!!! ----------------------------------- If you are worki ...
- java中打印数组的5种方法
Arrays.toString(arr) for(int n: arr) System.out.println(n+", "); for (int i = 0; i < ar ...
- C++学习之new与delete、malloc与free
在C/C++的面试时,对于new/delete和malloc/free这两对的使用和区别经常被考查到,如果这种基础的问题都答不上来,估计很难过面试了.这篇文章仅仅是浅显的讲一下,仅供参考. 一.new ...
- [dfs] UVALive 3667 Ruler
题目链接: option=com_onlinejudge&Itemid=8&page=show_problem&problem=1668">https://ic ...
- C# DateTime.Now和DateTime.UtcNow的区别
DateTime.UtcNow.ToString()输出的是0时区的事件(通俗点就是格林威治时间的当前时间),DateTime.Now.ToString()输出的是当前时区的时间,我们中国使用的是东八 ...
- 深入理解 JBoss 7/WildFly Domain 模式启动过程
概述 JBoss 7/WildFly 以 domain 模式启动时会启动多个 JVM.比如例如以下通过启动脚本启动 domain 模式: ./domain.sh 启动后我们查看进程: [kylin@l ...