分布式锁

分布式锁 基于数据库 乐观锁(数据更新之前先查状态,起冲突就更新失败) 悲观锁(for update,会占有,不利于并发) 获取锁的前提:结果集中的数据没有使用排他锁或共享锁时,才能获取锁,否则将会阻塞。 需要注意的是, FOR UPDATE 生效需要同时满足两个条件时才生效: 数据库的引擎为 innoDB 操作位于事务块中(BEGIN/COMMIT) 实现: 表加唯一索引 加锁:insert语句,报错证明加锁失败 解锁:delete 实现简单 数据库开销大、性能不高 没有自动超时机制,有死锁风险 基于Redis缓存 加锁:SET k v EX 300 NX 解锁:DELETE 可能会删除不是这个线程拥有的锁 解决:验证value(写value的时候也得保证唯一) 如果这个master节点由于某些原因发生了主从切换,那么就会出现锁丢失的情况: 在Redis的master节点上拿到了锁; 但是这个加锁的key还没有同步到slave节点; master故障,发生故障转移,slave节点升级为master节点; 导致锁丢失。 基于Zookeper 获得锁和等待 创建一个持久结点 C1获得锁:从持久结点创建临时结点Lock1。C1查找所有临时结点并排序,是最前的 一个就获得锁 C2获取锁,创建临时结点Lock2。查找所有临时结点并排序,发现不是最前,创建watch观察最近的Lock1 C3获取锁,创建临时结点观察最近的Lock2 形成等待队列 删除锁 C1调用删除Lock1 的指令(临时结点在C1断开连接时也会自动删除Lock1) C2因为watch着Lock1,删除后立即得到通知 再次查询所有临时结点,并排序,发现自己是最小的,获得锁 acquire方法用户获取锁,release方法用于释放锁。 性能不高,因为要创建临时结点 对于客户端Client断开的情况,Curator客户端支持多种重试策略。多次重试之后还不行的话才会删除临时节点。

2020/02/09 · Aris

缓存穿透、缓存击穿、缓存雪崩

缓存机制 受制于数据库操作速度(磁盘操作)和连接池数量限制,在一些高并发场景,通常会设计缓存(内存操作)来减轻DB压力,进行流量削峰。 本质上来说就是更多地延缓用户请求,将请求拦截在上游,以及层层过滤用户的访问需求,遵从“最后落地到数据库的请求数要尽量少”的原则。 穿透:利用不存在的key去攻击mysql数据库 雪崩:缓存中的很多key失效,导致数据库负载过重宕机 击穿:在正常的访问情况下,如果缓存失效,mysql自我保护,重启缓存的过程 缓存基本流程 graph LR; start(开始)--> case1{缓存内是否有数据}; do2-->ends(结束); case1--否-->case2{数据库内是否有数据}; case2--是-->do1[更新缓存数据]; do1-->do2[返回正常数据]; case2--否-->do3[返回空数据]; do3-->ends; case1--是-->do2; do1-->ends; 缓存穿透 概念场景 所谓穿透,指的就是流量没有命中缓存直接到达DB时: 正常流程:缓存未读取到->到DB查->查到后回写缓存 穿透流程:缓存未读取到->到DB查->DB也没查到->缓存内也不能有这个值。 这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。 这种情况常见于攻击,或者业务BUG。比如重复使用uid=-1或uid特别大或不存在的数据请求查询。 解决方案 创建uid的时候是有约束规范的,对于不在约束范围内的做拦截处理; 用布隆过滤器,使用一个足够大的bitmap,用于存储可能访问的key,不存在的key直接被过滤; 访问key未在DB查询到值,也将空值写进缓存 设置较短的缓存过期时间。 缓存击穿 概念场景 热点key,在不停的扛着大并发,大并发集中对这一个点进行访问 在一些特殊的时间点,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。 解决方案 设置热点数据永远不过期。 在访问key之前,采用SETNX(set if not exists)来设置另一个短期key来锁住当前key的访问,访问结束再删除该短期key。 缓存雪崩 概念场景 大量的key设置了相同的过期时间,同时/大批到过期时间,查询数据量巨大,引起数据库压力过大甚至down机 缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。 解决方案 缓存数据的过期时间设置随机,不会集中在同一时刻失效。 使用分布式缓存,分布式缓存中每一个节点只缓存部分的数据,当某个节点宕机时可以保证其它节点的缓存仍然可用。 设置热点数据永远不过期(或通过观察用户行为,合理设置缓存过期时间)。 预热数据:在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。 限流降级:这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。 参考 帮你解读什么是Redis缓存穿透和缓存雪崩(包含解决方案) 缓存穿透、缓存击穿、缓存雪崩概念及解决方案 分布式锁和缓存击穿 缓存穿透、缓存击穿、缓存雪崩区别和解决方案 应对缓存击穿的解决方法

2020/01/02 · Aris

服务降级、限流、熔断

2019/12/21 · Aris