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 竞争状态下如何解决库存的正确减少("超卖"问题) 对于第一个问题,已经很容易想到 ...
随机推荐
- 怎样编译和安装memcached
怎样编译和安装memcached 编译和安装步骤: $ apt-get install git $ git clone https://github.com/memcached/memcache ...
- Workspace in use or cannot be created, choose a different one.--错误解决的方法
eclipse 使用一段时间后.有时会由于一些故障自己就莫名奇异的关闭了,再打开时有时没有问题,有时有会提示错误 Workspace Unavailable: Workspace in use or ...
- C#之线程和线程池(Thread和ThreadPool类)
注:要使用此方法都需要引入应用:using System.Threading;//引入应用 参数意义:将要执行的方法排入队列以便执行,WaitCallback,即表示将要执行的方法:Object,包含 ...
- apache在windows下的命令安装与报错解决
1.在windows下能够通过执行apache的exe文件就能够,但当我们打包的时候,就须要命令来安装apache.apache在windows下用命令下的安装为: apache.exe -k ins ...
- ios--plist
// // main.m // 03-plist文件的回顾 // // Created by xiaomage on 15/12/29. // Copyright © 2015年 小码哥. All r ...
- git分支的合并和冲突解决【转】
本文转载自:http://blog.csdn.net/Kingson_Wu/article/details/39227611 http://gitbook.liuhui998.com/3_3.html ...
- IOS系统控件高度
以下是常见的几种控件的高度.Statusbar,Navigationbar和Tabbar的宽度极其图标大小. 下表是更为详细的参数,包括了Statusbar,Navigationbar.Tabbar. ...
- XAML实例教程系列 - XAML传递参数到值转换类实例 八
Kevin Fan分享开发经验,记录开发点滴 XAML实例教程系列 - XAML传递参数到值转换类实例 2012-06-28 05:25 by jv9, 508 阅读, 0 评论, 收藏, 编辑 继上 ...
- web跨域问题回顾
晚上看spring web源码时看到了cors包,查了一下原来是在4.2之后新加的用来更方便让web应用服务支持cors协议的.于是有了下面几个问题. web跨域问题的起源是因为浏览器为了安全而遵循的 ...
- min-max容斥小结
https://www.zybuluo.com/ysner/note/1248287 定义 对于一个集合\(S\), \(\min(S)\)表示其第一个出现的元素(\(or\)最小的元素), \(\m ...