本文分为两部分:

一、介绍redis分布式锁的原理和使用方法;

二、使用redis分布式锁实现一个简单的秒杀系统。

注意:本文使用java1.8,最后的例子为springboot项目。

目录

redis分布式锁

原理

进阶

锁过期时间

lock防误删

确保删锁原子性

秒杀系统示例


redis分布式锁

对并发有要求的系统常常面临一个问题,如何在实现并发的基础上保持数据的一致性。redis分布式锁能给出一个解决方案。

redis相信大家都非常熟悉了,作为一个数据库缓存技术,简便好用,也支持并发,核心就是使用redis分布式锁。

原理

redis分布式锁的原理非常简单:在运行实际的业务代码之前,首先到redis中去获得唯一的redis锁,如果获取到,则继续执行业务代码,并在业务代码结束后主动释放锁;若未成功获取到锁,则不执行业务代码。

核心代码如下:

//通过向redis服务器插入一组键值对来获取锁
Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", "123");if (lock) {//业务代码在此//......//通过删除redis中的lock键值对释放锁redisTemplate.delete("lock");
}

通过以上代码可以看出,所谓的“获取锁”,“释放锁”操作,本质上就是向redis服务器插入和删除键值对。是不是也没那么高大上?

理解了原理,我们下面就动手做一做。以一个springboot项目为例,看看如何使用redis分布式锁。

使用IDE创建一个基本的springboot项目,我这里使用的IDEA2021.1社区版。

需要引入的依赖如下。

        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-redis</artifactId><version>2.5.7</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency>

在配置文件中对redis服务器进行配置。我这里使用了本地的redis服务器,各位可以根据情况修改服务器配置。

创建一个package并创建一个RedisLockController.class,然后在其中创建一个testLock方法。该方法的业务逻辑非常简单:将redis中存储的num变量值+1。

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@Slf4j
public class RedisLockController {@AutowiredStringRedisTemplate redisTemplate;@GetMapping("/testLock")public void testLock() {//尝试获取锁,注意这里的lock为键名,123为对应的值Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", "123");if (lock) {log.info("成功获取锁");//以下是业务代码:到redis服务器中查找num,如果没找到则初始化,若找到则num+1String numStr = redisTemplate.opsForValue().get("num");if (StringUtils.isEmpty(numStr)) {redisTemplate.opsForValue().set("num", "0");} else {int number = Integer.valueOf(numStr);redisTemplate.opsForValue().set("num", ++number + "");}//释放锁redisTemplate.delete("lock");} {log.info("未获取到锁");}}
}

运行项目,并发起请求:

之后查看redis服务器上的数据,发现num已被初始化为0:

在以下位置打断点,并再次调用接口。

 代码暂停至断点处,此时我们再次查看redis服务器,发现多了一个键值对:这就是获得的锁。

让代码恢复运行,再次查看redis服务器,发现锁已经释放(其实就是把lock键值对删除了),并且业务代码正常运行,num+1:

以上示例简单说明了redis分布式锁的原理和使用方法。让我们更进一步,以上代码是否有问题或者是否存在可以优化的空间?

进阶

锁过期时间

试想以下场景:

1、代码正常获取到锁并开始运行业务代码,但是业务代码有bug,抛错了,会发生什么?

我们将代码修改一下,模拟业务代码出错。注意抛错是在释放锁之前。

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@Slf4j
public class RedisLockController {@AutowiredStringRedisTemplate redisTemplate;@GetMapping("/testLock")public void testLock() throws Exception{//尝试获取锁Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", "123");if (lock) {log.info("成功获取锁");//以下是业务代码:到redis服务器中查找num,如果没找到则初始化,若找到则num+1String numStr = redisTemplate.opsForValue().get("num");if (StringUtils.isEmpty(numStr)) {redisTemplate.opsForValue().set("num", "0");} else {int number = Integer.valueOf(numStr);redisTemplate.opsForValue().set("num", ++number + "");throw new Exception();//这里模拟业务代码抛错}//释放锁redisTemplate.delete("lock");} {log.info("未获取到锁");}}
}

再次运行代码并调用接口,发现确实抛错:

查看redis服务器,发现lock并没有消失:

这是因为业务代码抛错,导致之后的释放锁代码没有执行,进而出现锁永久生效的情况。之后我们再调用接口,会发现因为无法正常获得锁,num不会再自增。除非我们手动删除锁。

如何解决以上问题?很简单,给锁设置一个过期时间,像下面代码这样:在尝试获取锁时就给锁设置一个失效时间,在时间到期后,无论有没有主动释放锁,锁都会自动过期。

Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", "123", 30, TimeUnit.SECONDS);

我们将redis中的lock键值对手动删除,再次运行并调用接口(代码依旧会抛出异常):

lock仍然正常产生了,但是等待30秒后,发现lock已经自动删除。

总结下:通过给redis分布式锁设置一个自动过期时间,可以防止业务代码抛错导致产生死锁。

这里有的小伙伴可能要问了:那在业务代码执行完毕后不主动释放锁,等待锁自动过期不就行吗?答案是不行

redis分布式锁及秒杀系统实战相关推荐

