redis实战---分布式锁--单机篇

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
性能测试 PTS,5000VUM额度
简介: redis实战---分布式锁--单机篇

redis分布式锁

八:总结提升

一:故事背景

本篇文章是redis实战系列的第二篇文章。

本章的主要内容是Redis分布式锁的相关知识。本篇文章将告诉你什么是分布式锁,结合一个业务场景,先带大家看看,单机上是如何实现锁功能的。

学完本篇,你可以了解到什么是锁,为什么要加锁。

二:什么是Redis分布式锁

Redis的分布式锁是一种用于协调分布式系统并保护共享资源的机制。它利用Redis的原子操作和单线程执行特性来保证多个进程或线程之间的互斥性。它可以保证在分布式环境中,同一时刻只有一个客户端能够获取到锁,从而避免了多个客户端同时对同一资源进行修改的问题。


三:Redis分布式锁的作用

高可用性:Redis 分布式锁可以使用 Redis 集群或者 Redis Sentinel 进行部署,保证了高可用性和可扩展性。

可重入性:可以让同一个客户端重复获得锁,避免了同一个客户端重复执行代码的问题。

自动过期:可以设置锁的过期时间,如果锁没有及时释放,就会自动过期,避免了死锁的问题。

支持阻塞和非阻塞:可以根据需要选择阻塞式或者非阻塞式的锁。

高性能:使用 Redis 自带的原子操作实现锁的获取和释放,具有高性能和高并发性。

四:业务场景

接下来我将结合一个秒杀的例子讲述如果实现Redis的分布式锁。

秒杀场景是一个非常经典的需要使用锁的场景。


假设有一个商品限时秒杀的业务场景,多个用户同时在秒杀开始时间内尝试购买该商品,但是该商品数量有限,只有一定数量的用户可以购买成功,其他用户则购买失败。

为了保证秒杀的公平性与真确性,这个时候我们就要通过锁来对商品的数量进行访问

五:代码实现

5.1 未加任何锁

结合上面的业务场景,我们来先来实现一个未加任何锁的代码,简单实现一下这个小需求,并且分析它存在的问题,这样可以更好的帮助我们理解为什么要加锁。


5.1.1 数据准备

首先在redis里添加了 key值为 stock value值 为 200 的数据,模拟我们要秒杀的商品数量为200。

5.1.2 未加锁的业务逻辑代码

@RestController
@RequestMapping("/test")
public class IndexController {
    // 自动注入 StringRedisTemplate 对象
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    // 处理 HTTP GET 请求路径是 /test/lock
    @GetMapping("lock")
    public String deductStock() {
        // 获取当前库存
        String stock1 = stringRedisTemplate.opsForValue().get("stock");
        if( stock1 == null){
            System.out.println("秒杀未开始");
            return "end";
        }
        int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
        if (stock > 0) {
            // 扣减库存
            int realStock = stock - 1;
            // 更新库存
            stringRedisTemplate.opsForValue().set("stock", realStock + "");
            System.out.println("扣减成功,剩余的库存为:" + realStock);
        } else {
            System.out.println("扣减失败,库存不足");
        }
        return "end";
    }
}

上述是根据我们的业务进行的一个简单的实现,在这个实现里,未对代码进行加锁。如果在并发请求的时候,这段代码将会出现很经典的超卖问题。

5.1.3 接口压测,模拟并发情况

让我们来压测一下接口,看一下对应的效果在这里我们使用的ApiPost进行一键压测。发送了50个请求,让我们来一起看看请求的结果。

5.1.4 压测结果5.1.5 压测问题

我们发现,50个请求进来之后,如果是正常的情况下,是应该减少50个库存,每个请求获得1个商品。

可以根据结果看,我们的50个请求获得了5个商品。同一个商品卖给了多个用户。列如 195号商品同时卖给了10个人。

那么我们该如何去解决这个问题呢?

六:单机情况下JVM级别加锁

首先我们来看一下,如果是单机(项目只部署在一台机器上),使用 synchronized 进行jvm级别加锁,解决上述问题。


6.1加锁代码

synchronized (this){
            // 获取当前库存
            String stock1 = stringRedisTemplate.opsForValue().get("stock");
            if( stock1 == null){
                System.out.println("秒杀未开始");
                return "end";
            }
            int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
            if (stock > 0) {
                // 扣减库存
                int realStock = stock - 1;
                // 更新库存
                stringRedisTemplate.opsForValue().set("stock", realStock + "");
                System.out.println("扣减成功,剩余的库存为:" + realStock);
            } else {
                System.out.println("扣减失败,库存不足");
            }
        }

代码非常的简单,使用 synchronized 关键字,将我们的业务逻辑进行包裹即可。

synchronized,保证同一时刻只有一个线程执行被synchronized修饰的代码块或方法,从而避免多个线程同时对共享资源进行修改而导致的数据不一致的问题。

6.2 运行结果
6.3 结果分析

从结果上来看,通过synchronized 可以在jvm级别上进行上锁。但是我们实际的生产环境中,很少有部署单机服务的。如果我们部署了多个服务,那么通过synchronized 是肯定无法影响另一条机器上的请求的。

七:多服务部署

