百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT技术 > 正文

什么时候你应该使用redis来实现锁

wptr33 2025-01-05 20:32 24 浏览

在项目的开发过程中,经常会有以下几种场景:

  1. 用户下单,由于点击速度过快,或者页面卡住导致用户又点击了一次,这个时候后台就给用户生成了两笔订单。
  2. 消息队列的消费方由于网络抖动导致的超时,或者ack失败,导致消息重发,导致了计算结果重复或者出错。
  3. 某些需求需要限制n分钟某个用户只能操作一次。

类似这样的场景还有很多很多,每次面对这样的场景的时候,大部分同学都会说,“那我加个锁吧”,然后,你就会发现大部分的实现方式如下:

我们来看看上面的实现有没有问题。

假设在操作del命令的时候进行了网络抖动导致命令操作失败,这个锁就成了死锁,显然是不能接受的。

那我们改进一下,我们将锁的值设置为锁的有效时间,获取锁失败之后,我们再get一下,判断一下时间是否过期,如果过期了再删除,重新尝试获取锁。

如果没有过期,说明获取锁失败。这样是不是就没问题了?来看一个场景

假设A、B同时获取锁,发现锁被占用,A、B先后get,发现锁过期,A执行del,然后进行了setnx操作,获取锁成功。此时B执行del操作,然后也进行了setnx操作,获取锁成功,问题就来了,A、B同时获取到了锁,如下图:

所以,正确的做法应该是使用getset来替换del,这个命令是原子,如下图

使用getset方法时,如果遇到上面的情况,A执行getset发现过期,执行del。B执行getset,因为getset是原子的,获取到的将是A执行之后的值,此时值将不是过期的,可以有效的避免这个问题。

同时,redis在2.6.12版本后,提供了更强大的set命令

所以我们的加锁过程就变得比较简单,redis官方有详细的介绍,具体如下

这样,即使因为网络抖动,del操作失败,还是有过期时间来保证不会造成死锁。当然,del释放锁操作我们还是要注意两点:1、加锁的value使用线程独有的值;2、使用lua脚本进行del。避免被别的线程将锁释放,造成锁失效的问题。

到这,是不是我们关于redis锁的问题就解决完了?还没有,目前来说,我们都没有关注到redis服务本身上,我们接着往下看。

假设我们的redis是单机,好吧,单机就是个问题,如果redis服务挂了呢,这个锁的实现就出现了问题。

好,那来看看redis主从模式有没有问题,使用主从模式就能避免redis主服务挂了之后造成的单点故障。但是,仔细看redis官方文档

注意红圈圈住的部分,使用redis主从模式并不可靠,因为redis主从的复制是异步的

好的,接下来,我们看看redis主从复制的实现方式,为什么会造成这种方式实现锁不可靠。主从复制。

从文档中我们看到,redis主从同步默认使用异步模式,如下图

  1. ClientA请求redis master,redis master操作后返回成功,因为异步同步到slave,此时还没有将数据同步到redis slave。
  2. 在同步到redis slave之前,redis master挂掉了,redis slave中并没有刚刚Client A写入的数据。
  3. 此时,redis slave切换成master,Client B请求新的master也就是原来的redis slave,成功。

好吧,这个锁服务还是不可靠。

(具体redis主从复制的细节可以看redis哨兵及集群相关的官方文档)

正因为这个原因redis的做着提出了RedLock的实现方式,来提高redis锁的可靠性,接下来一起看看什么是RedLock。

假设有多个redis master节点,这些节点是完全独立的

  1. clientA获取当前的系统时间(毫秒数)。
  2. 使用同一个语句对所有的redis实例进行加锁操作,我们对加锁操作设置超时时间,这个时间要远远小于锁的失效时间。
  3. clientA计算加锁消耗了多长时间(当前时间减去第一步获取的时间),只有当在大多数redis上操作成功,并且获取锁的时间小于锁的失效时间,我们认为获取锁成功。
  4. 如果锁获取成功,则锁的有效时间为设置的时间减去加锁消耗的时间。
  5. 如果锁没有获取成功,则要在所有的节点上操作解锁。

如下图:

我们看到,如果要使用RedLock,需要额外的部署多套redis服务,然后同时在这些服务上去获取锁,然后跟进获取锁的结果,来判断是否获取锁成功。是不是使用RedLock就解决了redis锁不可靠的问题呢?并不是,接着往下看。

Martin Kleppmann这个人,针对RedLock写了一篇文章,指出了RedLock存在的问题:

  1. 如果应用发生了长时间的GC,这个GC时间超过了锁的有效时间,这个时候GC完成,接着去执行业务,但是此时,锁其实已经失效了。
  2. 因为RedLock算法依赖于机器的时间,如果机器的时间发生了跳跃(比如人为的进行了修改),锁也将变得不可靠。
  3. 同样的,长时间的IO等耗时操作,也会造成问题。

