在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
例如流水号格式如下: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); 测试后不难发现,在高并发下很容易就出现重复。 好像哪里不对啊,修改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; 终于成功了。 当然这种处理方式也有不好的地方。 比如当生成流水号最终没有使用,会造成浪费。
最后 感谢阅读,希望可以帮到你。也欢迎留言指正文中的错误与不足,大家共同进步。
|
请发表评论