面试问题浓缩总结 面试问题浓缩总结
  • Go
  • Java
  • C/C++
  • JavaScript/HTML
  • MySQL
  • Redis
  • MongoDB
  • 操作系统
  • 计算机网络
  • spring全家桶
  • mybatis
  • 中间件
  • 软件相关
  • 系统相关
  • 算法
  • 数据结构
  • 设计模式
  • CMU硕士经典100题
  • 剑指offer
  • 重点手撕代码
  • 程序员面试金典
  • 3月
  • 4月
  • 智力题
  • 业务问题
  • 一些技术
  • 安全相关
APP下载 (opens new window)
GitHub (opens new window)
  • Go
  • Java
  • C/C++
  • JavaScript/HTML
  • MySQL
  • Redis
  • MongoDB
  • 操作系统
  • 计算机网络
  • spring全家桶
  • mybatis
  • 中间件
  • 软件相关
  • 系统相关
  • 算法
  • 数据结构
  • 设计模式
  • CMU硕士经典100题
  • 剑指offer
  • 重点手撕代码
  • 程序员面试金典
  • 3月
  • 4月
  • 智力题
  • 业务问题
  • 一些技术
  • 安全相关
APP下载 (opens new window)
GitHub (opens new window)
  • MySQL

  • Redis

    • 数据类型
    • 持久化
    • 原理
    • 常见问题
    • 分布式锁,过期策略,淘汰规则
      • 分布式锁
        • Zookeeper实现分布式锁
        • Redis实现分布式锁
        • Redisson实现分布式锁
      • Redis三种过期策略
        • 定时过期
        • 惰性过期
        • 定期过期
        • 默认策略
      • Redis 6种内存淘汰策略
    • 集群和限流
    • 面试题
  • MongoDB

  • 数据库
  • Redis
小游
2021-03-20

分布式锁,过期策略,淘汰规则

# 分布式锁

在日常情况下在进程或线程中我们可以使用 synchronized (opens new window) 、Lock (opens new window) 实现并发编程,但是我, 如何在分布式集群下使用呢?可以使用 Redisson , Zookeeper ,Redis本身

分布式锁的整个流程如下图所示

1619315920885.png

分布式锁一般有下面几个特点

  • 互斥性: 同一时刻只能有一个线程持有锁
  • 可重入性: 同一节点上的同一个线程如果获取了锁之后能够再次获取锁
  • 锁超时:和J.U.C中的锁一样支持锁超时,防止死锁
  • 高性能和高可用: 加锁和解锁需要高效,同时也需要保证高可用,防止分布式锁失效
  • 具备阻塞和非阻塞性:能够及时从阻塞状态中被唤醒

# Zookeeper实现分布式锁

实现方式

利用 zookeeper 的同级节点的唯一性特性,在需要获取排他锁时,所有的客户端试图通过调用 create() 接口,在 /exclusive_lock 节点下创建临时子节点 /exclusive_lock/lock,最终只有一个客户端能创建成功,那么此客户端就获得了分布式锁。同时,所有没有获取到锁的客户端可以在 /exclusive_lock 节点上注册一个子节点变更的 watcher 监听事件,以便重新争取获得锁。

1、持久节点:客户端断开连接zk不删除persistent类型节点

2、临时节点:客户端断开连接zk删除ephemeral类型节点

3、顺序节点:节点后面会自动生成类似0000001的数字表示顺序

4、节点变化的通知:客户端注册了监听节点变化的时候,会调用回调方法

image.png

缺点

频繁的创建删除节点,加上注册watch事件,对于zookeeper集群的压力比较大,性能也比不上Redis实现的分布式锁。

可以参考这篇:

9、Zookeeper分布式锁原理浅析_Ron.Zheng-CSDN博客 (opens new window)

# Redis实现分布式锁

