dotNET Core实现分布式环境下的流水号唯一

640?wx_fmt=jpeg

业务背景

在管理系统中,很多功能模块都会涉及到各种类型的编号,例如:流程编号、订单号、合同编号等等。编号各有各自的规则,但通常有一个流水号来确定编号的唯一性,保证流水号的唯一,在不同的环境中实现方式有所不同。本文将介绍在单机和分布式环境中保证流水号唯一的方式。

实现思路

1、在数据库中创建 seqno 表,每个业务一条数据,存储业务 code 和流水号的最大值

环境

  • dotNET Core:2.1

  • VS For Mac:2019

  • Docker:18.09.2

  • MySql:8.0.17,基于Docker构建

  • Redis:3.2,基于Docker构建

  • CSRedisCore:3.1.5

准备工作

1、执行下面命令构建 Redis 容器

docker run -p 6379:6379  -d --name s2redis_test   --restart=always redis:3.2   redis-server --appendonly yes

2、执行下面命令构建 MySql 容器

docker run -d -p 3306:3306 -e MYSQL_USER="oec2003" -e MYSQL_PASSWORD="123456" -e MYSQL_ROOT_PASSWORD="123456" --name s2mysql mysql/mysql-server --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci --default-authentication-plugin=mysql_native_password

3、在 MySql 中创建数据库seqno_test,执行下面 SQL 创建表和测试数据

