网站建设评审会,阳泉软件定制网站建设,旅游网站模板html,wordpress内容模型一、基本原理和实现方式对比
分布式锁#xff1a;满足分布式系统或集群模式下多进程可见并且互斥的锁。
分布式锁的核心思想就是让大家都使用同一把锁#xff0c;只要大家使用的是同一把锁#xff0c;那么我们就能锁住线程#xff0c;不让线程进行#xff0c;让程序串行…一、基本原理和实现方式对比
分布式锁满足分布式系统或集群模式下多进程可见并且互斥的锁。
分布式锁的核心思想就是让大家都使用同一把锁只要大家使用的是同一把锁那么我们就能锁住线程不让线程进行让程序串行执行这就是分布式锁的核心思路 分布式锁应该满足的条件 可见性多个线程都能看到相同的结果注意这个地方说的可见性并不是并发编程中指的内存可见性只是说多个进程之间都能感知到变化的意思 互斥互斥是分布式锁的最基本的条件使得程序串行执行 高可用程序不易崩溃时时刻刻都保证较高的可用性 高性能由于加锁本身就让性能降低所有对于分布式锁本身需要他就较高的加锁性能和释放锁性能 安全性安全也是程序中必不可少的一环
常见的三种分布式锁 Mysqlmysql 本身就带有锁机制但是由于 mysql 性能本身一般所以采用分布式锁的情况下其实使用 mysql 作为分布式锁比较少见 Redisredis 作为分布式锁是非常常见的一种使用方式现在企业级开发中基本都使用 redis 或者 zookeeper 作为分布式锁利用 setnx 这个方法如果插入 key 成功则表示获得到了锁如果有人插入成功其他人插入失败则表示无法获得到锁利用这套逻辑来实现分布式锁 Zookeeperzookeeper 也是企业级开发中较好的一个实现分布式锁的方案由于这套视频不讲解zookeeper 的原理和分布式锁的实现自己搜索了解
二、 Redis分布式锁的实现核心思路
实现分布式锁时需要实现的两个基本方法
获取锁 互斥确保只能有一个线程获取锁非阻塞尝试一次成功返回true失败返回false 释放锁 手动释放超时释放获取锁时添加一个超时时间 核心思路
利用 redis 的 setNx 方法当有多个线程进入时我们就利用该方法第一个线程进入时redis 中就有这个 key 了返回了1如果结果是1则表示他抢到了锁那么他去执行业务然后再删除锁退出锁逻辑没有抢到锁的哥们等待一定时间后重试即可。
三、实现分布式锁版本一
锁的基本接口 实现类 SimpleRedisLock
获取锁利用 setnx 方法进行加锁同时增加过期时间防止死锁此方法可以保证加锁和增加过期时间具有原子性释放锁防止删除别人的锁
public class SimpleRedisLock implements ILock{// 用户传过来的业务名称private String name;private StringRedisTemplate stringRedisTemplate;public SimpleRedisLock(String name, StringRedisTemplate stringRedisTemplate) {this.name name;this.stringRedisTemplate stringRedisTemplate;}// 锁的名称前缀private static final String KEY_PREFIX lock:;Overridepublic boolean tryLock(long timeoutSec) {// 获取线程标识String threadId Thread.currentThread().getId()String key KEY_PREFIX name;// 获取锁Boolean success stringRedisTemplate.opsForValue().setIfAbsent(key, threadId, timeoutSec, TimeUnit.SECONDS);// 直接返回 success 会有空指针的风险因为有个拆箱操作return Boolean.TRUE.equals(success);}Overridepublic void unlock() {String key KEY_PREFIX name;// 释放锁stringRedisTemplate.delete(key);}
}
修改业务代码 Overridepublic Result seckillVoucher(Long voucherId) {// 1.查询优惠券SeckillVoucher voucher seckillVoucherService.getById(voucherId);// 2.判断秒杀是否开始if (voucher.getBeginTime().isAfter(LocalDateTime.now())) {// 尚未开始return Result.fail(秒杀尚未开始);}// 3.判断秒杀是否已经结束if (voucher.getEndTime().isBefore(LocalDateTime.now())) {// 尚未开始return Result.fail(秒杀已经结束);}// 4.判断库存是否充足if (voucher.getStock() 1) {// 库存不足return Result.fail(库存不足);}Long userId UserHolder.getUser().getId();//创建锁对象(新增代码)SimpleRedisLock lock new SimpleRedisLock(order: userId, stringRedisTemplate);//获取锁对象boolean isLock lock.tryLock(5);//加锁失败if (!isLock) {return Result.fail(不允许重复下单);}try {//获取代理对象(事务)IVoucherOrderService proxy (IVoucherOrderService) AopContext.currentProxy();return proxy.createVoucherOrder(voucherId);} finally {//释放锁lock.unlock();}}四、Redis分布式锁误删情况说明
逻辑说明
持有锁的线程在锁的内部出现了阻塞导致他的锁自动释放这时其他线程线程2来尝试获得锁就拿到了这把锁然后线程2在持有锁执行过程中线程1反应过来继续执行而线程1执行过程中走到了删除锁逻辑此时就会把本应该属于线程2的锁进行删除这就是误删别人锁的情况说明
解决方案解决方案就是在每个线程释放锁的时候去判断一下当前这把锁是否属于自己如果属于自己则不进行锁的删除假设还是上边的情况线程1卡顿锁自动释放线程2进入到锁的内部执行逻辑此时线程1反应过来然后删除锁但是线程1一看当前这把锁不是属于自己于是不进行删除锁逻辑当线程2走到删除锁逻辑时如果没有卡过自动释放锁的时间点则判断当前这把锁是属于自己的于是删除这把锁。
五、解决Redis分布式锁误删问题
需求修改之前的分布式锁实现满足在获取锁时存入线程标示可以用UUID表示 在释放锁时先获取锁中的线程标示判断是否与当前线程标示一致
如果一致则释放锁如果不一致则不释放锁
核心逻辑在存入锁时放入自己线程的标识在删除锁时判断当前这把锁的标识是不是自己存入的如果是则进行删除如果不是则不进行删除。 修改实现类 SimpleRedisLock
public class SimpleRedisLock implements ILock{// 用户传过来的业务名称private String name;private StringRedisTemplate stringRedisTemplate;public SimpleRedisLock(String name, StringRedisTemplate stringRedisTemplate) {this.name name;this.stringRedisTemplate stringRedisTemplate;}// 锁的名称前缀private static final String KEY_PREFIX lock:;// 线程标识id的前缀private static final String ID_PREFIX UUID.randomUUID().toString(true) -;Overridepublic boolean tryLock(long timeoutSec) {// 获取线程标识String threadId ID_PREFIX Thread.currentThread().getId();String key KEY_PREFIX name;// 获取锁Boolean success stringRedisTemplate.opsForValue().setIfAbsent(key, threadId, timeoutSec, TimeUnit.SECONDS);// 直接返回 success 会有空指针的风险因为有个拆箱操作return Boolean.TRUE.equals(success);}Overridepublic void unlock() {String key KEY_PREFIX name;// 获取线程标识String threadId ID_PREFIX Thread.currentThread().getId();// 获取锁中的标识String id stringRedisTemplate.opsForValue().get(key);// 判断标识是否一致if (threadId.equals(id)) {// 释放锁stringRedisTemplate.delete(key);}}
}有关代码实操说明
在我们修改完此处代码后我们重启工程然后启动两个线程第一个线程持有锁后手动释放锁第二个线程 此时进入到锁内部再放行第一个线程此时第一个线程由于锁的value值并非是自己所以不能释放锁也就无法删除别人的锁此时第二个线程能够正确释放锁通过这个案例初步说明我们解决了锁误删的问题。
六、分布式锁的原子性问题
更为极端的误删逻辑说明
线程1现在持有锁之后在执行业务逻辑过程中他正准备删除锁而且已经走到了条件判断的过程中比如他已经拿到了当前这把锁确实是属于他自己的正准备删除锁但是此时他的锁到期了那么此时线程2进来但是线程1他会接着往后执行当他卡顿结束后他直接就会执行删除锁那行代码相当于条件判断并没有起到作用这就是删锁时的原子性问题之所以有这个问题是因为线程1的拿锁比锁删锁实际上并不是原子性的我们要防止刚才的情况发生
七、Lua脚本解决多条命令原子性问题 基本语法参考网站https://www.runoob.com/lua/lua-tutorial.html 释放锁的逻辑 释放锁的业务流程
1、获取锁中的线程标示
2、判断是否与指定的标示当前线程标示一致
3、如果一致则释放锁删除
4、如果不一致则什么都不做
-- 锁的 key
-- local key KEYS[1]-- 获取线程标识
-- local threadId ARGV[1]-- 获取锁中的线程标识 get key
-- local id redis.call(get, KEYS[1])-- 比较线程标识与锁中的标识是否一致
if(redis.call(get, KEYS[1]) ARGV[1]) then-- 释放锁 del key释放成功则返回 1return redis.call(del, KEYS[1])
end
return 0ps看最后这些亮的就可以上面是注释掉的 八、利用Java代码调用Lua脚本改造分布式锁
lua脚本本身并不需要大家花费太多时间去研究只需要知道如何调用大致是什么意思即可
RedisTemplate中可以利用 execute 方法去执行lua脚本参数对应关系就如下图 unlock.lua 脚本文件放在 resources目录下保证释放锁时拿锁、比锁、删锁的操作具有原子性
private static final DefaultRedisScriptLong UNLOCK_SCRIPT;static {UNLOCK_SCRIPT new DefaultRedisScript();UNLOCK_SCRIPT.setLocation(new ClassPathResource(unlock.lua));UNLOCK_SCRIPT.setResultType(Long.class);}public void unlock() {// 调用lua脚本stringRedisTemplate.execute(UNLOCK_SCRIPT,Collections.singletonList(KEY_PREFIX name),ID_PREFIX Thread.currentThread().getId());
}九、总结
基于Redis的分布式锁实现思路
利用 set nx ex 获取锁并设置过期时间保存线程标示释放锁时先判断线程标示是否与自己一致一致则删除锁 特性 利用 set nx 满足互斥性利用 set ex 保证故障时锁依然能释放避免死锁提高安全性利用 Redis 集群保证高可用和高并发特性
秒杀实现分布式锁总结一路走来利用添加过期时间防止死锁问题的发生但是有了过期时间之后可能出现误删别人锁的问题这个问题我们开始是利用删之前 通过拿锁比锁删锁这个逻辑来解决的也就是删之前判断一下当前这把锁是否是属于自己的但是现在还有原子性问题也就是我们没法保证拿锁比锁删锁是一个原子性的动作最后通过lua表达式来解决这个问题
但是目前还剩下一个问题锁不住什么是锁不住呢你想一想如果当过期时间到了之后我们可以给他续期一下比如续个30s就好像是网吧上网 网费到了之后然后说来网管再给我来10块的是不是后边的问题都不会发生了那么续期问题怎么解决呢就可以选择依赖于 redission
测试逻辑
第一个线程进来得到了锁手动删除锁模拟锁超时了其他线程会执行lua来抢锁当第一天线程利用lua删除锁时lua能保证他不能删除他的锁第二个线程删除锁时利用lua同样可以保证不会删除别人的锁同时还能保证原子性。 文章转载自: http://www.morning.rfwrn.cn.gov.cn.rfwrn.cn http://www.morning.c7500.cn.gov.cn.c7500.cn http://www.morning.ptzbg.cn.gov.cn.ptzbg.cn http://www.morning.bnfjh.cn.gov.cn.bnfjh.cn http://www.morning.sbdqy.cn.gov.cn.sbdqy.cn http://www.morning.rjqtq.cn.gov.cn.rjqtq.cn http://www.morning.fpxsd.cn.gov.cn.fpxsd.cn http://www.morning.xrhst.cn.gov.cn.xrhst.cn http://www.morning.pdxqk.cn.gov.cn.pdxqk.cn http://www.morning.fmrd.cn.gov.cn.fmrd.cn http://www.morning.pqqhl.cn.gov.cn.pqqhl.cn http://www.morning.jrsgs.cn.gov.cn.jrsgs.cn http://www.morning.xwgbr.cn.gov.cn.xwgbr.cn http://www.morning.fmqng.cn.gov.cn.fmqng.cn http://www.morning.pkrb.cn.gov.cn.pkrb.cn http://www.morning.dbbcq.cn.gov.cn.dbbcq.cn http://www.morning.thntp.cn.gov.cn.thntp.cn http://www.morning.kdxzy.cn.gov.cn.kdxzy.cn http://www.morning.lrmts.cn.gov.cn.lrmts.cn http://www.morning.ffgbq.cn.gov.cn.ffgbq.cn http://www.morning.lnwdh.cn.gov.cn.lnwdh.cn http://www.morning.rhdqz.cn.gov.cn.rhdqz.cn http://www.morning.ywndg.cn.gov.cn.ywndg.cn http://www.morning.qjbxt.cn.gov.cn.qjbxt.cn http://www.morning.knmby.cn.gov.cn.knmby.cn http://www.morning.crqbt.cn.gov.cn.crqbt.cn http://www.morning.qklff.cn.gov.cn.qklff.cn http://www.morning.plqsc.cn.gov.cn.plqsc.cn http://www.morning.qnrpj.cn.gov.cn.qnrpj.cn http://www.morning.tpmnq.cn.gov.cn.tpmnq.cn http://www.morning.wrbnh.cn.gov.cn.wrbnh.cn http://www.morning.yhljc.cn.gov.cn.yhljc.cn http://www.morning.bmncq.cn.gov.cn.bmncq.cn http://www.morning.mtrz.cn.gov.cn.mtrz.cn http://www.morning.ngkng.cn.gov.cn.ngkng.cn http://www.morning.tkfnp.cn.gov.cn.tkfnp.cn http://www.morning.ahscrl.com.gov.cn.ahscrl.com http://www.morning.txlnd.cn.gov.cn.txlnd.cn http://www.morning.cgstn.cn.gov.cn.cgstn.cn http://www.morning.qftzk.cn.gov.cn.qftzk.cn http://www.morning.qcbhb.cn.gov.cn.qcbhb.cn http://www.morning.yhsrp.cn.gov.cn.yhsrp.cn http://www.morning.lgznf.cn.gov.cn.lgznf.cn http://www.morning.jjzxn.cn.gov.cn.jjzxn.cn http://www.morning.fhtmp.cn.gov.cn.fhtmp.cn http://www.morning.tqygx.cn.gov.cn.tqygx.cn http://www.morning.nd-test.com.gov.cn.nd-test.com http://www.morning.crqpl.cn.gov.cn.crqpl.cn http://www.morning.zcqbx.cn.gov.cn.zcqbx.cn http://www.morning.slzkq.cn.gov.cn.slzkq.cn http://www.morning.tpps.cn.gov.cn.tpps.cn http://www.morning.qzpw.cn.gov.cn.qzpw.cn http://www.morning.nshhf.cn.gov.cn.nshhf.cn http://www.morning.sffwz.cn.gov.cn.sffwz.cn http://www.morning.bqqzg.cn.gov.cn.bqqzg.cn http://www.morning.nwjzc.cn.gov.cn.nwjzc.cn http://www.morning.pngdc.cn.gov.cn.pngdc.cn http://www.morning.rmtmk.cn.gov.cn.rmtmk.cn http://www.morning.lqtwb.cn.gov.cn.lqtwb.cn http://www.morning.qxwrd.cn.gov.cn.qxwrd.cn http://www.morning.gtmgl.cn.gov.cn.gtmgl.cn http://www.morning.hxlch.cn.gov.cn.hxlch.cn http://www.morning.mzmqg.cn.gov.cn.mzmqg.cn http://www.morning.qctsd.cn.gov.cn.qctsd.cn http://www.morning.pxlpt.cn.gov.cn.pxlpt.cn http://www.morning.rhjsx.cn.gov.cn.rhjsx.cn http://www.morning.kcxtz.cn.gov.cn.kcxtz.cn http://www.morning.swsrb.cn.gov.cn.swsrb.cn http://www.morning.dbrdg.cn.gov.cn.dbrdg.cn http://www.morning.rcttz.cn.gov.cn.rcttz.cn http://www.morning.rhzzf.cn.gov.cn.rhzzf.cn http://www.morning.grcfn.cn.gov.cn.grcfn.cn http://www.morning.ldwxj.cn.gov.cn.ldwxj.cn http://www.morning.nkbfc.cn.gov.cn.nkbfc.cn http://www.morning.hpspr.com.gov.cn.hpspr.com http://www.morning.sxfnf.cn.gov.cn.sxfnf.cn http://www.morning.ymfzd.cn.gov.cn.ymfzd.cn http://www.morning.mdjtk.cn.gov.cn.mdjtk.cn http://www.morning.hkswt.cn.gov.cn.hkswt.cn http://www.morning.zzfjh.cn.gov.cn.zzfjh.cn