引子: 解决缓存击穿问题

synchronized (this){代码块}

public synchronized Map<String,List<Catelog2Vo>> getCatalog]sonFromDb()

:SpringBoot所有的组件在容器中都是单例的。

优点:速度快

所以这样只能有一个人拿到锁进入逻辑, 但是也是在单体服务下可行,在分布条件下,几个服务就生成几把锁(应该问题不大)

测试1: 有3个人进入拿到了锁

原因:  拿到锁的那个线程确认缓存没有,去查数据库,查询后释放锁 ,刚释放还没来得及往缓存中存数据, 另一个线程就拿到锁再进行操作

解决方案:  把 (往缓存中存数据) 放到锁中    [稍微影响效率]

这里推荐下阿里的jetcache 全部都封装好了

redis分布式锁

分布式中,即使多个服务,也只让一个人去查查数据

原理: redis  set hello world NX

先看缓冲中有没有, 没有的话就占分布式锁,执行下面的逻辑

可能遇到问题

1.拿到锁之后,执行完逻辑,在释放锁之前突然断电,无法释放锁导致其他服务无法获取,造成死锁  (解决办法: 锁设置过期时间:30S)

还有问题: 若是刚加锁,还没来得及设置过期时间,就闪断

解决办法:  原子操作

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

删锁的时候遇到问题

1.假设业务时间过长,锁自动过期了  就会有其他线程进来

2.自己的锁过期了,删掉了其他人正在用的锁

解决办法:可以加V值改为UUID,删锁执行判断UUID进行删除

String uuid = UUID.randomUUID().toString();

    新问题(无原子性): 因为数据传输慢的原因,去redis判断uuid是否是自己,判断后,在然后往回传结果true的路上  ,K过期了,其他人占了锁 , 此时,删除的锁,也是别人的锁

    解决办法: 删锁的原子性,利用lua脚本 

                String luaScript = "if redis.call('get',KEYS[1]) == ARGV[1]\n" +
"then\n" +
" return redis.call('del',KEYS[1])\n" +
"else\n" +
" return 0\n" +
"end";
// 删除锁
redisTemplate.execute(new DefaultRedisScript<Long>(luaScript, Long.class), Arrays.asList("LOCK_KEY_CATALOG_JSON"), uuid);
Arrays.asList("LOCK_KEY_CATALOG_JSON") ==  uid  就删除锁

加锁保证原子性,解锁保证原子性

问题: 锁的自动续期问题,  [把时间放长一点]

Redisson(单机redis)

导入依赖

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

配置

@Configuration
public class MyRedissonConfig { /**
* 注入客户端实例对象
*/
@Bean(destroyMethod="shutdown")
public RedissonClient redisson(@Value("${spring.redis.host}") String host, @Value("${spring.redis.port}")String port) throws IOException {
// 1.创建配置
Config config = new Config();
config.useSingleServer().setAddress("redis://" + host + ":" + port);// 单节点模式
// config.useSingleServer().setAddress("rediss://" + host + ":" + port);// 使用安全连接
// config.useClusterServers().addNodeAddress("127.0.0.1:7004", "127.0.0.1:7001");// 集群模式
// 2.创建redisson客户端实例
RedissonClient redissonClient = Redisson.create(config);
return redissonClient;
}
}

测试

RedissonClient redissonClient

实现分布式锁

锁的自动续期+30S (看门狗 : 10秒续一次)   ,一旦设置过期时间,看门狗会失效

可重入锁(抢占一把锁)

    public Map<String, List<Catalog2VO>> getCatalogJsonFromDBWithRedissonLock() {
// 1.抢占分布式锁,同时设置过期时间
RLock lock = redisson.getLock("LOCK_KEY_CATALOG_JSON");
lock.lock(30, TimeUnit.SECONDS);
try {
// 2.查询DB
Map<String, List<Catalog2VO>> result = getCatalogJsonFromDB();
return result;
} finally {
// 3.释放锁
lock.unlock();
}
}
RLock lock = redisson.getLock("anyLock");
// 最常见的使用方法
lock.lock();
// 加锁以后10秒钟自动解锁
// 无需调用unlock方法手动解锁
lock.lock(10, TimeUnit.SECONDS); // 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {
try {
...
} finally {
lock.unlock();
}
}