7.1 图像展示
7.2 问题分析

假设我们,部署了两个服务,部署在tomcat1和tomcat2上,使用nginx做负载。此时仅仅通过synchronized 只能保持 tomcat1自己本身。tomcat2自己本身的数据被锁住。如果两个服务同时提供服务,仍然会产生我们上述的超卖问题。


八:总结提升

本文我们主要讲了锁的概念,为什么要加锁,单机上jvm级别的加锁,多服务部署的话,我们现在的代码存在的问题。

接下来我会讲解如何解决我们这次遗留的问题,在分布式环境下,如何加锁,如何解决可能会存在的问题。


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
1月前
|
NoSQL Java Redis
太惨痛: Redis 分布式锁 5个大坑,又大又深, 如何才能 避开 ?
Redis分布式锁在高并发场景下是重要的技术手段,但其实现过程中常遇到五大深坑:**原子性问题**、**连接耗尽问题**、**锁过期问题**、**锁失效问题**以及**锁分段问题**。这些问题不仅影响系统的稳定性和性能,还可能导致数据不一致。尼恩在实际项目中总结了这些坑,并提供了详细的解决方案,包括使用Lua脚本保证原子性、设置合理的锁过期时间和使用看门狗机制、以及通过锁分段提升性能。这些经验和技巧对面试和实际开发都有很大帮助,值得深入学习和实践。
太惨痛: Redis 分布式锁 5个大坑,又大又深, 如何才能 避开 ?
|
7天前
|
NoSQL Redis
Redis分布式锁如何实现 ?
Redis分布式锁通过SETNX指令实现,确保仅在键不存在时设置值。此机制用于控制多个线程对共享资源的访问,避免并发冲突。然而,实际应用中需解决死锁、锁超时、归一化、可重入及阻塞等问题,以确保系统的稳定性和可靠性。解决方案包括设置锁超时、引入Watch Dog机制、使用ThreadLocal绑定加解锁操作、实现计数器支持可重入锁以及采用自旋锁思想处理阻塞请求。
40 16
|
21天前
|
NoSQL 关系型数据库 MySQL
MySQL与Redis协同作战:优化百万数据查询的实战经验
【10月更文挑战第13天】 在处理大规模数据集时,传统的关系型数据库如MySQL可能会遇到性能瓶颈。为了提升数据处理的效率,我们可以结合使用MySQL和Redis,利用两者的优势来优化数据查询。本文将分享一次实战经验,探讨如何通过MySQL与Redis的协同工作来优化百万级数据统计。
48 5
|
27天前
|
缓存 NoSQL Java
Spring Boot与Redis:整合与实战
【10月更文挑战第15天】本文介绍了如何在Spring Boot项目中整合Redis,通过一个电商商品推荐系统的案例,详细展示了从添加依赖、配置连接信息到创建配置类的具体步骤。实战部分演示了如何利用Redis缓存提高系统响应速度,减少数据库访问压力,从而提升用户体验。
69 2
|
1月前
|
NoSQL Java Redis
开发实战:使用Redisson实现分布式延时消息,订单30分钟关闭的另外一种实现!
本文详细介绍了 Redisson 延迟队列(DelayedQueue)的实现原理,包括基本使用、内部数据结构、基本流程、发送和获取延时消息以及初始化延时队列等内容。文章通过代码示例和流程图,逐步解析了延迟消息的发送、接收及处理机制,帮助读者深入了解 Redisson 延迟队列的工作原理。
|
1月前
|
NoSQL Redis 数据库
计数器 分布式锁 redis实现
【10月更文挑战第5天】
47 1
|
3月前
|
NoSQL Redis
基于Redis的高可用分布式锁——RedLock
这篇文章介绍了基于Redis的高可用分布式锁RedLock的概念、工作流程、获取和释放锁的方法,以及RedLock相比单机锁在高可用性上的优势,同时指出了其在某些特殊场景下的不足,并提到了ZooKeeper作为另一种实现分布式锁的方案。
108 2
基于Redis的高可用分布式锁——RedLock
|
3月前
|
缓存 NoSQL Java
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解分布式情况下如何添加分布式锁 【续篇】
这篇文章是关于如何在SpringBoot应用中整合Redis并处理分布式场景下的缓存问题,包括缓存穿透、缓存雪崩和缓存击穿。文章详细讨论了在分布式情况下如何添加分布式锁来解决缓存击穿问题,提供了加锁和解锁的实现过程,并展示了使用JMeter进行压力测试来验证锁机制有效性的方法。
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解分布式情况下如何添加分布式锁 【续篇】
|
1月前
|
缓存 NoSQL Java
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
59 3
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
|
1月前
|
NoSQL 算法 关系型数据库
Redis分布式锁
【10月更文挑战第1天】分布式锁用于在多进程环境中保护共享资源,防止并发冲突。通常借助外部系统如Redis或Zookeeper实现。通过`SETNX`命令加锁,并设置过期时间防止死锁。为避免误删他人锁,加锁时附带唯一标识,解锁前验证。面对锁提前过期的问题,可使用守护线程自动续期。在Redis集群中,需考虑主从同步延迟导致的锁丢失问题,Redlock算法可提高锁的可靠性。
73 4