当前位置: 首页 > 编程日记 > 正文

什么是分布式锁?Redis实现分布式锁详解

目录

前言:

分布式系统买票示例

引入redis做分布式锁

引入过期时间

引入校验id

引入lua脚本

过期时间续约问题

redlock算法

小结:


前言:

在分布式系统中,涉及多个主机访问同一块资源,此时就需要锁来做互斥控制,避免出现类似线程安全问题。而Java中的synchronized只是对当前进程中的线程有效,多个主机实际上是多个进程,那么它就无能为力了,此时就需要分布式锁。

分布式系统买票示例

客户端访问买票服务器进行买票操作,当买到票之后数据库余票执行减1操作。

客户端1先执行查询余票操作,发现余票还有一张,买票成功后服务器操作数据库执行减1操作。如果此时客户端1还没有执行数据库减1操作,客户端2执行了查询余票操作,发现余票也是一张,那么也进行数据库减1操作,那么此时客户端1和客户端2都会买到票。把一张票卖给了两个人,显然这是不合理的。

引入redis做分布式锁

引入分布式锁,所谓分布式锁实际上就是一个/一组单独的服务器程序,给其他服务提供 “加锁” 服务。redis是一种典型的实现分布式锁的方案,但不是唯一一种。

为了解决上述超卖问题,买票服务器在进行买票的时候,就需要先加锁。

加锁实际上就是在redis上设置一个特殊的键值对,完成上述买票操作再删除这个键值对。(此时认为这个键值对就是分布式锁)

其他服务器在买票的时候,也去redis尝试设置这样的键值对,如果发现键值对已经存在,就认为“加锁失败”,此时该进程是放弃还是阻塞就看具体的实现策略了。

这样就可以保证第一个服务器执行查询-更新的过程中,第二个服务器不会执行查询操作,也就解决了上述超卖问题。

注意:

上述买票的场景也可以使用MySQL的事务解决,批量执行查询-更新操作。但是分布式系统中数据库不一定是MySQL,也可能是其他数据库没有事务,因此使用redis作为分布式锁是比较好的解决方案。

引入过期时间

redis中使用 set nx 命令可以实现加锁效果,解锁使用 del 命令来完成。

如果某个服务器 set nx 成功了,还没有执行 del 命令就挂了,此时redis上的锁就无法删除,其他服务器就无法获取到锁。

解决方案:

可以在set key 的时候设置过期时间,一旦时间到锁自动就释放了。redis中可以使用 set ex nx命令完成。此时这个过期时间范围设置就显得尤为重要了,后面会有解释(过期时间续约问题)。

注意:

务必使用 set ex nx 命令一次性执行加锁和设置过期时间操作。如果使用set nx 设置锁,然后使用 expire 设置过期时间,就可能出现这两个命令一个成功一个失败,就算使用redis事务只能保证两条命令一块执行,但不能保证其正确性。相比之下一条命令直接操作就比较稳妥。

引入校验id

是否会出现服务器1执行了加锁操作,被其他服务器删除的情况呢?

这种情况有可能出现,代码总会有bug,需要提前去防止。

解决方案:

给服务器编号,每个服务器都有自己的唯一标识。进行加锁的时候,设置键值对.key对应要对哪个资源加锁(比如车次),value就可以存储服务器编号,标识出这个锁是哪个服务器加的。

解锁的时候就可以进行校验,先查询这个锁的服务器编号,和自己服务器编号进行对比。如果一致则执行解锁操作,否则就失败。服务器这边需要执行校验逻辑,此时就可以有效避免误解锁。

引入lua脚本

一个服务器内部也可能是多线程的,就有可能存在两个线程执行上述判断然后解锁操作。由于不是原子的,就有可能出现问题。

服务器1的线程A和线程B都执行解锁操作,如果线程A拿到锁判断完后,还没有执行DEL操作,此时线程B也拿到锁判断也会成功,那么就会执行两次DEL操作。

如果在线程A执行DEL之后,线程B执行DEL之前,服务器2的线程C在redis中进行加锁操作(此时由于线程A已经执行了DEL操作,因此可以加锁成功)。由于线程B已经校验完成,那么执行DEL就会删除掉服务器2加的锁。该问题就是因为GET和DEL操作不是原子的,就会出现问题。

解决上述问题,可以使用redis事务,保证GET和DEL的原子性,在执行期间不会有插队情况出现。但是一般不会这样做,引入lua脚本是更加有效的解决方案。

lua是一门编程语言,作为redis内嵌脚本,lua语言特别轻量,实现一个lua的解释器消耗系统资源非常小。

可以使用lua编写一些逻辑,把这个脚本上传到服务器上,然后客户端就可以控制redis执行这些脚本了。redis执行lua脚本的时候,是原子的,相当于执行一条命令一样。(redis官方文档中提出lua属于是redis事务的替代方案)。

