.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 }

执行结果

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

备注

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

 

 

你可能感兴趣的:(.net)