分布式锁实现(mysql,以及redis)以及分布式的概念
道生一,一生二,二生三,三生万物
我旁边的一位老哥跟我说,你知道分布式是是用来干什么的嘛?一句话给我干懵了,我能隐含知道,大概是用来做分压处理的,并增加系统稳定性的。但是具体如何,我却道不出个1,2,3。现在就将这些做一个详细的总结。至少以后碰到面试官可以说上个123。
那么就正式进入正题:
文章目录
- 道生一,一生二,二生三,三生万物
- 分布式概念
- 分布式锁
- 分布式锁实现的方案
- 数据库实现(Mysql)
- 方式1:乐观锁
- 方式2:读写锁的实现
- 第一步:链接数据库
- 第二部:建立数据库表
- 第三步:读方法(读操作)
- 读加锁操作
- 读解锁操作
- 第四步:写方法(写操作)
- 写加锁
- 写解锁
- redis实现
分布式概念
分布式是一种,将一个大问题拆分成多个小问题,并分别由多个节点协同完成的计算机解决方案。
既然是解决问题,那么是解决什么呢?
分布式的目的:是为了解决单个机器无法满足性能要求的问题
至于为什么单个机器无法满足,其实回头细想,现代的计算数据都是突出一个字,
大,这个大字就意味着处理数据的运算速度也得大,但是因为现代材料局限,一个主机的运算的能力有限,然而数据是成指数倍的增长,为了解决这个问题就是增加主机的数量,但是这就出现了另一个问题:->:如何将这些主机有机结合起来?分布式就是解决这个问题的。其实很多公司其实都不需要分布式,但是,时代进步的。不管现在是不是要用,但是未来的事情谁又说的清?
这个是网图,但是我认为这个是可以将现在大部分的分布式知识做一个较为全面的总结的

分布式这种框架的诞生,伴随着许多的问题,包括硬件,也包括软件,比如:
- 数据一致性
- 数据乱序
- 数据丢失
- 分库分表的扩容
- …
问题可以说是非常多。
但是大部分是可以用技术手段去避免很多的问题。而分布式加锁就是一个较为有效的手段。不过就是牺牲了一部分的时效性,但是对于人类来说,这点时间的损耗,就是一个眨眼的功夫。希望日后会出现更多手段,去解决这方面的问题。
接下来就是对于分布式锁介绍
分布式锁
分布式锁的应用场景,这些既是应用场景也是问题所在:
- 处理缓存击穿
- 处理缓存雪崩
- 重试处理
- 幂等性
- 数据不一致的问题
- …
基本上很多分布式应用上的问题基本可以用分布式锁处理
那么什么算是一个合格的分布式锁呢?众多巨人的文章中无不透露以下这几点:
互斥性:这个是锁的基本功能,即:同一时刻只允许一个客户端持有锁避免死锁:获取到锁的客户端出现问题了,没有办法解锁,所以要避免死锁。让系统继续运行下去(有些也叫安全性)容错:既然是服务器,那么提供锁的系统也是有可能崩溃的,所以要保证这一点。
这些属性,将会贯穿这篇文章。
分布式锁实现的方案
这些我会举出大体的实现思路,并不会全部去实现。
🌝每种实现类型中都有不同实现方式 ,比如mysql有悲观锁和乐观锁,读写锁这些方式的实现方式
常见的实现方案有:
- 数据库实现(我这里用的是mysql)
- redis实现
- zookeeper实现
- Redlock 算法实现
这里都会说到,但是对于实现来说,我目前就是只说MySQL的实现,redis的实现
语言对于程序员来说只是说某种工具而言,真正重要的是逻辑(算法)和数据结构,这个才是一个程序员安生立命的本钱。
所以这篇文章我会用go语言实现,其他语言的版本这里就不多说了。但是我会将常见的语言包说出,方便友友们能够快速查到相关的资料。
数据库实现(Mysql)
这里我用MySQL去实现:
技术:golang,gorm,数据结构
方式1:乐观锁
实现方式:通过对数据表添加一个字段 Version实现(数据版本(Version)记录机制实现)
主要逻辑:为数据库表增加一个数字类型的 version 字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一
如果对于跟新操作,需要先判断当前version与数据库中的version版本号是否对应,对应的上就允许跟新,诺是不相同,就会导致冲突,此时就更新失败。
是不是很简单?是的确实很简单。画个图解释一下