本身原理也比较简单,Redis 自身就是一个单线程处理器,具备互斥的特性,通过setNX,exist等命令就可以完成简单的分布式锁,处理好超时释放锁的逻辑即可。

  • SETNX

    SETNX 是SET if Not eXists的简写,日常指令是SETNX key value,如果 key 不存在则set成功返回 1,如果这个key已经存在了返回0。

  • SETEX

    SETEX key seconds value 表达的意思是 将值 value 关联到 key ,并将 key 的生存时间设为多少秒。如果 key 已经存在,setex命令将覆写旧值。并且 setex是一个原子性(atomic)操作。

  • 加锁

    一般就是用一个标识唯一性的字符串比如UUID 配合 SETNX 实现加锁。

  • 解锁

    这里用到了LUA脚本,LUA可以保证是原子性的,思路就是判断一下Key和入参是否相等,是的话就删除,返回成功1,0就是失败。

缺点

这个锁是无法重入的,且自己实心的话各种边边角角都要考虑到,所以了解个大致思路流程即可,工程化还是用开源工具包就行。

存在的问题

普通利用Redis实现分布式锁的时候,我们可能会为某个锁指定某个key,当线程获取锁并执行完业务逻辑代码的时候,将该锁对应的key删除掉来释放锁。lock->set(key),成功->执行业务,业务执行完毕->unlock->del(key)。

第一个问题,因为我们的业务不知道要执行多久才能结束,所以这个key我们一般不会设置过期时间。这样如果在执行业务的过程中,业务机器宕机,unlock操作不会执行,所以这个锁不会被释放,其他机器拿不到锁,从而形成了死锁。

第二个问题,如果Redis宕机,三种情况:

①Redis是单点模式

②Redis是集群模式,master在获取到一把锁之后(写操作成功后),在没来得及把该锁同步到slave之前就宕掉,这个时候slave没有锁,这把锁失效了……

③Redis是集群模式,而整个集群都宕机,那么就没救了……

Redisson(1)分布式锁——如何解决死锁问题_xxcupid的博客-CSDN博客_redisson分布式锁缺陷解决 (opens new window)

# Redisson实现分布式锁

Redisson 是在Redis基础上的一个服务,采用了基于NIO的Netty框架,不仅能作为Redis底层驱动客户端,还能将原生的RedisHash,List,Set,String,Geo,HyperLogLog等数据结构封装为Java里大家最熟悉的映射(Map),列表(List),集(Set),通用对象桶(Object Bucket),地理空间对象桶(Geospatial Bucket),基数估计算法(HyperLogLog)等结构。

image.png

加锁解锁的流程如下

image.png

redis 分布式锁的 5个坑,真是又大又深 - 程序员内点事 - 博客园 (cnblogs.com) (opens new window)

这里应该会问你Redisson解决了啥问题

  1. 可以实现可重入加锁机制
  2. 在一个分布式环境下,假如一个线程获得锁后,突然服务器宕机了,那么这个时候在一定时间后这个锁会自动释放,你也可以设置锁的有效时间(不设置默认30秒),这样的目的主要是防止死锁的发生。

# Redis三种过期策略

# 定时过期

每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即对key进行清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。

# 惰性过期

只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。

# 定期过期

每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。

expires字典会保存所有设置了过期时间的key的过期时间数据,其中 key 是指向键空间中的某个键的指针,value是该键的毫秒精度的UNIX时间戳表示的过期时间。键空间是指该Redis集群中保存的所有键。

# 默认策略

Redis采用的过期策略:惰性删除 + 定期删除。memcached采用的过期策略:惰性删除。

# Redis 6种内存淘汰策略

内存淘汰策略是指在Redis的用于缓存的内存不足时,怎么处理需要新写入且需要申请额外空间的数据

过期策略指的是如何让key过期(此时内存可能有很多),而淘汰就是当我们内存不够时淘汰哪些数据(此时key可能并没有过期)

  1. volatile-lru(常考):从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
  2. volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
  3. volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
  4. allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
  5. allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰 6、no-enviction(驱逐):禁止驱逐数据,不删除的意思。
编辑 (opens new window)
上次更新: 2021/04/25, 22:04:03
常见问题
集群和限流

← 常见问题 集群和限流→

Theme by Vdoing | Copyright © 2021-2021 小游
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式