这个分布式锁实现的有问题吗?

img

img

img

只要不是同一个id,就会加锁,这个分布式只对同一个id有效。这样的实现优问题吗?

又看到你问分布式锁的问题了,你的问题是“只要不是同一个id,就会加锁,这个分布式只对同一个id有效。这样的实现优问题吗?” 我就只针对这个回答,要清楚分布式锁的使用场景,如图如果对单个订单ID加锁,目标场景应该是,在同一个时间只允许一个线程对此ID的数据操作。和用户,和共享资源是没有关系的。你也可以直接对当前用户ID加锁,那么不管在多个线程中或者多个服务中,同一个时间只允许当前用户在一个线程内操作数据。

锁的过期时间可能有点偏长了,可以短点,正常业务逻辑不应该处理这么久还没有处理完吧,看到你在方法最后也加了finally去释放锁,应该是ok的

  • 帮你找了个相似的问题, 你可以看下: https://ask.csdn.net/questions/743703
  • 这篇博客也不错, 你可以看下分布式ID生成服务,真的有必要搞一个
  • 除此之外, 这篇博客: 分布式系统全局唯一ID简介、特点、生成方式、各自的优劣势中的 此方案是某团使用的分布式ID算法,小伙伴们如果想了解更深,可以去网上搜下,这里应该介绍了比较详细了。 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
  • 当然此方案美团还做了一些别的优化,监控ID使用频率,自动设置步长step,从而达到对ID节省使用。

     

     

     

     

     

     

     

  • 您还可以看一下 李月喜老师的企业微信开发第三方应用开发篇课程中的 推广二维码,推广包id生成安装链接【后端】小节, 巩固相关知识点
  • 以下回答由chatgpt基于相关博客总结生成:

    回答:

    对于问题中提到的分布式锁实现,只对同一个id有效,对于不同的id都会加锁的实现方式,存在一个问题就是可能会对目标系统造成不可预知的影响。比如,在某个高并发场景下,可能会产生大量相同id的请求,导致所有这些请求都被阻塞,从而影响到整个系统的运行。

    因此,建议在设计分布式锁时,需要保证加锁的操作只针对特定的资源,而不是针对相同的id。一种常见的做法是使用分布式缓存来实现分布式锁,如Redis等,可以使用具有原子性操作的setnx函数来加锁,如果返回1,则说明对应的key成功加锁,然后就可以进行业务处理。如果返回0,则说明已经被加锁了,需要等待一段时间后重试。

    下面是一个具体的解决方案示例,使用Redis实现加锁操作:

    1. 引入Redis依赖
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>${jedis.version}</version>
    </dependency>
    
    1. 封装Redis锁操作
    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.params.SetParams;
    
    public class RedisLock {
    
        private final Jedis jedis;
        private final SetParams setParams;
    
        private static final String LOCK_SUCCESS = "OK";
        private static final String SET_IF_NOT_EXIST = "NX";
        private static final String SET_WITH_EXPIRE_TIME = "PX";
        private static final Long RELEASE_SUCCESS = 1L;
    
        public RedisLock(Jedis jedis, int timeout) {
            this.jedis = jedis;
            // 将所有参数设置到setParams中
            this.setParams = new SetParams()
                    .nx()
                    .px(timeout);
        }
    
        /**
         * 尝试加锁
         * @param key 锁的名称
         * @param requestId 请求的唯一标识
         * @return true表示加锁成功,false表示加锁失败
         */
        public boolean tryLock(String key, String requestId) {
            String result = jedis.set(key, requestId, setParams);
            return LOCK_SUCCESS.equals(result);
        }
    
        /**
         * 释放锁
         * @param key 锁的名称
         * @param requestId 请求的唯一标识
         * @return true表示释放锁成功,false表示释放锁失败
         */
        public boolean releaseLock(String key, String requestId) {
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                    "return redis.call('del', KEYS[1]) " +
                    "else " +
                    "return 0 " +
                    "end";
            Object result = jedis.eval(script, Collections.singletonList(key), Collections.singletonList(requestId));
            return RELEASE_SUCCESS.equals(result);
        }
    }
    
    1. 使用Redis锁
    import redis.clients.jedis.Jedis;
    
    public class BusinessLogic {
    
        private static final int LOCK_TIMEOUT = 10000; // 锁超时时间,单位毫秒
    
        public void doBusinessLogic(String resourceId) {
            Jedis jedis = new Jedis("localhost", 6379); // 连接Redis
            RedisLock redisLock = new RedisLock(jedis, LOCK_TIMEOUT); // 创建Redis锁
            try {
                // 尝试获取锁
                boolean locked = redisLock.tryLock("resource_" + resourceId, UUID.randomUUID().toString());
                if (locked) {
                    // 进行业务处理
                    // ...
                } else {
                    // 加锁失败,重试或者抛出异常
                    throw new RuntimeException("Failed to acquire lock.");
                }
            } finally {
                // 释放锁
                redisLock.releaseLock("resource_" + resourceId, requestId);
                jedis.close();
            }
        }
    }
    

    在上述代码中,我们通过自定义RedisLock类来封装了加锁和解锁的操作,尝试加锁时会向Redis服务器发送操作请求并等待响应,在响应结果为OK时,表示加锁成功;释放锁则是通过Lua脚本实现的原子性操作,保证只有在当前请求持有锁时才能成功释放。

    需要注意的是,在使用Redis实现分布式锁时,需要考虑到对锁的持有时间以及死锁等问题,尽可能地保证加锁和释放锁的操作是原子性的,以防止数据的不一致性问题。另外,为了提高锁的性能,可以使用连接池等技术优化Redis的使用效率。

有点问题 锁过期时间不好控制,你并不知道你的业务具体处理时间多久,而且这个处理时间会随着当前的环境有所差异,锁过期时间过长,长时间无法释放锁,过短导致逻辑没有处理完就把锁释放。

同一个业务场景下,不是就应该保证只有一把锁嘛

这个实现有问题。Redisson实现的分布式锁是没有问题的,Redisson采用hash结构用来存储锁,其中大key表示表示这把锁是否存在,用小key表示当前这把锁被哪个线程持有。