例如流水号格式如下: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. ABP展现层——Javascript函数库

    ABP展现层——Javascript函数库 点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之21.ABP展现层——Javascript函数库 ABP是“ASP.N ...

  2. 【核心研究】消息队列_MessageQueue

    消息队列排队过程中的消息.这第一条消息将首先被处理.但假设消息本身指定要处理的时间.我们必须等待,直到时间的消息处理能力.新闻MessageQueue正在使用Message类的表示,队列中的邮件保存结 ...

  3. Javascript学习4 - 对象和数组

    原文:Javascript学习4 - 对象和数组 在Javascript中,对象和数组是两种基本的数据类型,而且它们也是最重要的两种数据类型. 对象是已命名的值的一个集合,而数组是一种特殊对象,它就像 ...

  4. Web采矿技术

      一.数据挖掘 数据挖掘是运用计算机及信息技术,从大量的.不全然的数据集中获取隐含在当中的实用知识的高级过程.Web 数据挖掘是从数据挖掘发展而来,是数据挖掘技术在Web 技术中的应用.Web 数据 ...

  5. Mono 4 和Jexus 5.6

    Mono 4 和Jexus 5.6 概述 在这篇文章中我们将讨论如何在CentOS 7操作系统,安装 jexus. mono 和 配置 jexus,因此它将能够在这种环境中运行一个asp.net mv ...

  6. mysql_oracle_随机查询几条记录

    数据库的随机查询SQL 1. Oracle,随机查询20条 select * from ( select  *  from 表名 order by dbms_random.value ) where ...

  7. BIZTALK项目中WEB引用WEBSERVICES服务时候报错

    近期工作中须要完毕通过BIZTALK完毕调用WEBLOGIC公布的WebServices服务,环境搭建好后,打开VS开发工具新建一个BIZTALK项目,加入WEB引用将对方公布的地址拷贝上去,能够正常 ...

  8. linux下一个Oracle11g RAC建立(八)

    linux下一个Oracle11g RAC建立(八) 七.安装oracle软件   直接在图形界面里安装oracle.在node1操作 在虚拟机界面中,直接切换到oracle用户下: [grid@no ...

  9. hdu 5053 the Sum of Cube(上海网络赛)

    the Sum of Cube Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...

  10. PHP实例——输出安全的HTML代码

    原文:PHP实例--输出安全的HTML代码 //输出安全的htmlfunction h($text, $tags = null){ $text = trim($text); //完全过滤注释 $tex ...