公平锁(先到先得)

//所有请求线程会在一个队列中排队,当某个线程出现宕机时,Redisson会等待5秒后继续下一个线程,
也就是说如果前面有5个线程都处于等待状态,那么后面的线程会等待至少25秒。 RLock fairLock = redisson.getFairLock("anyLock");
// 最常见的使用方法
fairLock.lock(); // 10秒钟以后自动解锁
// 无需调用unlock方法手动解锁
fairLock.lock(10, TimeUnit.SECONDS); // 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = fairLock.tryLock(100, 10, TimeUnit.SECONDS);
...
fairLock.unlock();

读写锁(写锁控制读锁)

RReadWriteLock rwlock = redisson.getReadWriteLock("anyRWLock");
// 最常见的使用方法
rwlock.readLock().lock();
// 或
rwlock.writeLock().lock();
// 10秒钟以后自动解锁
// 无需调用unlock方法手动解锁
rwlock.readLock().lock(10, TimeUnit.SECONDS);
// 或
rwlock.writeLock().lock(10, TimeUnit.SECONDS); // 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = rwlock.readLock().tryLock(100, 10, TimeUnit.SECONDS);
// 或
boolean res = rwlock.writeLock().tryLock(100, 10, TimeUnit.SECONDS);
...
lock.unlock(); //抽象lock(代表读写锁)

闭锁

等待全部成才能加锁

RCountDownLatch latch = redisson.getCountDownLatch("anyCountDownLatch");
latch.trySetCount(5); //等5个走完才结束
latch.await(); // 在其他线程或其他JVM里()
RCountDownLatch latch = redisson.getCountDownLatch("anyCountDownLatch");
latch.countDown();

信号量

做限流

提前在redis中存入信号量{"semaphore":"10"}

RSemaphore semaphore = redisson.getSemaphore("semaphore");
semaphore.acquire();
//获取信号量-1,获取不到就一直等待
semaphore.tryAcquire(23, TimeUnit.SECONDS);  /.尝试等待23秒,

//其他线程 semaphore.release(); //释放信号量+1

可过期性信号量

红锁(大多数锁上就行)

注意锁的粒度

办法:  要么加锁牺牲效率,    要么允许暂时的不一致,有最终一致性就行     丨(加了缓存过期时间)

问题: 脏数据问题,  2号慢  , 3号读到了脏数据,也更新了脏数据     丨 (加了缓存过期时间)

经常修改的数据,直接读数据库就行