过期时间续约问题

在加锁的时候key需要设置过期时间,那么这个时间设置多少合适呢?

如果设置短,那么有可能在业务逻辑还没执行完,锁就被释放了。

如果设置长,那么锁释放就会不及时。

动态续约:

初始情况下设置一个时间比较短的过期时间(灵活进行调整),如果发现时间快到的时候,业务逻辑还没执行完,那就在续上一些过期时间(无限续约)。直到业务逻辑执行完成,锁也可以在较短时间内被释放。

如果服务器中途挂了,那也没有负责续约的线程了,此时锁也可以在较短时间内被释放。

动态续约往往需要服务器这边一个专门的线程负责,把这个线程就叫做看门狗(watch dog)。

redlock算法

使用redis作为分布式锁,那么就需要保证redis的高可用。

使用redis哨兵机制,当主节点挂了可以投票选举从节点作为主节点。但是如果在主节点加锁后还没来得及同步给从节点,主节点就宕机了。此时哨兵选举的从节点也就不存在该锁了。

作为分布式系统,就需要随时考虑某个节点挂了,不会影响大局。

redlock算法核心思想就是:冗余

此处加锁会按照一定顺序,对这些redis主节点都进行加锁。如果某个节点宕机了,没关系继续给下一个redis主节点加锁。如果加锁成功节点个数超过总个数一半,就视为加锁成功。同理,解锁需要把上述节点都执行解锁操作。这里就不会因为某个节点挂了,而导致加不上锁的情况。

小结:

这里实际上实现了互斥锁,还可以使用redis做读写锁,可重入锁,公平锁等等。

相关文章:

SpringCloud Alibaba集成 Gateway(自定义负载均衡器)、Nacos(配置中心、注册中心)、Loadbalancer

要为未被某些网关路由谓词处理的请求提供相同的CORS配置,请将属性spring.cloud.gateway.globalcors.add-to-simple-url-handler-mapping设置为true。断言(Predicate):Java8中的断言函数,Spring Cloud Gateway中的断言函数输入类型是 Spring5.0框架中的ServerWebExchange。对于所有GET请求的路径,来自docs.spring.io的请求都将允许CORS请求。

zookeeper集群部署以及zookeeper原理

ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。ZooKeeper的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。ZooKeeper包含一个简单的原语集,提供Java和C的接口。

Ubuntu下安装和配置Redis

找到 /ect/redis/redis.conf 文件修改如下:注释掉 127.0.0.1 ,如果不需要远程连接redis则不需要这个操作。使用客户端向 Redis 服务器发送一个 PING ,如果服务器运作正常的话,会返回一个 PONG。默认情况下,Redis服务器不允许远程访问,只允许本机访问,所以我们需要设置打开远程访问的功能。执行sudo apt-get install redis-server 安装命令。查看 redis 是否启动,重新打开一个窗口。停止/启动/重启redis。

Windows下安装和配置Redis

下载版本Redis-x64-5.0.14.1.zip。(可能需要开代理)

Zookeeper概要、协议、应用场景

Zoopkeeper提供了一套很好的分布式集群管理的机制,就是它这种基于层次型的目录树的数据结构并对树中的节点进行有效管理,从而可以设计出多种多样的分布式的数据管理模型,作为分布式系统的沟通调度桥梁。

SSO 单点登录和 OAuth2.0 有何区别?

此方法的缺点是它依赖于浏览器和会话状态,对于分布式或者微服务系统而言,可能需要在服务端做会话共享,但是服务端会话共享效率比较低,这不是一个好的方案。在单点登录的上下文中,OAuth 可以用作一个中介,用户在一个“授权服务器”上登录,并获得一个访问令牌,该令牌可以用于访问其他“资源服务器”上的资源。首先,SSO 主要关注用户在多个应用程序和服务之间的无缝切换和保持登录状态的问题。这种方法通过将登录认证和业务系统分离,使用独立的登录中心,实现了在登录中心登录后,所有相关的业务系统都能免登录访问资源。

一键部署 SpringCloud 微服务,这套流程值得学习一波儿!

一键部署 springcloud 微服务,需要用到 Jenkins K8S Docker等工具。本文使用jenkins部署,流程如下图开发者将代码push到git运维人员通过jenkins部署,自动到git上pull代码通过maven构建代码将maven构建后的jar打包成docker镜像 并 push docker镜像到docker registry通过k8s发起 发布/更新 服务 操作其中 2~5步骤都会在jenkins中进行操作。

Springboot + oauth2 单点登录 - 原理篇

OAuth 协议为用户资源的授权提供了一个安全的、开放而又简易的标准,允许用户授权第三方移动应用访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方移动应用或分享他们数据的所有内容,OAuth2.0是OAuth协议的延续版本,但不向后兼容OAuth 1.0即完全废止了OAuth1.0。授权码模式(authorization code)密码模式(resource owner password credentials)客户端模式(client credentials) 不常用。

