【Redis】分布式锁

发布时间:2023年12月23日

分布式锁

为什么要使用分布式锁,或者分布式锁的使用场景?

定时任务。在分布式场景下,只控制一台服务器执行定时任务,这就需要分布式锁

要控制定时任务在同一时间只有 1 个服务器能执行,方案有哪些?

  1. 分离定时任务程序和主程序,只在 1 个服务器运行定时任务。成本太大
  2. 写死配置。每个服务器都执行定时任务,但是只有 ip 符合配置的服务器才真实执行业务逻辑,其他的直接返回。成本最低;但是我们的 IP 可能是不固定的,会把 IP 写的太死了
  3. 动态配置,配置是可以轻松的、很方便地更新的(代码无需重启),但是只有 ip 符合配置的服务器才真实执行业务逻辑。问题:服务器多了、IP 不可控还是很麻烦,还是要人工修改
    • 配置写在数据库 、Redis、配置中心(Nacos、Apollo、Spring Cloud Config)
  4. 分布式锁,只有抢到锁的服务器才能执行业务逻辑。坏处:增加成本;好处:不用手动配置,多少个服务器都一样。

Java 实现锁:synchronized 关键字、并发包的类
问题:只对单个 JVM 有效

分布式锁实现的关键

抢锁机制

怎么保证同一时间只有 1 个服务器能抢到锁?

核心思想 就是:先来的人先把数据改成自己的标识(服务器 ip),后来的人发现标识已存在,就抢锁失败,继续等待。等先来的人执行方法结束,把标识清空,其他的人继续抢锁。

实现方式有:

  • MySQL 数据库:select for update 行级锁(最简单)
  • Redis 实现:内存数据库,读写速度快 。支持 setnxlua 脚本,比较方便我们实现分布式锁。

实现细节:

  1. 锁一定要加过期时间
  2. 用完锁要释放,释放的时候检查是否是自己的锁
  3. 如果方法执行时间过长,锁要能进行续约
  4. Redis 如果是集群(而不是只有一个 Redis),如果分布式锁的数据不同步怎么办? 使用红锁

Redisson实现分布式锁

Redisson 是一个 java 操作 Redis 的客户端,提供了大量的分布式数据集来简化对 Redis 的操作和使用,可以让开发者像使用本地集合一样使用 Redis,完全感知不到 Redis 的存在。

使用方式

  1. spring boot starter 引入(不推荐,版本迭代太快,容易冲突)链接
  2. 使用Redisson依赖 链接

RedissonConfig

/**
 * Redisson 配置
 */
@Configuration
@ConfigurationProperties(prefix = "spring.redis")
@Data
public class RedissonConfig {

    private String host;

    private String port;

    @Bean
    public RedissonClient redissonClient() {
        // 1. 创建配置
        Config config = new Config();
        String redisAddress = String.format("redis://%s:%s", host, port);
        //  使用单个Redis,没有开集群 useClusterServers()  设置地址和使用库
        config.useSingleServer().setAddress(redisAddress).setDatabase(3);
        // 2. 创建实例
        RedissonClient redisson = Redisson.create(config);
        return redisson;
    }
}

Redisson测试


@SpringBootTest
public class RedissonTest {

    @Resource
    private RedissonClient redissonClient;

    @Test
    void test() {
        // list,数据存在本地 JVM 内存中
        List<String> list = new ArrayList<>();
        list.add("yupi");
        System.out.println("list:" + list.get(0));

        list.remove(0);

        // 数据存在 redis 的内存中
        RList<String> rList = redissonClient.getList("test-list");
        rList.add("yupi");
        System.out.println("rlist:" + rList.get(0));
        rList.remove(0);

        // map
        Map<String, Integer> map = new HashMap<>();
        map.put("yupi", 10);
        map.get("yupi");

        RMap<Object, Object> map1 = redissonClient.getMap("test-map");

        // set

        // stack


    }
}

看门狗机制

Redisson会开一个监听线程,如果锁的方法还没执行完,就帮你重置 redis 锁的过期时间。

原理:

  • 监听当前线程,默认过期时间是 30 秒,每 10 秒续期一次(补到 30 秒)
  • 如果线程挂掉(注意 debug 模式也会被它当成服务器宕机),则不会续期
文章来源:https://blog.csdn.net/weixin_44147535/article/details/135171528
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。