C# 订单流水号生成
例如流水号格式如下:XX201604120001,2位前缀加8位日期加4位流水号
首先各种搜索出现如下解决方案
public class SerialNoHelper
{
/// <summary>
/// 生成流水号
/// </summary>
/// <param name="serialno">从数据库读取最大的流水号</param>
/// <returns></returns>
public String Generate(String serialno)
{
var today = DateTime.Today.ToString("yyyyMMdd"); if (String.IsNullOrEmpty(serialno))
return $"XX{today}0001"; var date = serialno.Substring(2, 8);
if (date == today)
{
var no = Convert.ToInt32(serialno.Substring(10));
return $"XX{today}{++no:0000}";
} return $"XX{today}0001";
}
}
然后测试
class Program
{
static void Main(string[] args)
{
//模拟数据库
var array = new List<String>(); //模拟订单号生成
var tasks = new Task[1000];
for (var i = 0; i < tasks.Length; i++)
{
tasks[i] = Task.Run(() =>
{
var helper = new SerialNoHelper(); var sno = array.LastOrDefault();//模拟从数据库读取最大的流水号 var serialno = helper.Generate(sno);
//各种逻辑操作
array.Add(serialno);//模拟保存到数据库 Console.WriteLine(serialno);
});
} //等待执行完成
Task.WaitAll(tasks); Console.WriteLine("-----------------------------------分割线-----------------------------------"); //测试是否重复
var repeat = array.GroupBy(m => m).Where(m => m.Count() > 1).Select(m => m.Key).ToList();
foreach (var item in repeat)
Console.WriteLine(item); Console.ReadLine();
}
}
测试后不难发现,在高并发下很容易就出现重复。
好像哪里不对啊,修改SerialNoHelper类实现单例,然后给Generate方法加锁。这下应该可以了吧。
public sealed class SerialNoHelper
{
private static volatile SerialNoHelper helper;
private static readonly Object syncRoot = new Object(); private SerialNoHelper()
{
} public static SerialNoHelper Helper
{
get
{
if (helper == null)
{
lock (syncRoot)
{
if (helper == null)
helper = new SerialNoHelper();
}
}
return helper;
}
} /// <summary>
/// 生成流水号
/// </summary>
/// <param name="serialno">从数据库读取最大的流水号</param>
/// <returns></returns>
public String Generate(String serialno)
{
lock (syncRoot)
{
var today = DateTime.Today.ToString("yyyyMMdd"); if (String.IsNullOrEmpty(serialno))
return $"XX{today}0001"; var date = serialno.Substring(2, 8);
if (date == today)
{
var no = Convert.ToInt32(serialno.Substring(10));
return $"XX{today}{++no:0000}";
} return $"XX{today}0001";
}
}
}
心情忐忑的按下F5,WTF,居然还是有重复。
不慌,走到窗口猛吸两口雾霾压压惊。接下来分析一下为什么还是会出现重复呢?
生成序列号的时候依赖的是从数据库获取最大的流水号,但是在生成序列号之后,到保存序列号到数据库这之间一般会有一些逻辑操作。
这就导致在高并发的时候,前一个流水号还没有保存到数据库,那就有可能从数据库获取到的流水号是相同的,那么生成的流水号自然就会出现重复。
怎么解决这个问题呢?在Generate方法内就把生成的流水号保存到数据库?这显然不太合适,上面提到保存流水号到数据库一般会有一些逻辑操作。
最终版本
public sealed class SerialNoHelper
{
private static volatile SerialNoHelper helper;
private static readonly Object syncRoot = new Object(); private static String lastdate;
private static Int32 lastno; private SerialNoHelper()
{
} public static SerialNoHelper Helper
{
get
{
if (helper == null)
{
lock (syncRoot)
{
if (helper == null)
helper = new SerialNoHelper();
}
}
return helper;
}
} /// <summary>
/// 生成流水号
/// </summary>
/// <param name="serialno">从数据库读取最大的流水号</param>
/// <returns></returns>
public String Generate(String serialno)
{
lock (syncRoot)
{
var today = DateTime.Today.ToString("yyyyMMdd"); if (today == lastdate)
return $"XX{today}{++lastno:0000}"; lastdate = today;
lastno = 0;
if (!String.IsNullOrEmpty(serialno) && serialno.Substring(2, 8) == today)
lastno = Convert.ToInt32(serialno.Substring(10)); return $"XX{today}{++lastno:0000}";
}
}
}
终于成功了。
当然这种处理方式也有不好的地方。
比如当生成流水号最终没有使用,会造成浪费。
最后
感谢阅读,希望可以帮到你。也欢迎留言指正文中的错误与不足,大家共同进步。
C# 订单流水号生成的更多相关文章
- .net EF 事物 订单流水号的生成 (二):观察者模式、事物、EF
针对.net EF 事物 订单流水号的生成 (一) 的封装. 数据依然不变. using System; using System.Linq; using System.Transactions; ...
- SQLSERVER之高灵活的业务单据流水号生成
SQLSERVER之高灵活的业务单据流水号生成 最近的工作中要用到流水号,而且业务单据流水号生成的规则分好几种,并非以前那种千篇一律的前缀+日期+流水号的简单形式,经过对业务的分析,以及参考网上程序员 ...
- 通过序列号Sequence零代码实现订单流水号
序列号管理 本文通过产品编码和订单流水号介绍一下序列号(Sequence)在crudapi中的应用. 概要 序列号 MySQL数据库没有单独的Sequence,只支持自增长(increment)主键, ...
- Winform通用模块之流水号生成
打算接下来的时间里把自己觉得用起来还比较好用的通用模块,在这里向大家介绍一下,如果你有更好的想法时,也希望你不吝指点. 1.数据库表及存储过程 在介绍这个通用流水号生成的模块前,我们先来看一下其相关的 ...
- Java订单号生成,唯一订单号(日均千万级别不重复)
Java订单号生成,唯一订单号 相信大家都可以搜索到很多的订单的生成方式,不懂的直接百度.. 1.订单号需要具备以下几个特点. 1.1 全站唯一性. 1.2 最好可读性. 1.3 随机性,不能重复,同 ...
- 偶尔在网上看到的,相对比较好的c#端订单号生成规则
偶尔在网上看到的,相对比较好的c#端订单号生成规则 public class BillNumberBuilder{ private static object locker = new obj ...
- .net EF 事物 订单流水号的生成 (一)
首先需要 添加 System.Transactions 程序集 数据表: create table SalesOrder ( ID ,) primary key not null, OrderNo ) ...
- 业务订单号生成算法,每秒50W左右,不同机器保证不重复,包含日期可读性好
参考snowflace算法,基本思路: 序列12位(更格式化的输出后,性能损耗导致每毫秒生成不了这么多,所以可以考虑减少这里的位,不过留着也并无影响) 机器位10位 毫秒为左移 22位 上述几个做或运 ...
- 分布式系统-主键唯一id,订单编号生成-雪花算法-SnowFlake
分布式系统下 我们每台设备(分布式系统-独立的应用空间-或者docker环境) * SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作 ...
随机推荐
- 使用JS的FormData对象
利用FormData对象,你可以使用一系列的键值对来模拟一个完整的表单,然后使用XMLHttpRequest发送这个"表单". 创建一个FormData对象 你可以先创建一个空的F ...
- English is very important!
Well, as a college student,I haven't realized how important the English is . But as a web programmer ...
- nodejs爬虫系统
其中express是服务端框架 request相当于前端的ajax请求 cheerio相当于jq 开始 首先我们先新建一个 crawler目录 执行 npm install express -g 命令 ...
- uva757 - Gone Fishing(馋)
题目:uva757 - Gone Fishing(贪心) 题目大意:有N个湖泊仅仅有一条通路将这些湖泊相连. 每一个湖泊都会给最開始5分钟间隔内能够调到的鱼(f).然后给每过5分钟降低的鱼的数量(d) ...
- CQRS 示例
CQRS 示例 上一篇:<IDDD 实现领域驱动设计-CQRS(命令查询职责分离)和 EDA(事件驱动架构)> 学习架构知识,需要有一些功底和经验,要不然你会和我一样吃力,CQRS.EDA ...
- 源码安装saltstack的时候遇到的问题
公司的系统都是内网,无法连接互联网,所以没办法只有源码安装了. 看了下saltstack的官网,需要安装的包有 https://docs.saltstack.com/en/latest/topics/ ...
- 怎样下载并编译Android4.0内核源代码goldfish(图文)
关于怎样下载Android4.0源代码,请查看我的博客内还有一篇文章(相同是图文教程): http://blog.csdn.net/flydream0/article/details/7036156 ...
- Java清理临时目录文件Demo(一)
/** * 删除单个文件 * * @param sPath * 被删除文件的文件名 * @return 单个文件删除成功返回true,否则返回false */ public static boolea ...
- 使用OpenCV玩家营造出一个视频控制(没有声音)
说明:OpenCV计算机视觉库,所以使用的图像或视频处理,因此,没有任何声音在播放视频的临时 软件:使用OpenCV制播放器(无声音) 功能说明:新建播放窗体.加入进度条能够拖动视频播放. 流程图: ...
- SQLServer 2005 数据库定阅复制实现双机热备(主要是sharepoint 内容数据库)
原文:SQLServer 2005 数据库定阅复制实现双机热备(主要是sharepoint 内容数据库) 场景 公司最近的sharepoint的数据库服务器老是出问题,并且在一旦出现问题,就导致无法正 ...