易商讯
当前位置: 首页 » 资讯 » 生活 » 正文

redis分布式锁优化(redis 分布式事务锁)

放大字体  缩小字体 发布日期:2023-11-27 15:53:54
导读

文章目录一、集群环境下 秒杀 一人一单的并发问题:snowman:基本原理和实现方式:zap:Redis 分布式锁的核心实现思路三、实战开发 实现 Redis 分布式锁四、ApiFox 测试 集群模式下是否能够解决并发问题:partly_sunny:前言在 微服务 Spring Boot 整合Redis 实现优惠卷秒杀 一人一单 下, 单线程的情况,不会出现并发的问题 ,那么,我们的 秒杀场景都是

文章目录

  • 一、集群环境下 秒杀 一人一单的并发问题
  • :snowman:基本原理和实现方式:zap:Redis 分布式锁的核心实现思路
  • 三、实战开发 实现 Redis 分布式锁
  • 四、ApiFox 测试 集群模式下是否能够解决并发问题

:partly_sunny:前言

在 微服务 Spring Boot 整合Redis 实现优惠卷秒杀 一人一单 下, 单线程的情况,不会出现并发的问题 ,那么,我们的 秒杀场景都是出现在并发环境下的,多个用户同时去抢购一件商品 ,这时就体现出了 系统 的 抗受 高并发、高可用 性 ,在用户 访问数多的情况下,我们需要去 搭建集群 并配置负载均衡 去均匀的分配服务器的压力 ,以免出现 服务宕机 导致系统不可用,集群下我们的 秒杀一人一单存在问题,下面详细介绍。

一、集群环境下 秒杀 一人一单的并发问题

之前我们在单机情况下通过加 sync 锁就可以达到线程安全 ,但是在集群环境下,就不可以了。

开启集群来测试

将服务启动2份,端口为8002和 8083

如何开始Services 服务列表?

View --> Tool Windows --> Services打开服务列表 或者 快捷键 (Alt + 8)

出现以下Services,点击新建服务

单击 Run Configuration Type 选择Spring Boot 即可 (注意:如果没有Spring Boot 选项,那就手动启动程序,会自动出现 Spring Boot 列表)

即可完成新建服务,实现集群的效果

为什么会出现此问题呢?

由于现在我们 部署了多个tomcat每个tomcat都有一个属于自己的JVM ,那么 假设在服务器A的tomcat内部,有两个线程 ,这两个线程由于 使用的是同一份代码 ,那么 他们的锁对象是同一个 ,是 可以实现互斥 的, 但是 如果现在是服务器B的tomcat内部,又有两个线程,但是他们的锁对象写的虽然和服务器A一样 ,但是 锁对象却不是同一个 ,所以 线程3和线程4可以实现互斥 ,但是 却无法和线程1和线程2实现互斥 ,这就是 集群环境下,syn锁失效的原因 ,在这种情况下,我们就需要 使用分布式锁来解决 这个问题。

二、什么是分布式锁?

:snowman:基本原理和实现方式

分布式锁: 满足分布式系统或集群模式下多进程可见并且互斥的锁

分布式锁的 核心思想就是让大家都使用同一把锁 ,只要大家使用的是同一把锁,那么我们就能锁住线程,不让线程进行,让程序串行执行,这就是分布式锁的核心思路

分布式锁它应该满足一些什么样的条件呢?

可见性:多个线程都能看到相同的结果

注意:这个地方说的可见性并不是并发编程中指的内存可见性,只是说多个进程之间都能感知到变化的意思

互斥:互斥是分布式锁的最基本的条件,使得程序串行执行

高可用:程序不易崩溃,时时刻刻都保证较高的可用性

高性能:由于加锁本身就让性能降低,所有对于分布式锁本身需要他就较高的加锁性能和释放锁性能

安全性:安全也是程序中必不可少的一环

常见的分布式锁有三种

MySQL: MySQL 本身就带有锁机制,但是由于MySQL性能本身一般,所以采用分布式锁的情况下,其实使用MySQL作为分布式锁比较少见

Redis: Redis作为分布式锁是非常常见的一种使用方式 ,现在 企业级开发中基本都使用Redis或者Zookeeper作为分布式锁 ,利用 setnx 这个方法, 如果插入key成功,则表示获得到了锁,如果有人插入成功,其他人插入失败则表示无法获得到锁 ,利用这套逻辑来实现分布式锁

Zookeeper:Zookeeper也是企业级开发中较好的一个实现分布式锁的方案, 是通过创建临时节点来实现的

:zap:Redis 分布式锁的核心实现思路