那么悲观锁如何实现我相信大家肯定也就明白了。但是这里我就浅浅提一点:
在sql语句后添加for update
逻辑实现:
- 通过添加线程做轮询等待,然后抢锁
- 添加过期时间
- 更新版本号
接下来重点是读写锁的实现
方式2:读写锁的实现
而这两种锁的实现,需要满足一下几个特点:
- 执行操作的环境是分布式的(当然单机不是不能用)
- 读操作,不做限制,里面资源是共享的。可以支持多个线程或者协程对资源的读取的操作。
- 写操作,是互斥的,也就是说明一个时刻只允许一个协程或者进程对资源进行访问。
- 读操作,写操作两者是互斥的。不能同时存在
ps:相当于对于读写这两个操作来说,都有自己的申请锁和解锁的方法,读模式共享,写模式互斥
读写锁的有点在于:
- 分布式读写锁是比分布式锁粒度更小的锁
- 对于业务场景更加灵活
所以综上的出:读写锁的状态有:读加锁状态、写加锁状态、无锁状态
| 当前锁状态 | 读锁请求 | 写锁请求 |
|---|---|---|
| 无锁状态 | 可以 | 可以 |
| 读锁状态 | 可以 | 不可以 |
| 写锁状态 | 不可以 | 不可以 |
第一步:链接数据库
每个程序员的链接手法各不相同,所以这里就不献丑了。只要连上数据库就好。然后将客户端暴露出来。
第二部:建立数据库表
要包含一下这几个字段。
var (statusUnLock = "Unlock"statusReadLock = "ReadLock"statusWirteLock = "WirteLock"
)type RWLock struct {
//表示某条数据加锁的状态,只能是读锁、写锁、无锁状态中的一种,默认状态为无锁状态LockStatus string `gorm:"default:'Unlock'"` //ReadLockCount 字段则记录当前并发访问的 goroutine(可以理解成线程) 数量ReadLockCount uint32 `gorm:"default:0"` LockReason string //记录当前加锁的原因
}// Stock 存储锁
type Stock struct {gorm.ModelRWLockCount int64
}func (Stock) TableName() string {return "stock"
}
这三个是状态值,分别代表:无锁,读锁,写锁
statusUnLock = “Unlock”
statusReadLock = “ReadLock”
statusWirteLock = “WirteLock”
gorm.Model中包含了:一下字段:
//主键idID uint `gorm:"primarykey"`//创建时间CreatedAt time.Time//更新时间UpdatedAt time.Time//删除时间,软删除DeletedAt DeletedAt `gorm:"index"`
这里我们通过的方式是,建立一个锁表来管理整个锁。相应的字段的功能我这里就不做赘述,在代码中已经有了。
第三步:读方法(读操作)
读加锁操作
// ReadLock 读锁
func (s Stock) ReadLock(ctx context.Context, db *gormX.DataBD, lockReason string) error {fields := map[string]interface{}{"lock_status": statusReadLock,"read_lock_count": gorm.Expr("read_lock_count + ?", 1),"lock_reason": lockReason,}//将所属的id锁的写状态改为读状态result := db.DB(ctx).Model(&Stock{}).Where("id=? AND lock_status=?", s.ID, statusWirteLock).Updates(fields)if result.Error != nil {return result.Error}if result.RowsAffected == 0 {return errors.New("重入锁失败,受影响的行数为 0")}return nil
}
我将对这里的代码做一个解释:
context.Context:上下文,用来管理请求
db *gormX.DataBD: 用来处理mysql连接的
lockReason:对每个锁进行备注
这里的读锁就是做一个统计,统计有多少个线程,是读锁状态
result := db.DB(ctx).Model(&Stock{}).Where("id=? AND lock_status=?", s.ID, statusWirteLock).Updates(fields):
这个整个sql的翻译,{参数}
update stock
setlock_status={statusReadLock},read_lock_count =read_lock_count +1 ,//这个不是参数lock_reason={lockReason} where id={s.ID} and statusWirteLoc={statusWirteLock}
这里为什么是update呢?因为在这里gorm中,update没有的数据的话,会变成insert插入数据。其他语言在做的时候一定要注意。
读解锁操作
// UnReadLock 读解锁
func (s Stock) UnReadLock(ctx context.Context, db *gormX.DataBD, UnLockReason string) error {fields := map[string]interface{}{"read_lock_count": gorm.Expr("if(read_lock_count>0),read_lock_count-1,0"),"lock_status": gorm.Expr("if(lock_status < 1),?,?", statusUnLock, statusReadLock),"lock_reason": UnLockReason,}result := db.DB(ctx).Model(&Stock{}).Where("id=? and lock_status=?", s.ID, statusReadLock).UpdateColumns(fields)if result.Error != nil {return result.Error}if result.RowsAffected == 0 {return errors.New("解读锁失败,受影响的行数为 0")}return nil
}
这里将读操作做完的业务进行释放后,在表中做统计减少的操作。
我将对这里的代码做一个解释:
result := db.DB(ctx).Model(&Stock{}).Where("id=? and lock_status=?", s.ID, statusReadLock).UpdateColumns(fields)
update stock
setlock_status=(if(read_lock_count>0),read_lock_count-1,0),read_lock_count =(if(lock_status < 1),{statusUnLock},{statusReadLock}),lock_reason={lockReason} where id={s.ID} and statusWirteLoc={statusReadLock}
第四步:写方法(写操作)
写加锁
// WriteLock 写锁
func (s Stock) WriteLock(ctx context.Context, db *gormX.DataBD, lockReason string) error {fields := map[string]interface{}{"read_lock_count": 0,"lock_status": statusWirteLock,"lock_reason": lockReason,}result := db.DB(ctx).Model(&Stock{}).Where("id=? and lock_status=?", s.ID, statusUnLock).Updates(fields)if result.Error != nil {return result.Error}if result.RowsAffected == 0 {return errors.New("写入锁失败,受影响的行数为 0")}return nil
}
这里不对线程进行统计,因为这是互斥的。并将锁写入状态
我将对这里的代码做一个解释:
result := db.DB(ctx).Model(&Stock{}).Where("id=? and lock_status=?", s.ID, statusUnLock).Updates(fields)
update stock
setlock_status={statusWirteLock},read_lock_count =0 ,//这个不是参数lock_reason={lockReason} where id={s.ID} and statusWirteLoc={statusWirteLock}
这里为什么是update呢?因为在这里gorm中,update没有的数据的话,会变成insert插入数据。其他语言在做的时候一定要注意。
写解锁
// UnWriteLock 写解锁
func (s Stock) UnWriteLock(ctx context.Context, db gormX.DataBD, UnLockReason string) error {fields := map[string]interface{}{"read_lock_count": 0,"lock_status": statusUnLock,"lock_reason": UnLockReason,}result := db.DB(ctx).Model(&Stock{}).Where("id=? and lock_status=?", s.ID, statusWirteLock).Updates(fields)if result.Error != nil {return result.Error}if result.RowsAffected == 0 {return errors.New("解写锁失败,受影响的行数为 0")}return nil
}
这里不对线程进行统计,因为这是互斥的。并修改其状态
我将对这里的代码做一个解释:
result := db.DB(ctx).Model(&Stock{}).Where("id=? and lock_status=?", s.ID, statusWirteLock).Updates(fields)
update stock
setlock_status={statusUnLock},read_lock_count =0 ,//这个不是参数lock_reason={lockReason} where id={s.ID} and statusWirteLoc={statusWirteLock}
redis实现
今天太晚了,先不写,明天补上:连接
续:今天补上了:请看这篇文章
相关文章:
分布式锁实现(mysql,以及redis)以及分布式的概念
道生一,一生二,二生三,三生万物 我旁边的一位老哥跟我说,你知道分布式是是用来干什么的嘛?一句话给我干懵了,我能隐含知道,大概是用来做分压处理的,并增加系统稳定性的。但是具体如…...
实现分布式锁:Zookeeper vs Redis
目录 引言 1. Zookeeper分布式锁 1.1特点和优势: 强一致性 顺序节点 Watch机制 1.2 Zookeeper分布式锁代码示例 2. Redis分布式锁 2.1特点和优势: 简单高效 可续租性 灵活性 2.2Redis分布式锁代码示例 3.对比和选择 3.1 一致性要求 3.2…...
电脑录屏必备技能,让分享变得更加简单!
随着网络技术的飞速发展,电脑录屏已经成为日常工作和学习中不可或缺的一部分。无论是录制在线课程、游戏解说、软件教程,还是远程会议、演示文稿等,电脑录屏都有着广泛的应用。接下来,本文将介绍三种常见的电脑录屏方法࿰…...
重构改善既有代码的设计-学习(一):封装
1、封装记录(Encapsulate Record) 一些记录性结构(例如hash、map、hashmap、dictionary等),一条记录上持有什么字段往往不够直观。如果其使用范围比较宽,这个问题往往会造成许多困扰。所以,记录…...
Python图像处理【19】基于霍夫变换的目标检测
基于霍夫变换的目标检测 0. 前言1. 使用圆形霍夫变换统计图像中圆形对象2. 使用渐进概率霍夫变换检测直线2.1 渐进霍夫变换原理2.2 直线检测 3. 使用广义霍夫变换检测任意形状的对象3.1 广义霍夫变换原理3.2 检测自定义形状 小结系列链接 0. 前言 霍夫变换 (Hough Transform,…...
Spring+SprinMVC+MyBatis注解方式简易模板
SpringSprinMVCMyBatis注解方式简易模板代码Demo GitHub访问 ssm-tpl-anno 一、数据准备 创建数据库test,执行下方SQL创建表ssm-tpl-cfg /*Navicat Premium Data TransferSource Server : 127.0.0.1Source Server Type : MySQLSource Server Version :…...
Python基础第五篇(Python数据容器)
文章目录 一、数据容器入门二、数据容器 list 列表(1),list 列表定义(2),list列表的索引(3),list列表的常见操作(4),list列表的遍历 三、数据容器:tuple(元组)(1),tuple元组定义(2),tuple元组的索引(3),tuple元组的常见操作(4),tuple元组的遍…...
【H3C】配置AAA认证和Telnet远程登陆,S5130 Series交换机
AAA配置步骤为: 1.开启telent远程登陆服务 2.创建用户,设置用户名、密码、用户的服务类型 3.配置终端登录的数量 4.配置vlan-if的ip地址,用来远程登陆 5.允许对应的vlan通过 1.开启telent远程登陆服务 sys …...
GaussDB数据库中的MERGE INTO介绍
一、前言 二、GaussDB MERGE INTO 语句的原理概述 1、MERGE INTO 语句原理 2、MERGE INTO 的语法 3、语法解释 三、GaussDB MERGE INTO 语句的应用场景 四、GaussDB MERGE INTO 语句的示例 1、示例场景举例 2、示例实现过程 1)创建两个实验表,并…...
2024年上海高考数学最后四个多月的备考攻略,目标140+
亲爱的同学们,寒假已经来临,春节即将到来,距离2024年上海高考已经余额不足5个月了。作为让许多学子头疼,也是拉分大户的数学科目,你准备好了吗?今天,六分成长为您分享上海高考数学最后四个多月的…...
SSL证书自动化管理有什么好处?如何实现SSL证书自动化?
SSL证书是用于加密网站与用户之间传输数据的关键元素,在维护网络安全方面,管理SSL证书与部署SSL证书一样重要。定期更新、监测和更换SSL证书,可以确保网站的安全性和合规性。而自动化管理可以为此节省时间,并避免人为错误和不必要…...
路由器初始化配置、功能配置
实验环境 拓扑图 Ip规划表(各组使用自己的IP规划表) 部门 主机数量 网络地址 子网掩码 网关 可用ip Vlan 市场部 38 192.168.131.0 255.255.255.0 192.168.131.1 2-254 11 研发部 53 192.168.132.0 255.255.255.0 192.168.132.1 2-2…...
node介绍
1.node是什么 Node是一个基于Chrome V8引擎的JS运行环境。 Node不是一个独立的语言、node不是JS框架。 Node是一个除了浏览器之外的、可以让JS运行的环境 Node.js是一个让JS运行在服务端的开发平台,是使用事件驱动,异步非阻塞I/O,单线程&…...
海外抖音TikTok、正在内测 AI 生成歌曲功能,依靠大语言模型 Bloom 进行文本生成歌曲
近日,据外媒The Verge报道,TikTok正在测试一项新功能,利用大语言模型Bloom的AI能力,允许用户上传歌词文本,并使用AI为其添加声音。这一创新旨在为用户提供更多创作音乐的工具和选项。 Bloom 是由AI初创公司Hugging Fac…...
【ARM 嵌入式 编译系列 3.6 -- 删除lib中的某个文件】
请阅读【嵌入式开发学习必备专栏 之 ARM GCC 编译专栏】 文章目录 删除lib中的某个文件 删除lib中的某个文件 比如,如果要删除 libc.a 静态库中的特定对象文件并重新使用这个静态库,在终端中可以使用 ar 命令。ar 是一个归档工具,它可以创建…...
web架构师编辑器内容-图层拖动排序功能的开发
新的学习方法 用手写简单方法实现一个功能然后用比较成熟的第三方解决方案即能学习原理又能学习第三方库的使用 从两个DEMO开始 Vue Draggable Next: Vue Draggable NextReact Sortable HOC: React Sortable HOC 列表排序的三个阶段 拖动开始(dragstart&#x…...
3.RHCSA脚本配置及通过node2改密码
运行脚本发现node2不成功 脚本破解 选第二个 Ctrl x 换行 破解成功后做node2的改密码题 回到redhat, 发现检测程序检测密码题成功,得了8分....
AtCoder Regular Contest 170(A~B)
A - Yet Another AB Problem 给你两个字符串S和T,你可以对S执行操作,选择两个字符,将前面的改为A,后面的改为B,最少操作几次可以把S改成T。如果改不成就输出-1。 从左往右一个一个改过去,分类讨论&#x…...
rk1126, 实现 yolov8 目标检测
基于 RKNN 1126 实现 yolov8 目标检测 Ⓜ️ RKNN 模型转换 ONNX yolo export model./weights/yolov8s.pt formatonnx导出 RKNN 这里选择输出 concat 输入两个节点 onnx::Concat_425 和 onnx::Concat_426 from rknn.api import RKNNONNX_MODEL ./weights/yolov8s.onnxRKNN_MOD…...
【软件测试】学习笔记-网站可扩展性架构设计
可扩展性,指的是网站的架构设计能够快速适应需求的变化,当需要增加新的功能实现时,对原有架构不需要做修改或者做很少的修改就能够快速实现新的业务需求。 从这个定义中,我们很容易就可以得出衡量网站可扩展性设计优秀与否的主要标…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...
AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...
【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...
零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)
本期内容并不是很难,相信大家会学的很愉快,当然对于有后端基础的朋友来说,本期内容更加容易了解,当然没有基础的也别担心,本期内容会详细解释有关内容 本期用到的软件:yakit(因为经过之前好多期…...
视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...
uniapp手机号一键登录保姆级教程(包含前端和后端)
目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号(第三种)后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...
