.NET:可扩展的单据编号生成器 之 基于缓冲区的顺序号

背景

我在上篇文章“.NET:可扩展的单据编号生成器 之 顺序号(防止重复)”中介绍了如何使用“种子表”和“悲观锁”解决顺序号的问题。昨天找朋友讨论,说这种速度不够高,今天就稍微改进一下,引入一个内存缓冲区,提高生成的速度。

思路

引入内存缓冲区后,顺序号的生产流程变为:在内存中维护一个顺序号区间,在这个区间内,就直接查内存,否则更新种子表并重新更新内存区间。还是直接看代码吧。

实现

代码下载:http://yunpan.cn/Q5jj5yedRAtk5

SeedCodeRuleProvider.cs

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Text;
  5 using System.Threading.Tasks;
  6 
  7 using System.Transactions;
  8 using System.Text.RegularExpressions;
  9 using System.Data.Entity.Infrastructure;
 10 
 11 namespace EntityCodeRuleDemo
 12 {
 13     public class SeedCodeRuleProvider : ICodeRuleProvider
 14     {
 15         private static readonly int _OnceBufferSize = 10000;
 16         private static readonly Dictionary<string, BufferSeed> _Buffer = new Dictionary<string, BufferSeed>();
 17 
 18         private readonly int _width;
 19 
 20         public SeedCodeRuleProvider(int width)
 21         {
 22             _width = width;
 23         }
 24 
 25         public string Generate(object entity)
 26         {
 27             return GetSeedValue(entity).ToString().PadLeft(_width, '0');
 28         }
 29 
 30         protected virtual string GetKey(object entity)
 31         {
 32             return entity.GetType().FullName;
 33         }
 34 
 35         private int GetSeedValue(object entity)
 36         {
 37             var key = this.GetKey(entity);
 38 
 39             lock (_Buffer)
 40             {
 41                 if (!_Buffer.ContainsKey(key))
 42                 {
 43                     this.SetBufferSeed(entity, key);
 44                 }
 45             }
 46 
 47             lock (_Buffer[key])
 48             {
 49                 if (_Buffer[key].IsOverflow())
 50                 {
 51                     this.SetBufferSeed(entity, key);
 52                 }
 53 
 54                 _Buffer[key].CurrentValue++;
 55 
 56                 return _Buffer[key].CurrentValue;
 57             }
 58         }
 59 
 60         private void SetBufferSeed(object entity, string key)
 61         {
 62             var value = this.GetOrSetSeedValueFormDatabase(entity);
 63 
 64             _Buffer[key] = new BufferSeed
 65             {
 66                 CurrentValue = value - _OnceBufferSize,
 67                 MaxValue = value
 68             };
 69         }
 70 
 71         private int GetOrSetSeedValueFormDatabase(object entity)
 72         {
 73             var key = this.GetKey(entity);
 74 
 75             try
 76             {
 77                 using (var ts = new TransactionScope(TransactionScopeOption.RequiresNew,
 78                     new TransactionOptions { IsolationLevel = IsolationLevel.RepeatableRead }))
 79                 {
 80                     var value = 0;
 81 
 82                     using (var context = new TestContext())
 83                     {
 84                         var seed = context.CodeSeeds.Where(x => x.Key == key).FirstOrDefault();
 85                         if (seed == null)
 86                         {
 87                             seed = new CodeSeed { Id = Guid.NewGuid(), Key = key, Value = -1 };
 88                             context.CodeSeeds.Add(seed);
 89                         }
 90 
 91                         seed.Value += _OnceBufferSize;
 92                         context.SaveChanges();
 93 
 94                         value = seed.Value;
 95                     }
 96 
 97                     ts.Complete();
 98 
 99                     return value;
100                 }
101             }
102             catch (DbUpdateException)
103             {
104                 return this.GetSeedValue(entity);
105             }
106         }
107 
108         public static SeedCodeRuleProvider SeedCodeRuleProviderFactory(string literal)
109         {
110             var match = new Regex("^<种子(:(?<宽度>.*?))?>$").Match(literal);
111 
112             var width = match.Groups["宽度"].Value;
113 
114             return new SeedCodeRuleProvider(string.IsNullOrEmpty(width) ? 5 : int.Parse(width));
115         }
116 
117         private class BufferSeed
118         {
119             public int CurrentValue { get; set; }
120 
121             public int MaxValue { get; set; }
122 
123             public bool IsOverflow()
124             {
125                 return this.CurrentValue >= this.MaxValue;
126             }
127         }
128     }
129 }

Program.cs

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 using System.Diagnostics;
 8 using System.Text.RegularExpressions;
 9 
10 namespace EntityCodeRuleDemo
11 {
12     class Program
13     {
14         static void Main(string[] args)
15         {
16             CodeRuleInterpreter.RegistProviderFactory(new Regex("^<种子(:(?<宽度>.*?))?>$"), SeedCodeRuleProvider.SeedCodeRuleProviderFactory);
17 
18             var generator = CodeRuleInterpreter
19                 .Interpret("前缀_<日期:yyyy_MM_dd>_<属性:NamePinYin>_<种子:6>");
20 
21             var watch = Stopwatch.StartNew();
22 
23             for (var i = 0; i < 10000; i++)
24             {
25                 generator.Generate(new Employee { NamePinYin = "DUANGW" });
26             }
27 
28             watch.Stop();
29 
30             Console.WriteLine("1万条编号用时:" + watch.Elapsed);
31         }
32     }
33 }

执行结果

备注

 优化前后,速度相差几百倍。

原文地址:https://www.cnblogs.com/happyframework/p/3077095.html