实现分布式锁时需要实现的两个基本方法:

  • 获取锁(setnx)
  • 互斥 :确保只能有一个线程获取锁
  • 非阻塞 :尝试一次,成功返回true,失败返回false
  • 释放锁:手动释放超时释放 : 获取锁时添加一个超时时间

核心思路:

我们利用 Redis 的setNx 方法 ,当有多个线程进入时,我们就利用该方法,第一个线程进入时,redis 中就有这个key 了,返回了1,如果结果是1,则表示他抢到了锁,那么他去执行业务,然后再删除锁,退出锁逻辑,没有抢到锁的哥们,等待一定时间后重试即可

三、实战开发 实现 Redis 分布式锁

加锁:新建 Lock 锁接口

ILock 锁接口

package com.chen.utils; public interface ILock { /** * 尝试获取锁 * @param timeoutSecond * @return */ boolean tryLock(long timeoutSecond); /** * 释放锁 */ void unLock(); }

SimpleRedisLock 锁实现类

package com.chen.utils; import cn.hutool.core.lang.UUID; import cn.hutool.core.util.BooleanUtil; import com.chen.utils.ILock; import org.springframework.core.io.ClassPathResource; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import java.util.concurrent.TimeUnit; /** * redis 分布式锁实现类,实现获取锁与释放锁 */ public class SimpleRedisLock implements ILock { private String name; private StringRedisTemplate stringRedisTemplate; private static final String KEY_PREFIX = &34;; public SimpleRedisLock(String name, StringRedisTemplate stringRedisTemplate) { this.name = name; this.stringRedisTemplate = stringRedisTemplate; } @Override public boolean tryLock(long timeoutSecond) { String threadId = ID_PREFIX + Thread.currentThread().getId(); // 尝试获取锁 Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + name, threadId, timeoutSecond, TimeUnit.SECONDS); return BooleanUtil.isTrue(success); } @Override public void unLock() { stringRedisTemplate.delete(KEY_PREFIX + name); } }

修改业务代码

public Result seckillVoucher(Long voucherId) { //1. 查询优惠卷 SeckillVoucher seckillVoucher = seckillVoucherService.getById(voucherId); //2. 判断秒杀是否开始 开始时间大于当前时间表示未开始抢购 if (seckillVoucher.getBeginTime().isAfter(LocalDateTime.now())) { return Result.fail(&34;); } //3. 判断秒杀是否结束 if (seckillVoucher.getEndTime().isBefore(LocalDateTime.now())) { return Result.fail(&34;); } //4. 判断库存是否充足 if (seckillVoucher.getStock() < 1) { return Result.fail(&34;); } // 新增代码 Long userId = UserHolder.getUser().getId(); // 创建锁对象 SimpleRedisLock lock = new SimpleRedisLock(&34; + userId, stringRedisTemplate); // 获取锁对象 boolean tryLock = lock.tryLock(2000); if (!tryLock) { return Result.fail(&34;); } try { IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy(); return proxy.createVoucherOrder(voucherId, userId); } catch (Exception e) { } finally { //释放锁 lock.unLock(); } return null; }

四、ApiFox 测试 集群模式下是否能够解决并发问题

加入请求地址、参数 进行测试

第二个项目同上操作,换一下端口为8083 再次进行测试,返回结果

完成,以上接口,测试正常~

:boat:小结

以上就是【 Bug 终结者 】对 微服务 Spring Boot 整合Redis分布式锁 实现优惠卷秒杀 一人一单 的简单介绍, 在分布式系统下,高并发的场景下,会出现此类库存超卖问题,本篇文章介绍了采用分布式锁来解决,但是依然是有弊端,集群环境下,不同的服务之间删除锁会出现误删问题, 下章节,我们将继续进行优化,持续关注!


声明:易商讯尊重创作版权。本文信息搜集、整理自互联网,若有来源标记错误或侵犯您的合法权益,请联系我们。我们将及时纠正并删除相关讯息,非常感谢!

 
(文/小编)
免责声明
• 
本文redis分布式锁优化(redis 分布式事务锁)链接:http://www.esxun.cn/news/398505.html 。本文仅代表作者个人观点,请读者仅做参考,如若文中涉及有违公德、触犯法律的内容,一经发现,立即删除,作者需自行承担相应责任。涉及到版权或其他问题,请及时联系我们,我们将在24小时内处理完毕。如涉及作品内容、版权等问题,请在作品发表之日起一周内与本网联系,否则视为放弃相关权利。
 

Copyright © www.esxun.cn 易商讯ALL Right Reserved


冀ICP备2023038169号-3