-- ----------------------------
-- Table structure for seqno
-- ----------------------------
DROP TABLE IF EXISTS `seqno`;
CREATE TABLE `seqno` (`code` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,`num` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;-- ----------------------------
-- Records of seqno
-- ----------------------------
BEGIN;
INSERT INTO `seqno` VALUES ('order', 1);
COMMIT;SET FOREIGN_KEY_CHECKS = 1;

4、在 VS2019 中创建两个控制台项目和一个类库项目,如下图:

640?wx_fmt=png

单机测试

1、在 SeqNo 类中添加 GetSeqByNoLock 方法

public static string GetSeqNoByNoLock()
{string connectionStr = "server = localhost; user id = oec2003; password = 123456; database = seqno_test";string getSeqNosql = "select num from seqno where code='order'";string updateSeqNoSql = "update seqno set num=num+1 where code='order'";var seqNo = MySQLHelper.ExecuteScalar(connectionStr, System.Data.CommandType.Text, getSeqNosql);MySQLHelper.ExecuteNonQuery(connectionStr, System.Data.CommandType.Text, updateSeqNoSql);return seqNo.ToString();
}

2、在 RedisLockConsoleApp1 控制台程序中用多线程来模拟测试

class Program
{static void Main(string[] args){Task.Run(() =>{for (int i = 0; i < 50; i++){Console.WriteLine($"Thread1:SeqNo:{SeqNo.GetSeqNoByNoLock()}");}});Task.Run(() =>{for (int i = 0; i < 50; i++){Console.WriteLine($"Thread2:SeqNo:{SeqNo.GetSeqNoByNoLock()}");}});Console.ReadLine();}
}

3、测试结果如下,可以看出在多线程情况下会出现重复的编号

640?wx_fmt=png

单机环境加锁测试

在 SeqNo 类中添加 GetSeqNoByLock 方法,通过 Monitor.Enter 来解决单机多线程流水号重复问题

public static string GetSeqNoByLock()
{string connectionStr = "server = localhost; user id = oec2003; password = 123456; database = seqno_test";string getSeqNosql = "select num from seqno where code='order'";string updateSeqNoSql = "update seqno set num=num+1 where code='order'";var seqNo = string.Empty;try{Monitor.Enter(_myLock);seqNo = MySQLHelper.ExecuteScalar(connectionStr, System.Data.CommandType.Text, getSeqNosql).ToString();MySQLHelper.ExecuteNonQuery(connectionStr, System.Data.CommandType.Text, updateSeqNoSql);Monitor.Exit(_myLock);}catch{Monitor.Exit(_myLock);}return seqNo.ToString();
}

运行结果如下,可以看出已经没有出现重复的流水号了

640?wx_fmt=png

多机环境测试

Monitor 只能解决进程内的重复性问题,现在用两个控制台程序来模拟分布式下的多机器运行,在 RedisLockConsoleApp2 控制台程序添加如下代码

static void Main(string[] args)
{Task.Run(() =>{for (int i = 0; i < 50; i++){Console.WriteLine($"Thread1:SeqNo:{SeqNo.GetSeqNoByLock()}");}});Task.Run(() =>{for (int i = 0; i < 50; i++){Console.WriteLine($"Thread2:SeqNo:{SeqNo.GetSeqNoByLock()}");}});Console.ReadLine();
}

同时运行两个控制台程序,测试结果如下:

640?wx_fmt=png

可以看出在每一个控制台程序内没有重复流水号,但两个控制台还是会间歇性地出现重复流水号。

要解决这个问题就必须使用分布式锁。

多机环境分布式锁测试

分布式锁又很多实现方式,本例中采用 Redis 来实现,Redis 客户端使用的是 CSRedisCore ,在 CSRedisCore 最新的版本 3.1.5 中实现了分布式锁,这让使用变得非常的方便。

1、在 RedisLockLib 项目中添加 CSRedisCore 包的引用

640?wx_fmt=png

2、在 SeqNo 类中添加 GetSeqNoByRedisLock 方法

public static string GetSeqNoByRedisLock()
{string connectionStr = "server = localhost; user id = oec2003; password = 123456; database = seqno_test";string getSeqNosql = "select num from seqno where code='order'";string updateSeqNoSql = "update seqno set num=num+1 where code='order'";var seqNo=string.Empty;using (_redisClient.Lock("test", 5000)){seqNo = MySQLHelper.ExecuteScalar(connectionStr, System.Data.CommandType.Text, getSeqNosql).ToString();MySQLHelper.ExecuteNonQuery(connectionStr, System.Data.CommandType.Text, updateSeqNoSql);}return seqNo;
}

3、测试结果如下:

640?wx_fmt=png

总结

例子非常简单,提供一种解决问题的思路,如您有更好的方式欢迎讨论。本文的示例代码已上传 Github ,地址如下:

https://github.com/oec2003/StudySamples/tree/master/RedisLockDemo

祝大家假期快乐!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/313816.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

单位根反演小记

单位根反演 一个等式&#xff1a;[n∣a]1n∑k0n−1wnak[n \mid a] \frac{1}{n} \sum\limits_{k 0} ^{n - 1}w_n ^{ak}[n∣a]n1​k0∑n−1​wnak​ 证明&#xff1a; wnaw_n ^ awna​是nnn次单位根的aaa次方&#xff0c;所以这里是一个公比为wnaw_n ^ awna​的等比数列&…

.NET Core3发布Json API

我们给DNC3&#xff08;.NET Core 3&#xff09;上了一个新包&#xff0c;叫做System.Text.Json(点我下载)&#xff0c;支持读写器&#xff0c;DOM&#xff08;文档对象模型&#xff09;&#xff0c;和序列化&#xff0c;在这篇博文里&#xff0c;我会告诉大家为什么要做这个&a…

Java ThreadLocal

** 一&#xff1a;ThreadLocal的简要介绍及使用 ** Java中的ThreadLocal类允许我们创建只能被同一个线程读写的变量。因此&#xff0c;如果一段代码含有一个ThreadLocal变量的引用&#xff0c;即使两个线程同时执行这段代码&#xff0c;它们也无法访问到对方的ThreadLocal变…

P5591 小猪佩奇学数学(单位根反演)

P5591 小猪佩奇学数学 ∑i0n(in)pi⌊ik⌋⌊ik⌋i−i%kk1k∑i0n(in)pi(i−i%k)1k∑i0n(in)pii−1k∑i0n(in)pi(imodk)\sum_{i 0} ^{n} (_i ^ n) \times p ^ i \times \lfloor \frac{i}{k} \rfloor\\ \lfloor \frac{i}{k} \rfloor \frac{i - i \% k}{k}\\ \frac{1}{k} \sum_{i …

认证方案之初步认识JWT

前言&#xff1a;现在越来越多的项目或多或少会用到JWT&#xff0c;为什么会出现使用JWT这样的场景的呢&#xff1f;假设现在有一个APP&#xff0c;后台是分布式系统。APP的首页模块部署在上海机房的服务器上&#xff0c;子页面模块部署在深圳机房的服务器上。此时你从首页登录…

Java实现生产消费模型的5种方式

** 前言 ** 生产者和消费者问题是线程模型中的经典问题&#xff1a;生产者和消费者在同一时间段内共用同一个存储空间&#xff0c;生产者往存储空间中添加产品&#xff0c;消费者从存储空间中取走产品&#xff0c;当存储空间为空时&#xff0c;消费者阻塞&#xff0c;当存储…

C#.NET 一颗璀璨的全能明星

C# 是微软推出的一种基于.NET框架的、面向对象的高级编程语言&#xff0c;她可以做什么呢&#xff1f;1.桌面开发&#xff0c;WinForm/GUI可视化编程&#xff1a;Windows开发中的葵花宝典&#xff0c;霸主地位至今无出其右&#xff0c;开发效率令人发指&#xff0c;大部分营销软…

#3328. PYXFIB(单位根反演)

#3328. PYXFIB ∑i0⌊nk⌋CnikFik∑i0nCniFi[i≡0(modk)]i≡0(modk)&#xff0c;单位根反演有1k∑j0k−1wkij1k∑i0nCniFi∑j0k−1wkij\sum_{i 0} ^{\lfloor \frac{n}{k} \rfloor} C_{n} ^{i \times k} \times F_{i \times k}\\ \sum_{i 0} ^{n} C_n ^{i} \times F_i \times …

Leetcode 86. 分隔链表

给定一个链表和一个特定值 x&#xff0c;对链表进行分隔&#xff0c;使得所有小于 x 的节点都在大于或等于 x 的节点之前。你应当保留两个分区中每个节点的初始相对位置。示例:输入: head 1->4->3->2->5->2, x 3输出: 1->2->2->4->3->5题目分析…

深入理解 JVM Class文件格式(一)

** 一、JVM体系结构 ** ** 二、class格式文件概述 ** class文件是一种8位字节的二进制流文件&#xff0c; 各个数据项按顺序紧密的从前向后排列&#xff0c; 相邻的项之间没有间隙&#xff0c; 这样可以使得class文件非常紧凑&#xff0c; 体积轻巧&#xff0c; 可以被J…

min_25 推导及例题总结

min_25 筛 一个亚线性筛&#xff0c;复杂度大概是O(n34log⁡n)O(\frac{n ^{\frac{3}{4}}}{ \log n})O(lognn43​​)。 使用min_25min\_25min_25求前缀和&#xff0c;有两个基本特征&#xff1a;① 积性函数&#xff0c;② 满足质数点为多项式。 算法思路 给定n≤1011n \leq…

asp.net core 使用 signalR(一)

asp.net core 使用 signalR&#xff08;一&#xff09;IntroSignalR 是什么&#xff1f;ASP.NET Core SignalR 是一个开源代码库&#xff0c;它简化了向应用添加实时 Web 功能的过程。实时 Web 功能使服务器端代码能够即时将内容推送到客户端。SignalR 的适用对象&#xff1a;需…

深入理解 JVM Class文件格式(二)

** class文件中的特殊字符串 ** 特殊字符串是常量池中符号引用的一部分&#xff0c;包括三种&#xff1a; 类的全限定名&#xff0c; 字段和方法的描述符&#xff0c; 特殊方法的方法名。 下面我们就分别介绍这三种特殊字符串。 &#xff08;1&#xff09; 类的全限定名 在…

P4211 [LNOI2014]LCA(离线 + 在线 做法)

P4211 [LNOI2014]LCA 有一棵根节点为111树&#xff0c;有mmm次询问&#xff0c;每次给定l,r,zl, r, zl,r,z&#xff0c;输出∑ilrdep[lca(i,z)]\sum\limits_{i l} ^{r} dep[lca(i, z)]il∑r​dep[lca(i,z)]。 乍一看这题好像无从下手&#xff0c;仔细想想lca(i,z)lca(i, z)l…

.NET框架之“小马过河”

.NET框架之“小马过河”有许多流行的 .NET框架&#xff0c;大家都觉得挺“重”&#xff0c;认为很麻烦&#xff0c;重量级&#xff0c;不如其它“轻量级”框架&#xff0c;从而不愿意使用。面对形形色色的框架发愁&#xff0c;笔者也曾发愁。但我发现只要敢于尝试&#xff0c;这…

深入理解 JVM Class文件格式(三)

** JVM常量池中各数据项类型详解 ** 关于常量池的大概内容&#xff0c; 已经在 深入理解 JVM Class文件格式&#xff08;一&#xff09; 中讲解过了&#xff0c; 这篇文章中还介绍了常量池中的11种数据类型。 本文的任务是详细讲解这11种数据类型&#xff0c; 深度剖析源文件…

#6073. 「2017 山东一轮集训 Day5」距离(树链剖分 + 永久标记主席树)

#6073. 「2017 山东一轮集训 Day5」距离 给定一颗有nnn个节点带边权的树&#xff0c;以及一个排列ppp&#xff0c;path(u,v)path(u, v)path(u,v)为u,vu, vu,v路径上的点集&#xff0c;dist(u,v)dist(u, v)dist(u,v)为u,vu, vu,v之间的最短路的长度。 有mmm次询问&#xff0c;…

ML.NET 示例:搜索引擎结果排名

ML.NET 示例中文版&#xff1a;https://github.com/feiyun0112/machinelearning-samples.zh-cn/edit/master/samples/csharp/getting-started/Ranking_Web英文原版请访问&#xff1a;https://github.com/dotnet/machinelearning-samples/tree/master/samples/csharp/getting-st…

深入理解 JVM Class文件格式(四)

&#xff08;3&#xff09;CONSTANT_Integer_info 一个常量池中的CONSTANT_Integer_info数据项, 可以看做是CONSTANT_Integer类型的一个实例。 它存储的是源文件中出现的int型数据的值。 同样&#xff0c; 作为常量池中的一种数据类型&#xff0c; 它的第一个字节也是一个tag值…

.Net Core中使用Quartz.Net Vue开即用的UI管理

Quartz.NETQuartz.Net 定制UI维护了常用作业添加、删除、修改、停止、启动功能&#xff0c;直接使用cron表达式设置作业执行间隔&#xff0c;有完整的日志记录。Quartz.NET是一个功能齐全的开源作业调度系统&#xff0c;可用于从最小的应用程序到大型企业系统。Quartz.NET是一个…