**redis分布式锁**

简单了解下分布式锁

	1.了解分布式锁,要先了解下线程锁,线程锁主要用来给代码块或者某个方法上加锁,同一时刻仅有一个线程执行该代码块或者方法,比如synchronized,ReetarntLock;同一JVM下有效果;
	2.分布式锁,多个jvm环境下,多个线程可能不是同一个Jvm中;

简单场景:

秒杀减库存:如果是单一JVM环境,使用线程锁就能解决并发问题,如果多个JVM,线程锁就无法解决问题,假如不同JVM下的两个线程A和线程B同时做减库存操作,就会引发并发问题;

使用redis分布式锁,首先了解SETNX命令
SETNX key,value:
将 key 的值设为 value,当且仅当 key 不存在。
若给定的 key 已经存在,则 SETNX 不做任何动作。

当所有线程执行减库存操作时,先执行这条语句:
命令对应的Java程序 boolean result = stringRedisTemplate.opsForValue().setIfAbsent(key,value);
并对结果进行判断,如果执行成功返回true, 就执行减库存操作,否则返回失败的操作,最后删除这个key,其他线程继续执行;由于redis是单线程的,无论多少线程来操作redis最后都是一个个进行的;
这就是一个简单的分布式锁,但是还有很多问题需要解决;

问题1
如果减库存出现异常,导致没执行删除key操作,进而导致其他线程无法执行;try catch finally无论什么异常最终都会执行删除key操作,但是如果删除key之前服务器宕机呢?

解决:设置key的时候,同时给key设置一个过期时间;stringRedisTemplate.opsForValue().setIfAbsent(key,value,10,TimeUnit.SECONDS);设置key并设置有效时间;

问题2
假如多个线程,A线程在执行中,但是key过期了,B线程可以执行减库存操作了,但是A线程还在执行,B线程创建key成功执行操作,此时A线程正好执行删除Key操作。。导致线程A删除了线程B的Key,然后连锁反应,线程C.D.E…高并发问题来了。。。

解决:
每个线程创建一个比一个唯一标识,比如使用UUid,
设置key的时候,value就用这个标识,在删除key的时候,判断下标识与当前的key的Value是否相同,相同才执行删除key操作;

问题3
假如10秒过了 线程A 还在执行中。。。线程B进来一样存在并发问题;

解决:
这时候需要一个分线程,假如10秒过了 线程A 还在执行中。。这时候需要分个线程查看线程a的执行时间 是否还持有锁,延长失效时间,直到线程A删除key操作,使用redisson分布式锁;redisson执行问题123一系列操作。

问题4:
使用redisson分布式锁解决了问题,但是在集群模式下,假如某个线程主节点创建了一个key,然后区执行减库存操作去了,因为主从复制,如果主服务器还没同步到从服务器就宕机了,从节点变成新的主节点。。。其他线程进来因为此时的主节点没有了那个key。。。又引发了并发问题。

使用红锁 redlock 红锁的思想:就是向多个节点发送setNX,多数节点设置成功就算获取锁,单机版,redisctluster (多主从)根据key计算哈希分布到不同的主节点;哨兵模式不能使用,红锁也有缺点:假如有5个节点,线程A 向,1,2,3发送获得锁,然后节点3挂了,然后3的从升为主新3,线程B 向4,5和新的3发送 也能获得锁;

使用分布式锁防止缓存击穿,如果命中缓存直接返回数据,如果缓存击穿,100个线程,其中一个线程获取锁去查询数据库,更新缓存,释放锁 ,其他99个线程接着获取锁,查询缓存,这里两个缓存,第一个是正常的缓存,第二个是击穿后获取锁查询的缓存,这99个执行完,如果没有发生缓存击穿,再来线程就不会拿到锁了,直接第一个缓存中查询返回结果;

缓存雪崩:
指的是在某个时间点,缓存中的Key集体发生过期失效致使大量查询数据库的请求都落在了数据库上,导致数据库负载过高。这种问题产生的原因其实主要是因为大量的Key在某个时间点或者某个时间段过期失效导致的
解决办法:Key设置不同的、随机的过期失效时间,从而错开缓存中Key的失效时间点,可以在某种程度上减少数据库的查询压力。

缓存击穿:指缓存中某个频繁被访问的Key(可以称为“热点Key”),在不停地扛着前端的高并发请求,当这个Key突然在某个瞬间过期失效时,持续的高并发访问请求就“穿破”缓存,直接请求数据库,导致数据库压力在某一瞬间暴增
解决办法:候使用分布式锁就能有效防止缓存击穿,当然热点key可以设置永不失效。

缓存穿透:大量的请求访问一个不存在的数据,因为不存在缓存中肯定没有,大量的请求去数据库查询,导致数据库压力暴增;
解决办法
首次访问先访问缓存,如果缓存没有访问数据库,如果数据库不存在,就将其序列化写进缓存中,如果数据库中数据不存在,就将key写进缓存中并设置过期时间redisTemplate.opsForValue(key,"",30,TimeUnit.SECONDS);

(序列化与反序列化)
核心作用就是对象的保存和重建,序列化就是把对象转换成字节序列的过程,反序列化就是把字节序列恢复成对象的过程;
例如ObjectMapper(Jackson 的一个主要类,是一个能够将java对象序列化为JSON字符串,也能够将JSON字符串反序列化为java对象的框架)的两个方法:
json字符串转为Java对象:readValue(json字符串,类.class);
Java对象转为json字符串:writeValueAsString(class类对象)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值