分布式锁(redis/mysql)
单台机器所能承载的量是有限的,用户的量级上万,基本上服务都会做分布式集群部署。很多时候,会遇到对同一资源的方法。这时候就需要锁,如果是单机版的,可以利用java等语言自带的并发同步处理。如果是多台机器部署就得要有个中间代理人来做分布式锁了。
常用的分布式锁的实现有三种方式。
基于redis实现(利用redis的原子性操作setnx来实现)
基于mysql实现(利用mysql的innodb的行锁来实现,有两种方式, 悲观锁与乐观锁)
基于Zookeeper实现(利用zk的临时顺序节点来实现)
目前,我已经是用了redis和mysql实现了锁,并且根据应用场景应用在不同的线上环境中。zk实现比较复杂,又无应用场景,有兴趣的可以参考他山之石中的《Zookeeper实现分布式锁》。
说说心得和体会。
基于redis缓存实现分布式锁
基于redis的锁实现比较简单,由于redis的执行是单线程执行,天然的具备原子性操作,我们可以利用命令setnx和expire来实现,java版代码参考如下:
包名和获取redis操作对象换成自己的就好了。
基本步骤是
每次进来先检测一下这个key是否实现。如果失效了移除失效锁
使用setnx原子命令争抢锁。
抢到锁的设置过期时间。
步骤2为最核心的东西,为啥设置步骤3?可能应为获取到锁的线程出现什么移除请求,而无法释放锁,因此设置一个最长锁时间,避免死锁。为啥设置步骤1?redis可能在设置expire的时候挂掉。设置过期时间不成功,而出现锁永久生效。
线上环境,步骤1、3的问题都出现过。所以要做保底拦截。
redis集群部署
通常redis都是以master-slave解决单点问题,多个master-slave组成大集群,然后通过一致性哈希算法将不同的key路由到不同master-slave节点上。
redis锁的优缺点:
优点:redis本身是内存操作、并且通常是多片部署,因此有这较高的并发控制,可以抗住大量的请求。缺点:redis本身是缓存,有一定概率出现数据不一致请求。
在线上,之前,利用redis做库存计数器,奖品发放理论上只发放10个的,最后发放了14个。出现了数据的一致性问题。
因此在这之后,引入了mysql数据库分布式锁。
基于mysql实现的分布式锁。
实现第一版
在此之前,在网上搜索了大量的文章,基本上都是 插入、删除发的方式或是直接通过"select for update"这种形式获取锁、计数器。具体可以参考他山之石中的《分布式锁的几种实现方式~》关于数据库锁章节。
一开始,我的实现方式伪代码如下:
这样实现出现了很严重的死锁问题,具体原因可以可以参考他山之石中的《select for update引发死锁分析》这个版本中存在如下几个比较严重的问题:
1.通常线上数据是不允许做物理删除的2.通过唯一键重复报错,处理错误形式是不太合理的。3.如果appclient在处理中还没释放锁之前就挂掉了,会出现锁一直存在,出现死锁。4.如果以这种方式,实现redis中的计数器(incr decr),当记录不存在的时候,会出现大量死锁的情况。
因此考虑引入,记录状态字段、中央锁概念。
实现第二版
在第二版中完善了数据库表设计,参考如下:
在这个版本中,考虑到再条锁并发插入存在死锁(间隙锁争抢)情况,引入中央锁概念。
基本方式是:
根据sql创建好数据库
创建一条记录Flock_name="center_lock"的记录。
在对其他锁(如Flock_name="sale_invite_lock")进行操作的时候,先对"center_lock"记录select for update
"sale_invite_lock"记录自己的增删改查。
考虑到不同公司引入的数据库操作包不同,因此提供伪代码,以便于理解伪代码
到此,该方案,能够满足我的分布式锁的需求。
但是该方案,有一个比较致命的问题,就是所有记录共享一个锁,并发并不高。
经过测试,开启50*100个线程并发修改,5次耗时平均为8秒。
实现第三版
由于方案二,存在共享同一把中央锁,并发不高的请求。参考concurrentHashMap实现原理,引入分段锁概念,降低锁粒度。
基本方式是:
根据sql创建好数据库
创建100条记录Flock_name="center_lock_xx"的记录(xx为00-99)。
在对其他锁(如Flock_name="sale_invite_lock")进行操作的时候,根据crc32算法找到对应的center_lock_02,先对"center_lock_02"记录select for update
"sale_invite_lock"记录自己的增删改查。
伪代码如下:
经过测试,开启50*100个线程并发修改,5次耗时平均为5秒。相较于版本二几乎有一倍的提升。
至此,完成redis/mysql分布式锁、计数器的实现与应用。
最后
根据不同应用场景,做出如下选择:
高并发、不保证数据一致性:redis锁/计数器
低并发、保证数据一致性:mysql锁/计数器
低并发、不保证数据一致性:你随意
高并发。保证数据一致性:redis锁/计数器 + mysql锁/计数器。
表数据和记录:
推荐一个交流学习群:753650956 里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多:
本文来源于网络
作者:林湾村龙猫
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
为什么要写技术文章-我对写作收获的理解
为了迎接更好的自己。 过去的止步不前 程序员最反感别人没写文档,最不喜欢自己写文档。 我一直很认同技术人员应该持续写技术文章,可以总结经验,打造个人品牌,等等。但加上公司内部分享,实际也没写多少篇,这可能也是很多技术人员的通病吧。 对我个人来说,没有坚持写首先是时间因素。并不是真的没有一点时间,而是时间管理任务规划的问题。对自身认知不足,制定了太多没想清楚的计划。经常键盘一敲,这个月要写三篇技术分享,洋洋洒洒做了一堆计划很有满足感,好像已经成功了一半。但做计划时没规划好落地细节,比如什么时候写,写什么方向的内容。总有更紧急的事情要做,于是转眼就一个月过去了,没写的等以后再写吧。很快,一年也过去了。 其次,有时想真正沉下心写点东西,又没有什么特别想写的内容。个人也看了很多别人的技术文章,像复杂系统的介绍,各种开源产品的使用心得、架构、源码分析
- 下一篇
Tomcat架构解析之1 架构简介
Tomcat除了能够支撑通常的web app外,其本身高度模块化的架构体系,也能带来最大限度的可扩展性。 目前tomcat版本已经衍生到tomcat9,但是主流的版本还是tomcat6。此系列架构体系介绍还是以tomcat6为蓝本。 Tomcat是有一系列逻辑模块组织而成,这些模块主要包括: 核心架构模块,例如Server,Service,engine,host和context及wrapper等 网络接口模块connector log模块 session管理模块 jasper模块 naming模块 JMX模块 权限控制模块 …… 这些模块会在相关的文档里逐一描述,本篇文档以介绍核心架构模块为主。 1 核心架构模块说明 核心架构模块之间是层层包含关系。 例如可以说Service是Server的子组件,Server是Service的父组件。 在server.xml已经非常清晰的定义了这些组件之间的关系及配置。 需要强调的是Service中配置了实际工作的Engine,同时配置了用来处理时间业务的线程组Executor(如果没有配置则用系统默认的WorkThread模式的线程组),以及处理网...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7,8上快速安装Gitea,搭建Git服务器
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- CentOS关闭SELinux安全模块
- CentOS6,CentOS7官方镜像安装Oracle11G
- CentOS7设置SWAP分区,小内存服务器的救世主
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7