Redis---------分布式锁Redisson

2024-05-04 11:44
文章标签 redis 分布式 redisson

本文主要是介绍Redis---------分布式锁Redisson,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 概述

 Redisson入门

 第一步:引入依赖

        <dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.13.6</version></dependency>

第二步:配置文件

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class RedissonConfig {@Beanpublic RedissonClient redissonClient(){//配置Config config = new Config();config.useSingleServer().setAddress("redis://192.168.136.132:6379").setPassword("pz030812...");//创建RedissonClient对象return Redisson.create(config);}
}

第三步:使用锁

        Long id = UserHolder.getUser().getId();//创建锁对象RLock lock = redissonClient.getLock("lock:order" + id);//获取锁boolean trylock = lock.tryLock();//判断是否获得锁成功if (!trylock) {//获取锁失败return Result.fail("不允许重复下单!");}try {//获得锁进行操作//获得Spring的代理对象(事务)IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();return proxy.createVoucherOrder(voucherId);} finally {//释放锁lock.unlock();}

Redisson锁可重入原理

 就是在Lock中利用hash数据加一个标识字段,标识有几个自己线程中获得锁的数量,每次获取锁的流程:①判断锁是否存在,如果不存在则说明没人用锁,则获取到锁,并且设置好标识以及线程名②如果存在则判断是不是自己线程中的锁③如果不是,就说明是别人的锁,则获取失败返回错误④如果判断是自己线程的锁,则把标识字段+1,并且获得锁⑤之后只要是自己线程中的人来获取锁就直接更改标识。

而释放锁的流程:①先判断这个锁是不是自己线程的②如果不是就说明锁已经被释放了③如果是的话,就把标识减1④再去看标识是否已经等于0,即到了最外层⑤如果是的话就直接释放锁⑥不是的话就重置锁的有限期,继续执行业务

 Redisson锁可重试,超时释放(看门狗)原理

 Redisson锁主从一致性

multiLock原理:

异步实现秒杀商品

 执行流程:将判断资格和写进数据库分离开来,使得并发能力大大增强

 

 代码执行:

①在添加秒杀商品的同时,将数据写进Redis当中

 @Transactionalpublic void addSeckillVoucher(Voucher voucher) {// 保存优惠券save(voucher);// 保存秒杀信息SeckillVoucher seckillVoucher = new SeckillVoucher();seckillVoucher.setVoucherId(voucher.getId());seckillVoucher.setStock(voucher.getStock());seckillVoucher.setBeginTime(voucher.getBeginTime());seckillVoucher.setEndTime(voucher.getEndTime());seckillVoucherService.save(seckillVoucher);//保存秒杀劵到Redis中stringRedisTemplate.opsForValue().set("seckill:stock"+voucher.getId(),voucher.getStock().toString());}

 ②编写Lua脚本,实现资格判断

--1.参数列表
--1.1。优惠券id
local voucherId = ARGV[1]
--1.2.用户id
local userId = ARGV[2]--2.数据key
--2.1 库存key
local stockKey = 'seckill:stock' .. voucherId
--2.1 订单id
local orderKey = 'seckill:order' .. voucherId--3.脚本业务
--3.1,判断库存是否充足 get stockKey
if(tonumber(redis.call('get',stockKey)) <= 0) thenreturn 1end
--3.2 判断用户是否下单 SISMEMBER orderKey userID
if(redis.call('sismember',orderKey,userId) == 1) then--3.3 存在,说明是重复下单,返回2return 2
end
--3.4 扣库存
redis.call('incrby',stockKey,-1)
--3.5 下单
redis.call('sadd',orderKey,userId)

③编码

@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {@Autowiredprivate ISeckillVoucherService iSeckillVoucherService;@Autowiredprivate RedisUUID redisUUID;@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Autowiredprivate RedissonClient redissonClient;private static final DefaultRedisScript<Long> SECKILL_SCRIPT;static {SECKILL_SCRIPT = new DefaultRedisScript<>();SECKILL_SCRIPT.setLocation(new ClassPathResource("seckill.lua"));SECKILL_SCRIPT.setResultType(Long.class);}//创建阻塞队列private BlockingDeque<VoucherOrder> orderTasks = new ArrayBlockingQueue<>(1024 * 1024);//创建线程池private static final ExecutorService SECKILL_ORDER_EXECUTOR = Executors.newSingleThreadExecutor();@PostConstructprivate void init(){SECKILL_ORDER_EXECUTOR.submit(new VoucherOrderHandler());}private class VoucherOrderHandler implements Runnable{public void run(){while (true){try {//1,获取队列中的订单信息VoucherOrder voucherOrder = orderTasks.take();//2.创建订单handleVoucherOrder(voucherOrder);} catch (InterruptedException e) {log.error("处理订单异常!",e);}}}}private void handleVoucherOrder(VoucherOrder voucherOrder) {Long userId = voucherOrder.getUserId();//创建锁对象RLock lock = redissonClient.getLock("lock:order" + userId);//获取锁boolean trylock = lock.tryLock();//判断是否获得锁成功if (!trylock) {//获取锁失败return;}try {proxy.createVoucherOrder(voucherOrder);} finally {//释放锁lock.unlock();}}private IVoucherOrderService proxy;@Overridepublic Result seckillVoucher(Long voucherId) {Long id = UserHolder.getUser().getId();//1.执行Lua脚本Long result = stringRedisTemplate.execute(SECKILL_SCRIPT,Collections.emptyList(),voucherId.toString(),id.toString());//2.判断结果为0int r = result.intValue();if (r != 0){//2.1 不为0,代表没有购买资格return Result.fail(r == 1 ? "库存不足" : "不能重复下单");}//2.2 为0,有购买资格,把下单信息保存到阻塞队列VoucherOrder voucherOrder = new VoucherOrder();//2.3,订单id----id生成器long order = redisUUID.nextid("order");voucherOrder.setVoucherId(order);//2.4,用户idvoucherOrder.setUserId(id);//2.5,商品idvoucherOrder.setVoucherId(voucherId);//2.6,放入阻塞队列orderTasks.add(voucherOrder);//获取代理对象proxy = (IVoucherOrderService) AopContext.currentProxy();//3.返回订单idreturn Result.ok(order);}@Transactionalpublic void createVoucherOrder(VoucherOrder voucherOrder) {//一人一单问题Long id = voucherOrder.getUserId();Integer count = query().eq("user_id", id).eq("voucher_id", voucherOrder.getVoucherId()).count();if(count > 0){return;}//4,如果有就减扣库存boolean sucess = iSeckillVoucherService.update().setSql("stock = stock - 1").eq("voucher_id", voucherOrder.getVoucherId()).gt("stock",0).update();if (!sucess) {return;}//6,保存进数据库save(voucherOrder);}
}

这篇关于Redis---------分布式锁Redisson的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/959120

相关文章

Redis Cluster模式配置

《RedisCluster模式配置》:本文主要介绍RedisCluster模式配置,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录分片 一、分片的本质与核心价值二、分片实现方案对比 ‌三、分片算法详解1. ‌范围分片(顺序分片)‌2. ‌哈希分片3. ‌虚

Springboot整合Redis主从实践

《Springboot整合Redis主从实践》:本文主要介绍Springboot整合Redis主从的实例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言原配置现配置测试LettuceConnectionFactory.setShareNativeConnect

Redis过期删除机制与内存淘汰策略的解析指南

《Redis过期删除机制与内存淘汰策略的解析指南》在使用Redis构建缓存系统时,很多开发者只设置了EXPIRE但却忽略了背后Redis的过期删除机制与内存淘汰策略,下面小编就来和大家详细介绍一下... 目录1、简述2、Redis http://www.chinasem.cn的过期删除策略(Key Expir

Redis指南及6.2.x版本安装过程

《Redis指南及6.2.x版本安装过程》Redis是完全开源免费的,遵守BSD协议,是一个高性能(NOSQL)的key-value数据库,Redis是一个开源的使用ANSIC语言编写、支持网络、... 目录概述Redis特点Redis应用场景缓存缓存分布式会话分布式锁社交网络最新列表Redis各版本介绍旧

Java如何从Redis中批量读取数据

《Java如何从Redis中批量读取数据》:本文主要介绍Java如何从Redis中批量读取数据的情况,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一.背景概述二.分析与实现三.发现问题与屡次改进3.1.QPS过高而且波动很大3.2.程序中断,抛异常3.3.内存消

Redis中的Lettuce使用详解

《Redis中的Lettuce使用详解》Lettuce是一个高级的、线程安全的Redis客户端,用于与Redis数据库交互,Lettuce是一个功能强大、使用方便的Redis客户端,适用于各种规模的J... 目录简介特点连接池连接池特点连接池管理连接池优势连接池配置参数监控常用监控工具通过JMX监控通过Pr

python操作redis基础

《python操作redis基础》Redis(RemoteDictionaryServer)是一个开源的、基于内存的键值对(Key-Value)存储系统,它通常用作数据库、缓存和消息代理,这篇文章... 目录1. Redis 简介2. 前提条件3. 安装 python Redis 客户端库4. 连接到 Re

Redis迷你版微信抢红包实战

《Redis迷你版微信抢红包实战》本文主要介绍了Redis迷你版微信抢红包实战... 目录1 思路分析1.1hCckRX 流程1.2 注意点①拆红包:二倍均值算法②发红包:list③抢红包&记录:hset2 代码实现2.1 拆红包splitRedPacket2.2 发红包sendRedPacket2.3 抢

Golang实现Redis分布式锁(Lua脚本+可重入+自动续期)

《Golang实现Redis分布式锁(Lua脚本+可重入+自动续期)》本文主要介绍了Golang分布式锁实现,采用Redis+Lua脚本确保原子性,持可重入和自动续期,用于防止超卖及重复下单,具有一定... 目录1 概念应用场景分布式锁必备特性2 思路分析宕机与过期防止误删keyLua保证原子性可重入锁自动

Spring Boot 整合 Redis 实现数据缓存案例详解

《SpringBoot整合Redis实现数据缓存案例详解》Springboot缓存,默认使用的是ConcurrentMap的方式来实现的,然而我们在项目中并不会这么使用,本文介绍SpringB... 目录1.添加 Maven 依赖2.配置Redis属性3.创建 redisCacheManager4.使用Sp