博客
关于我
day104-缓存-分布式锁-分布式锁原理与使用
阅读量:760 次
发布时间:2019-03-23

本文共 2718 字,大约阅读时间需要 9 分钟。

分布式锁的实现与优化

分布式锁是解决多线程环境下共享资源竞争问题的一种重要手段。在分布式系统中,由于服务之间的通信和数据一致性问题,传统的锁机制难以实现效果。Redis的分布式锁提供了一种经济高效的解决方案。

分布式锁的一个基本原理是多个服务内的多个线程访问某一资源时,都会访问同一个缓存位置。第一个访问的线程会将资源缓存到Redis中,其余线程尝试访问时会发现缓存已存在,从而获得分布式锁。

Redis实现方式

Redis的分布式锁通过set命令实现,采用NX(Remote inorder neighbor to)选项。set nx表示只有在键不存在时才会执行命令。如果多个客户端同时尝试获取锁,只有一客户端能成功,其他客户端会进入等待状态。

实现步骤
  • 复制链接:可通过命令cp将链接复制到多个客户端上。
  • 切换权限:确保客户端拥有Redis访问权限,通常需切换至root权限。
  • 发送命令:在切换权限后,发送同一命令到所有客户端,确保锁的获取同步。
  • 验证结果:若命令返回nil,则表示已存在锁,进入等待状态;否则,成功获取锁。
  • 验证过程

    当客户端接入后,重复发送相应命令,结果如下:

    • 一个客户端先接入并成功获取锁。
    • 其他客户端尝试时,均返回nil,表明锁已存在并被占用。

    通过Redis的NX选项,可以轻松实现分布式锁的基本功能。

    代码实现与优化

    基础实现代码如下:

    @Overridepublic Map
    > getCatalogJson() { Map
    > catalogJsonFromCache = null; synchronized (this) { ValueOperations
    opsForValue = stringRedisTemplate.opsForValue(); String catalogJson = opsForValue.get("catalogJson"); if (StringUtils.isEmpty(catalogJson)) { catalogJsonFromDb = getCatalogJsonFromDbWithRedisLock(); return catalogJsonFromDb; } else { Map
    > catalogJsonFromCache = JSON.parseObject(catalogJson, new TypeReference
    >(){}); return catalogJsonFromCache; } } return catalogJsonFromCache;}public Map
    > getCatalogJsonFromDbWithRedisLock() throws InterruptedException { Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", "111"); if (lock) { Map
    > dataFromDb = getDataFromDb(); stringRedisTemplate.delete("lock"); return dataFromDb; } else { Thread.sleep(100); return getCatalogJsonFromDbWithRedisLock(); }}

    问题分析:

  • 锁删除异常:若数据库操作中出现异常或系统断电,锁可能无法正确删除,导致死锁问题。
  • 过期时间设置:设置锁的过期时间需确保原子性,以防止超时后出现锁互相等待。
  • 优化代码
    public Map
    > getCatalogJsonFromDbWithRedisLock() throws InterruptedException { Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", "111", 30, TimeUnit.SECONDS); String uuid = UUID.randomUUID().toString(); if (lock) { Map
    > dataFromDb; try { dataFromDb = getDataFromDb(); } finally { String script = "if redis.call('get', KEYS[1]) == ARGV[1]\n" + "then\n" + " return redis.call('del', KEYS[1])\n" + "else\n" + " return 0\n" + "end"; Long lockDeleteResult = stringRedisTemplate.execute(new DefaultRedisScript
    ( .script, Arrays.asList("lock"), uuid)); } return dataFromDb; } else { Thread.sleep(200); return getCatalogJsonFromDbWithRedisLock(); }}

    优化说明:

  • 设置过期时间:通过set命令设置30秒过期时间确保锁自动释放。
  • 原子性删除锁:使用Redis脚本在获取锁时即进行原子性删除操作,防止死锁。
  • 唯一标识 UUID:为锁赋予唯一标识,确保删除锁时仅删除当前线程的锁。
  • 总结

    通过以上优化,分布式锁方案已实现并完善,基本问题已解决,适合实际应用使用。

    转载地址:http://sdezk.baihongyu.com/

    你可能感兴趣的文章
    Mysql学习总结(33)——阿里云centos配置MySQL主从复制
    查看>>
    Mysql学习总结(35)——Mysql两千万数据优化及迁移
    查看>>
    Mysql学习总结(36)——Mysql查询优化
    查看>>
    Mysql学习总结(37)——Mysql Limit 分页查询优化
    查看>>
    Mysql学习总结(38)——21条MySql性能优化经验
    查看>>
    Mysql学习总结(39)——49条MySql语句优化技巧
    查看>>
    Mysql学习总结(3)——MySql语句大全:创建、授权、查询、修改等
    查看>>
    Mysql学习总结(40)——MySql之Select用法汇总
    查看>>
    Mysql学习总结(41)——MySql数据库基本语句再体会
    查看>>
    Mysql学习总结(42)——MySql常用脚本大全
    查看>>
    Mysql学习总结(43)——MySQL主从复制详细配置
    查看>>
    Mysql学习总结(44)——Linux下如何实现mysql数据库每天自动备份定时备份
    查看>>
    Mysql学习总结(45)——Mysql视图和事务
    查看>>
    Mysql学习总结(46)——8种常被忽视的SQL错误用法
    查看>>
    Mysql学习总结(48)——MySql的日志与备份还原
    查看>>
    Mysql学习总结(49)——从开发规范、选型、拆分到减压
    查看>>
    Mysql学习总结(4)——MySql基础知识、存储引擎与常用数据类型
    查看>>
    Mysql学习总结(50)——Oracle,mysql和SQL Server的区别
    查看>>
    Mysql学习总结(51)——Linux主机Mysql数据库自动备份
    查看>>
    Mysql学习总结(52)——最全面的MySQL 索引详解
    查看>>