前言

有不少业务场景下需要生成流水号,比较简单的情况是新建一个表,然后利用自增Id来实现。但这种实现弊端比较大,占用数据库资源,操作步骤也多。于是想到使用Redis更好,因为是基于内存操作的,速度快且具有原子性。

实现

新建项目RedisDemo
image.png
image.png
新建接口ISequenceNumberGenerator

  1. using System.Threading.Tasks;
  2. namespace RedisDemo
  3. {
  4. public interface ISequenceNumberGenerator
  5. {
  6. Task<long> GenerateAsync(string key);
  7. Task<string> GenerateAsync(string key, int totalWidth);
  8. }
  9. }

通过Nuget引入类库StackExchange.Redis
新建实现类SequenceNumberGenerator

  1. using Microsoft.Extensions.Configuration;
  2. using StackExchange.Redis;
  3. using System.Threading.Tasks;
  4. namespace RedisDemo
  5. {
  6. public class SequenceNumberGenerator : ISequenceNumberGenerator
  7. {
  8. protected ConnectionMultiplexer RedisConnection { get; }
  9. public SequenceNumberGenerator(IConfiguration configuration)
  10. {
  11. RedisConnection = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]);
  12. }
  13. protected virtual IDatabase GetDatabase()
  14. {
  15. return RedisConnection.GetDatabase();
  16. }
  17. public async Task<long> GenerateAsync(string key)
  18. {
  19. var db = GetDatabase();
  20. return await db.StringIncrementAsync(key);
  21. }
  22. public async Task<string> GenerateAsync(string key, int totalWidth)
  23. {
  24. var incrementValue = await GenerateAsync(key);
  25. return incrementValue.ToString().PadLeft(totalWidth, '0');
  26. }
  27. }
  28. }

代码写完开始测试
新建xunit测试项目
image.png
将自带的类文件重命名为SequenceNumberGenerator_Test

  1. using Microsoft.Extensions.Configuration;
  2. using RedisDemo;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Threading;
  7. using System.Threading.Tasks;
  8. using Xunit;
  9. using Xunit.Abstractions;
  10. namespace RedisDemoTest
  11. {
  12. public class SequenceNumberGenerator_Test
  13. {
  14. private readonly ISequenceNumberGenerator _sequenceNumberGenerator;
  15. private readonly ITestOutputHelper _outputHelper;
  16. public SequenceNumberGenerator_Test(ITestOutputHelper outputHelper)
  17. {
  18. IConfigurationBuilder builder = new ConfigurationBuilder();
  19. builder.AddInMemoryCollection(new Dictionary<string, string>
  20. {
  21. { "Redis:Configuration","127.0.0.1:6379"},
  22. });
  23. IConfigurationRoot configurationRoot = builder.Build();
  24. _sequenceNumberGenerator = new SequenceNumberGenerator(configurationRoot);
  25. _outputHelper = outputHelper;
  26. }
  27. [Fact]
  28. public async Task Generate_Test()
  29. {
  30. string key = "orderNumber" + Guid.NewGuid();
  31. string number = await _sequenceNumberGenerator.GenerateAsync(key, 4);
  32. Assert.Equal("0001", number);
  33. }
  34. [Fact]
  35. public void Generate2_Test()
  36. {
  37. string key = "orderNumber" + Guid.NewGuid();
  38. Parallel.For(1, 1000, async _ =>
  39. {
  40. long number = await _sequenceNumberGenerator.GenerateAsync(key);
  41. _outputHelper.WriteLine("number:" + number);
  42. });
  43. }
  44. }
  45. }

运行测试
image.png
image.png
可以看到实现没有问题。