  1. 基于redis分布式锁实现“秒杀”

    作者丨lsfire https://blog.csdn.net/u010359884/article/details/50310387 最近在项目中遇到了类似"秒杀"的业务场景,在 ...

  2. Java程序猿笔记——基于redis分布式锁实现“秒杀”

    最近在项目中遇到了类似"秒杀"的业务场景,在本篇博客中,我将用一个非常简单的demo,阐述实现所谓"秒杀"的基本思路. 业务场景 所谓秒杀,从业务角度看,是短时 ...

  3. Redis分布式锁实现秒杀

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/u010359884/article/details/50310387 最近在项目中遇到了类似&quo ...

  4. redis分布式锁实现秒杀活动

    最近,参与和负责公司的一次秒杀活动的设计开发,收获颇多,与大家分享.其实大家在生活中或见过或参见过秒杀活动,用户以极低的成本获得高价值的商品,所以也导致活动期间出现拥挤现象,进而导致一些高并发问题,所 ...

  5. @scheduled cron启动后和每小时执行_小耶哥: 一个Redis分布式锁又要和小鑫同学扯半个小时!...

    1 Redis分布式锁 |1-1 定时任务重复执行-问题引入 最近小耶哥在做一个功能, 什么功能呢? 就是超时未支付的订单我们要定时关闭, 释放库存, 并且短信通知用户该订单因超时被取消了.由于小耶哥 ...

  6. java设计前期工作基础和存在的困难_Java秒杀系统实战系列-基于Redisson的分布式锁优化秒杀逻辑...

    本文是"Java秒杀系统实战系列文章"的第十五篇,本文我们将借助综合中间件Redisson优化"秒杀系统中秒杀的核心业务逻辑",解决Redis的原子操作在优化秒 ...

  7. ieee39节点系统介绍_Java秒杀系统实战系列-基于ZooKeeper的分布式锁优化秒杀逻辑...

    本文是"Java秒杀系统实战系列文章"的第十六篇,本文我们将继续秒杀系统的优化之路,采用统一协调调度中心中间件ZooKeeper控制秒杀系统中高并发多线程对于共享资源~代码块的并发 ...

  8. Java秒杀系统实战系列~基于Redisson的分布式锁优化秒杀逻辑

    摘要: 本篇博文是"Java秒杀系统实战系列文章"的第十五篇,本文我们将借助综合中间件Redisson优化"秒杀系统中秒杀的核心业务逻辑",解决Redis的原子 ...

  9. 京东秒杀系统模块的Redis分布式锁深度剖析,没给你讲明白你打我

    1|0背景 目前开发过程中,按照公司规范,需要依赖框架中的缓存组件.不得不说,做组件的大牛对CRUD操作的封装,连接池.缓存路由.缓存安全性的管控都处理的无可挑剔.但是有一个小问题,该组件没有对分布式 ...

最新文章

  1. 关于层的挡隔问题的探讨
  2. bootstrap-wysiwyg中JS控件富文本的用法
  3. 内存管理之程序内存分布
  4. SpringBoot 根据传参返回不同的内容
  5. 通俗易懂地搞懂决策树(Decision Tree)那些事儿
  6. 51CTO交流摘录(1):SOC的定义、适用性和组成
  7. LeetCode 835. 图像重叠
  8. 工作中应用计算机,浅谈计算机在我国计工作中的应用与发展.doc
  9. “两小学生研究喝茶抗癌获奖”,官方回应:经老师培训独立完成
  10. 机器学习速成课程 | 练习 | Google Development——编程练习:特征集
  11. L2-012. 关于堆的判断(STL中heap)
  12. 2061:【例1.2】梯形面积【入门题】
  13. Hyper-V应用指南之3-理解并配置Hyper-V虚拟网络[转]
  14. 内存共享【Delphi版】
  15. 软件缺陷分析的几种方法
  16. [原创] 阿里巴巴平台型产品经理 实习生面试经历
  17. 豆瓣电影数据分析案例
  18. 蒙特卡洛的应用软件统计——小李子LC
  19. 每日一题(二三)function Foo(){ Foo.a = function(){console.log(1); } this.a = function(){console.log(2)}) Fo
  20. 基于LeNet的手写汉字识别(caffe)

热门文章

  1. 魔塔之拯救白娘子~我的第一个VB6+DX8做的小游戏源码~15开始游戏-地图层次判断
  2. python怎么运用大数据_R/Python在大数据中的运用策略
  3. linux内核与bash脚本接囗,Bash脚本编程之脚本基础和bash配置文件
  4. Dubbo学习(一):dubbo的基本概念
  5. 安装净网大师的后果---无线重启 解决办法
  6. 10设置精美的免费网站后台管理系统模板
  7. 3分钟学会Excel“自主学习”
  8. 联想转型移动互联网 决心与苹果背水一战;微软推出政治社交平台townhall(每日关注:20100420)...
  9. 数据结构学习笔记——前、中、后缀表达式的转换(栈的应用)
  10. 学习SpringCloudAlibaba(二)微服务的拆分与编写