JAVA 中 13 种锁的实现方式

分布式系统时代,线程并发,资源抢占,慢慢变得很重要。那么常见的锁都有哪些?

三种方式实现分布式锁

通过以上过程你可以发现锁的获取是按照创建时间来的,谁先来争取锁谁就先获得锁,因此它实现的是公平锁。答案是不能,以Synchronized关键字为例,Synchronized关键字无论是在偏向锁、轻量级锁还是重量级锁状态都不能实现这点,如重量级锁,重量级锁是靠系统底层的互斥量Mutex实现的,也就是说每个节点(服务器)所使用的互斥量是分开的,节点A的互斥量是无法锁住节点B的线程访问临界区,因此Synchronized关键字只能保证单服务器内的JVM进程的不同线程同步,是不能用做分布式环境中来保证线程同步。

浅谈Java分布式与集群

在日常操作中,相信很多人在怎么理解Java分布式与集群问题上存在疑惑,今天就大概说说,不注意听,觉得两个可能是同一个东西,其实这个是两个概念。一句话概括:分布式是以缩短单个任务的执行时间来提升效率的,而集群则是通过提高单位时间内执行的任务数来提升效率。

java面试题:分布式和微服务的区别

分布式架构解决的是如何将一个大的系统划分为多个业务模块这些业务模块会分别部署到不同的机器上,通过接口进行数据交互的问题。微服务是指很小的服务,可以小到只完成一个功能,这个服务可以单独部署运行,不同服务之间通过rpc调用。分布式架构是将一个大的系统划分为多个业务模块,这些业务模块会分别部署到不同的机器上,通过接口进行数据交互。微服务架构是架构设计方式,是设计层面的东西,一般考虑如何将系统从逻辑上进行拆分,也就是垂直拆分。分布式系统是部署层面的东西,即强调物理层面的组成,即系统的各子系统部署在不同计算机上。

k8s搭建部署(超详细)

Kubernetes是Google 2014年创建管理的,是Google 10多年大规模容器管理技术Borg的开源版本。它是容器集群管理系统,是一个开源的平台,可以实现容器集群的自动化部署、自动扩缩容、维护等功能。快速部署应用快速扩展应用无缝对接新的应用功能节省资源,优化硬件资源的使用可移植: 支持公有云,私有云,混合云,多重云(multi-cloud)可扩展: 模块化, 插件化, 可挂载, 可组合自动化: 自动部署,自动重启,自动复制,自动伸缩/扩展。

什么是数据中台?

说完了数据中台诞生的历史背景,现在,我们应该对数据中台有了一定的了解,那我们现在给数据中台下个定义。自2016年,数据中台被提出以来,不同的人对数据中台有不同的理解,就像一千个读者心中有一千个哈姆雷特,因此也有许多不同的定义,以下是我从一些文章、书籍中搜集到的关于数据中台的定义:数据中台是DT时代的大背景下,为实现数据快(快速)、准(准确)、省(低成本)赋能业务发展的目标,将企业的数据统一整合起来,基于Onedata方法论借助大数据平台完成数据的统一加工处理,对外提供数据服务的一套机制。

Redis 击穿、穿透、雪崩产生原因解决思路

也就是在设定的时间里数据没有取出来,但是锁由过期了,常见的思路是,锁过期时间值递增,但是想想不靠谱,因为第一个请求可能超时,如果后面的也超时呢,接连多次超时之后,锁过期时间值势必特别大了,这样做弊端太多。雪崩,和击穿类似,不同的是击穿是一个热点Key某时刻失效,而雪崩是大量的热点Key在一瞬间失效,网络上很多博客都在强调解决雪崩的策略是随机过期时间,这个非常不准确,举个例子,银行做活动,之前这个利息系数为2%,过了零点系数改为3%,这种情况能将用户的对应的key改为随机过期吗?如果用的过去的数据叫脏数据。

Redis的key过期策略是怎么实现的

这是一道经典的Redis面试题,一个Redis中可能存在很多很多的key,这些key中可能有很大一部分都有过期时间,此时Redis服务器咋知道哪些key已经过期,哪些还没过期呢?如果直接遍历所有的key,这显然是行不通的,效率非常低!!Redis整体的策略是定期删除和惰性删除相结合。举个栗子:假如我去小卖铺买东西,付款的时候,发现东西过期了。就告知老板,于是老板下架此产品。消费者发现过期了,才去下架,这就叫。小卖铺老板主动定期抽取一部分商品,进行筛查,这就叫定期删除。

弹性搜索引擎Elasticsearch:本地部署与远程访问指南

本文主要讲解如何使用Elasticsearch分布式搜索分析引擎本地部署与远程访问。

分布式系统架构设计之分布式数据存储的备份恢复和监控故障排查

