例如流水号格式如下: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# 订单流水号生成的更多相关文章

  1. .net EF 事物 订单流水号的生成 (二):观察者模式、事物、EF

    针对.net EF 事物 订单流水号的生成 (一)  的封装. 数据依然不变. using System; using System.Linq; using System.Transactions; ...

  2. SQLSERVER之高灵活的业务单据流水号生成

    SQLSERVER之高灵活的业务单据流水号生成 最近的工作中要用到流水号,而且业务单据流水号生成的规则分好几种,并非以前那种千篇一律的前缀+日期+流水号的简单形式,经过对业务的分析,以及参考网上程序员 ...

  3. 通过序列号Sequence零代码实现订单流水号

    序列号管理 本文通过产品编码和订单流水号介绍一下序列号(Sequence)在crudapi中的应用. 概要 序列号 MySQL数据库没有单独的Sequence,只支持自增长(increment)主键, ...

  4. Winform通用模块之流水号生成

    打算接下来的时间里把自己觉得用起来还比较好用的通用模块,在这里向大家介绍一下,如果你有更好的想法时,也希望你不吝指点. 1.数据库表及存储过程 在介绍这个通用流水号生成的模块前,我们先来看一下其相关的 ...

  5. Java订单号生成,唯一订单号(日均千万级别不重复)

    Java订单号生成,唯一订单号 相信大家都可以搜索到很多的订单的生成方式,不懂的直接百度.. 1.订单号需要具备以下几个特点. 1.1 全站唯一性. 1.2 最好可读性. 1.3 随机性,不能重复,同 ...

  6. 偶尔在网上看到的,相对比较好的c#端订单号生成规则

    偶尔在网上看到的,相对比较好的c#端订单号生成规则 public class BillNumberBuilder{     private static object locker = new obj ...

  7. .net EF 事物 订单流水号的生成 (一)

    首先需要 添加 System.Transactions 程序集 数据表: create table SalesOrder ( ID ,) primary key not null, OrderNo ) ...

  8. 业务订单号生成算法,每秒50W左右,不同机器保证不重复,包含日期可读性好

    参考snowflace算法,基本思路: 序列12位(更格式化的输出后,性能损耗导致每毫秒生成不了这么多,所以可以考虑减少这里的位,不过留着也并无影响) 机器位10位 毫秒为左移 22位 上述几个做或运 ...

  9. 分布式系统-主键唯一id,订单编号生成-雪花算法-SnowFlake

    分布式系统下 我们每台设备(分布式系统-独立的应用空间-或者docker环境) * SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作 ...

随机推荐

  1. Tick and Tick------HDOJ杭州电(无法解释,直接看代码)

    Problem Description The three hands of the clock are rotating every second and meeting each other ma ...

  2. ASPxComboBox控件联动效果bug改进

    原文:ASPxComboBox控件联动效果bug改进 在应用第三方控件DevExpress控件的时候,大家应该对ASPxComboBox控件应该不是很陌生吧,尤其在做多级联动效果的时候,有着它独特的地 ...

  3. java 突击队注意事项:在路上

    情绪: 灵活:让标准成为价格值.为了给你一个想法和标准,你可以有一个不同的使用.不是死扣定理.决这个问题. 看书:分两类,一类依据知识点进行罗列.并且结构清晰,能够看完一章有选择进行总结(不是笔记,总 ...

  4. SSIS从理论到实战,再到应用

    原文:SSIS从理论到实战,再到应用 一,是什么(What?) 1.SSIS是Microsoft SQL Server Integration Services的简称,是生成高性能数据集成解决方案(包 ...

  5. 手机新闻网站,手持移动新闻,手机报client,jQuery Mobile手机新闻网站,手机新闻网站demo,新闻阅读器开发

    我们坐在地铁.经常拿出新浪手机查看新闻.腾讯新闻,或者看新闻,等刷微信功能.你有没有想过如何实现这些目标. 移动互联网.更活泼. 由于HTML5未来,jQuery Moblie未来. 今天我用jqm的 ...

  6. hdu 1002 Java 大数 加法

    http://acm.hdu.edu.cn/showproblem.php?pid=1002 PE   由于最后一个CASE不须要输出空行 import java.math.BigInteger; i ...

  7. 【转】Appium根据xpath获取控件实例随笔

    原文地址:http://blog.csdn.net/zhubaitian/article/details/39754233 如文章<Appium基于安卓的各种FindElement的控件定位方法 ...

  8. java阅读器hdfs单纯demo

    周围环境:eclipse + eclipse hadoop插入. hadoop + rhel6.4 package test; import java.io.IOException; import j ...

  9. Hack 语言学习/参考---1.3 Summary

    Summary Hack provides the following, non-exhaustive list of features: Ability to annotate function a ...

  10. Unofficial Microsoft SQL Server Driver for PHP (sqlsrv)非官方的PHP SQL Server 驱动

    原文 Unofficial Microsoft SQL Server Driver for PHP (sqlsrv) Here are unofficial modified builds of Mi ...