当然,redis作者针对Martin Kleppmann的文章也进行了反驳大家可以搜一搜看看。

说到这里,不得不提一下CAP原则,CAP指的是Consistency(一致性)、Availability(可用性)、Partition tolerance(分区容错性),是说分布式系统只能同时满足这三个原则中的两个,Redis是基于AP模型的。所以如果对一致性有严格要求的系统,不能基于redis来使用redis锁。当然,如果对一致性要求不高,但是对高可用要求比较高的系统,可以选用redis,最最重要的,一定是根据自己的业务来选择合适的技术。

相关推荐

MySQL进阶五之自动读写分离mysql-proxy

自动读写分离目前,大量现网用户的业务场景中存在读多写少、业务负载无法预测等情况,在有大量读请求的应用场景下,单个实例可能无法承受读取压力,甚至会对业务产生影响。为了实现读取能力的弹性扩展,分担数据库压...

Postgres vs MySQL_vs2022连接mysql数据库

...

3分钟短文 | Laravel SQL筛选两个日期之间的记录,怎么写?

引言今天说一个细分的需求,在模型中,或者使用laravel提供的EloquentORM功能,构造查询语句时,返回位于两个指定的日期之间的条目。应该怎么写?本文通过几个例子,为大家梳理一下。学习时...

一文由浅入深带你完全掌握MySQL的锁机制原理与应用

本文将跟大家聊聊InnoDB的锁。本文比较长,包括一条SQL是如何加锁的,一些加锁规则、如何分析和解决死锁问题等内容,建议耐心读完,肯定对大家有帮助的。为什么需要加锁呢?...

验证Mysql中联合索引的最左匹配原则

后端面试中一定是必问mysql的,在以往的面试中好几个面试官都反馈我Mysql基础不行,今天来着重复习一下自己的弱点知识。在Mysql调优中索引优化又是非常重要的方法,不管公司的大小只要后端项目中用到...

MySQL索引解析(联合索引/最左前缀/覆盖索引/索引下推)

目录1.索引基础...

你会看 MySQL 的执行计划(EXPLAIN)吗?

SQL执行太慢怎么办?我们通常会使用EXPLAIN命令来查看SQL的执行计划,然后根据执行计划找出问题所在并进行优化。用法简介...

MySQL 从入门到精通(四)之索引结构

索引概述索引(index),是帮助MySQL高效获取数据的数据结构(有序),在数据之外,数据库系统还维护者满足特定查询算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构...

mysql总结——面试中最常问到的知识点

mysql作为开源数据库中的榜一大哥,一直是面试官们考察的重中之重。今天,我们来总结一下mysql的知识点,供大家复习参照,看完这些知识点,再加上一些边角细节,基本上能够应付大多mysql相关面试了(...

mysql总结——面试中最常问到的知识点(2)

首先我们回顾一下上篇内容,主要复习了索引,事务,锁,以及SQL优化的工具。本篇文章接着写后面的内容。性能优化索引优化,SQL中索引的相关优化主要有以下几个方面:最好是全匹配。如果是联合索引的话,遵循最...

MySQL基础全知全解!超详细无废话!轻松上手~

本期内容提醒:全篇2300+字,篇幅较长,可搭配饭菜一同“食”用,全篇无废话(除了这句),干货满满,可收藏供后期反复观看。注:MySQL中语法不区分大小写,本篇中...

深入剖析 MySQL 中的锁机制原理_mysql 锁详解

在互联网软件开发领域,MySQL作为一款广泛应用的关系型数据库管理系统,其锁机制在保障数据一致性和实现并发控制方面扮演着举足轻重的角色。对于互联网软件开发人员而言,深入理解MySQL的锁机制原理...

Java 与 MySQL 性能优化:MySQL分区表设计与性能优化全解析

引言在数据库管理领域,随着数据量的不断增长,如何高效地管理和操作数据成为了一个关键问题。MySQL分区表作为一种有效的数据管理技术,能够将大型表划分为多个更小、更易管理的分区,从而提升数据库的性能和可...

MySQL基础篇:DQL数据查询操作_mysql 查

一、基础查询DQL基础查询语法SELECT字段列表FROM表名列表WHERE条件列表GROUPBY分组字段列表HAVING分组后条件列表ORDERBY排序字段列表LIMIT...

MySql:索引的基本使用_mysql索引的使用和原理

一、索引基础概念1.什么是索引?索引是数据库表的特殊数据结构(通常是B+树),用于...