架构师通过设计和实施数据备份和恢复策略,可以最大程度地保障分布式系统在面对数据损失、硬件故障、灾难性事件等情况下的稳定性和可用性。在分布式数据存储中,数据备份和恢复是保障数据存储系统可靠性和容灾性的重要组成部分。通过合理的监控和故障排查策略,可以确保分布式数据存储系统在运行过程中保持高可用性、高性能,并且能够及时应对潜在的故障情况。在分布式系统中,对数据存储进行有效的监控和出现问题后故障排查策略是确保系统稳定性和性能可靠性的关键。

Redis 除了用作缓存还能干吗?

Redis 是一种内存键值数据库。它支持多种数据结构,如 String, Hash, List, Set 和 SortedSet。

【微服务】springboot整合skywalking使用详解

springboot整合skywalking

光伏发电模式中,分布式和集中式哪种更受欢迎?

5.可实现远距离输送,集中式光伏电站发出的电经高压并网,将电一层层的输送到更高的电压等级,如将高压电输送到华东等地区,以实现西电东输。分布式光伏发电:一般建在楼顶、屋顶、厂房等地方,较多的是基于建筑物表面,就近解决用户的用电问题,通过并网实现供电差额的补偿与外送。1.光伏电源处于用户侧,自发自用,就近发电,就近用电,发电供给当地负荷,视作负载,可以减少对电网供电的依赖,减少线路损耗。4.分布式光伏一般就近并网,线路的损耗很低或者可以说没有,可非常方便的补充当地的电量,供当地及附近的用电用户使用。

在 linux 服务器上安装Redis数据库

这里 我就改用 sudo service redis start。然后 他会提示你要占多少磁盘空间 例如 我这里是 1.7 M。因为我们刚安装好 端口 密码都没设置过 就是。然后 我们可以根据版本选择启动命令。没问题就 y 然后回车就可以了。如下图 我们很明显就是连接到了。先打开我们的Linux服务器。这样我们 redis就起来了。终端执行 安装redis。这样 就能看到版本了。

【Redis】Redis 红锁

本章节主要讲解redis中的红锁,假设我们有个客户端要获取锁,然后向master去获取锁,然后master会把锁同步到slave节点,假设现在还没同步出去的时候,master节点挂了。然后从节点升级为主节点,然后另外一个刚刚在竞争锁失败处于阻塞状态的线程去获取锁的时候,但是新的master上,并没有锁的信息,然后就能拿到锁,此时就可能导致一些问题。此时我们就能使用红锁,来解决这个问题。一个客户端要向多个master节点发送加锁请求。只有半数的锁加锁成功,才是获取锁。

【Redis】Redis 配置文件

自定义目录:/myredis/redis.conf。

【redis】redis的哨兵Sentinel高可用架构

redis的哨兵Sentinel高可用架构,redis 哨兵集群与redis集群的关系以及故障转移,故障检测等原理

【redis】Redis 架构演化之路

里面大致说了redis的架构演进。开始学redis架构的时候,总是感觉架构好多,迷迷糊糊的,但是你知道Redis 架构演化之路之后,就明白了前因后果,这样学习更加深入以及搞笑。这篇文章我想和你聊一聊 Redis 的架构演化之路。现如今 Redis 变得越来越流行,几乎在很多项目中都要被用到,不知道你在使用 Redis 时,有没有思考过,Redis 到底是如何稳定、高性能地提供服务的?如果你对 Redis 已经有些了解,肯定也听说过。

【redis】Redis哨兵机制和集群有什么区别?

Redis哨兵机制和集群有什么区别?redis集群有几种实现方式,一个是主从集群,一个是redis cluster.

Elasticsearch分布式搜索分析引擎本地部署与远程访问

本文主要讲解如何使用Elasticsearch分布式搜索分析引擎本地部署与远程访问

Gitlab基础篇: Gitlab docker 安装部署、Gitlab 设置账号密码

安装docker gitlab前确保docker环境,如果没有搭建docker请查阅“Linux docker 安装文档”可以看到在docker ps -a 打印中看到 容器ID ps 展示的容器ID只时原来的一部分。修改docker镜像的gitlab容器端口前需要把gitlab容器以及docker镜像关闭。通过容器ID就能找到containers下具体哪一个是gitlab容器的配置。修改config.v2.json、hostconfig.json文件。docker 下载 gitlab容器。

docker搭建maven私库Nexus3

阿里代理地址:http://maven.aliyun.com/nexus/content/groups/public/由于nexus的默认端口为8081,我们在启动的时候改为18091后需要修改nexus的配置文件。这样就可以在本地浏览器进入nexus页面了,地址为 服务器ip:18091。右上角登录用户名为admin,密码为之前查看的密码。配置maven-central的代理地址。删除nuget开头的仓库。同时查看admin密码。