本地锁 & 分布式锁的更多相关文章

  1. 图解Janusgraph系列-并发安全:锁机制(本地锁+分布式锁)分析

    图解Janusgraph系列-并发安全:锁机制(本地锁+分布式锁)分析 大家好,我是洋仔,JanusGraph图解系列文章,实时更新~ 图数据库文章总目录: 整理所有图相关文章,请移步(超链):图数据 ...

  2. 本地锁、redis分布式锁、zk分布式锁

    本地锁.redis分布式锁.zk分布式锁 https://www.cnblogs.com/yjq-code/p/dotnetlock.html 为什么要用锁? 大型站点在高并发的情况下,为了保持数据最 ...

  3. SpringBoot--防止重复提交(锁机制---本地锁、分布式锁)

    防止重复提交,主要是使用锁的形式来处理,如果是单机部署,可以使用本地缓存锁(Guava)即可,如果是分布式部署,则需要使用分布式锁(可以使用zk分布式锁或者redis分布式锁),本文的分布式锁以red ...

  4. .net下 本地锁、redis分布式锁、zk分布式锁的实现

    为什么要用锁? 大型站点在高并发的情况下,为了保持数据最终一致性就需要用到技术方案来支持.比如:分布式锁.分布式事务.有时候我们在为了保证某一个方法每次只能被一个调用者使用的时候,这时候我们也可以锁来 ...

  5. 分布式改造剧集2---DIY分布式锁

    前言: ​ 好了,终于又开始播放分布式改造剧集了.前面一集中(http://www.cnblogs.com/Kidezyq/p/8748961.html)我们DIY了一个Hessian转发实现,最后我 ...

  6. Redisson分布式锁实现

    转: Redisson分布式锁实现 2018年09月07日 15:30:32 校长我错了 阅读数:3303   转:分布式锁和Redisson实现 概述 分布式系统有一个著名的理论CAP,指在一个分布 ...

  7. 【转】Redis学习笔记(四)如何用Redis实现分布式锁(1)—— 单机版

    原文地址:http://bridgeforyou.cn/2018/09/01/Redis-Dsitributed-Lock-1/ 为什么要使用分布式锁 这个问题,可以分为两个问题来回答: 为什么要使用 ...

  8. 分布式锁和Redisson实现

    http://thoreauz.com/2017/08/20/language/java/%E5%9F%BA%E7%A1%80/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81 ...

  9. redission 分布式锁

    https://my.oschina.net/haogrgr/blog/469439   分布式锁和Redisson实现 Aug 20, 2017 CONTENTS 概述 分布式锁特性 Redis实现 ...

  10. [转]分布式锁-RedisLockRegistry源码分析

    前言 官网的英文介绍大概如下: Starting with version 4.0, the RedisLockRegistry is available. Certain components (f ...

随机推荐

  1. 3.2 Windows驱动开发:内核CR3切换读写内存

    CR3是一种控制寄存器,它是CPU中的一个专用寄存器,用于存储当前进程的页目录表的物理地址.在x86体系结构中,虚拟地址的翻译过程需要借助页表来完成.页表是由页目录表和页表组成的,页目录表存储了页表的 ...

  2. x64dbg 2022 最新版编译方法

    x64dbg 调试器的源码编译很麻烦,网络上的编译方法均为老版本,对于新版本来说编译过程中会出现各种错误,编译的坑可以说是一个接着一个,本人通过研究总结出了一套编译拳法可以完美编译输出,不过话说回来x ...

  3. 进程锁(互斥锁)(Python)

    3:# 抢票示例 import json import time from multiprocessing import Process,Lock def search(i): with open(' ...

  4. 多进程|基于非阻塞调用的轮询检测方案|进程等待|重新理解挂起|Linux OS

    说在前面 今天给大家带来操作系统中进程等待的概念,我们学习的操作系统是Linux操作系统. 我们今天主要的目标就是认识wait和waitpid这两个系统调用. 前言 那么这里博主先安利一下一些干货满满 ...

  5. T406696 『STA - R4』冰红茶 题解

    题目链接:冰红茶 比较有意思的套路题(前提是接触过) 首先,一个最基本的线段树包含两种操作的板子要会,分别为区间赋值与区间加,同时维护区间最值.这个挺简单的,区间赋值优先级高于区间加,可以将区间加覆盖 ...

  6. P3730 曼哈顿交易 题解

    题目链接:曼哈顿交易 比较容易想的题,观察下首先不带修改,考虑维护的东西:次数作为权值,这玩意很显然很难在线维护,考虑下离线算法.看到这种和次数有关的权值,典型的单点加入和删除是非常好找到变化的,那么 ...

  7. 使用DoraCloud构建远程办公桌面云

    公司总部在上海.员工分布在各地.部分员工需要远程办公.为了实现远程办公,有几种备选方案. 方案1.在员工的PC上安装向日葵.ToDesk之类的远程工具. 方案2.公司总部提供VPN,员工通过VPN拨号 ...

  8. Linux的yum源配置总结

    本文总结归纳了一系列Linux系统的各种yum配置,虽然不一定是标准配置,但都是自己曾真实测过可用的. 1.RHEL6 本地 yum源配置 2.CentOS6 本地 yum源配置 3.OEL5 本地 ...

  9. ASP.NET Core分布式项目实战(oauth2 + oidc 实现 client部分)--学习笔记

    任务16:oauth2 + oidc 实现 client部分 实现 client 之前启动一下上一节的 server,启动之前需要清除一些代码 注释 Program 的 MigrateDbContex ...

  10. 《ASP.NET Core 微服务实战》-- 读书笔记(第6章)

    第 6 章 事件溯源与 CQRS 在本章,我们来了解一下随着云平台一同出现的设计模式 我们先探讨事件溯源和命令查询职责分离(CQRS)背后的动机与哲学 事件溯源简介 事实由事件溯源而来 我们大脑就是一 ...