水军大提督 发表于 2023-2-28 00:23:28

redis分布式锁原子性设置超时问题

1.问题:

  最近客服有报无法上报运动记录,通过日志查看是分布式锁等待超时所致。
  redis出现一个分布式锁的TTL为-1,正常情况都会设置超时时间的。
  https://img2023.cnblogs.com/blog/841795/202302/841795-20230227152915314-543813772.png
 
2.分析:

通过k8s发现sport服务在50几天内重启了40几次,机器上内存比较紧缺,暂时只能重启,占用内存高的问题也先不解决。
看下之前加锁的代码:
acquire_lock def acquire_lock(redis_client, lock_key, lock_value, expire=10, timeout=5):
    """
    获取锁
    :param redis_client: redis;连接
    :param lock_key: lock key
    :param lock_value: lock value
    :param expire: lock key过期时间
    :param timeout: 等待超时
    :return:
    """
    assert isinstance(redis_client, RedisClient),
    wait_time = 0
    sleep_time = 0.05
    with redis_client.ctx_conn as conn:
      while True:
            stime = time.time()
            if conn.execute('SETNX', lock_key, lock_value):
                conn.execute('EXPIRE', lock_key, expire)
                return True
            elif not conn.execute('TTL', lock_key):
                conn.execute('EXPIRE', lock_key, expire)
            logger.info("acquire_lock :%s" % lock_key)
            gevent.sleep(sleep_time)
            etime = time.time()
            wait_time += etime - stime
            if wait_time >= timeout:
                break
    assert 0, 从现状来分析,应该是SETNX之后没有执行EXPIRE,正常执行肯定没有问题;
我估计是因为进程刚好执行到SETNX的时候被k8s杀了,导致EXPIRE没有去执行
 
3.解决方案:


[*]set需要同时兼容NX EX的原子功能
  研究了一下redis的set命令,果然有这个功能
  SET', lock_key, lock_value, NX, EX, expire

[*]兼容没有设置超时的时候设置一下超时
  判断TTL 是-1的时候:
  ttl当 key 不存在时,返回 -2 。 当 key 存在但没有设置剩余生存时间时,返回 -1 。 否则,以秒为单位,返回 key 的剩余生存时间
  
原子操作set def acquire_lock(redis_client, lock_key, lock_value, expire=10, timeout=5):
    """
    获取锁
    :param redis_client: redis;连接
    :param lock_key: lock key
    :param lock_value: lock value
    :param expire: lock key过期时间
    :param timeout: 等待超时
    :return:
    """
    assert isinstance(redis_client, RedisClient),
    wait_time = 0
    sleep_time = 0.05
    logger.info("acquire_lock lock_key:%s lock_value:%s" % (lock_key, lock_value))
    with redis_client.ctx_conn as conn:
      while True:
            stime = time.time()
            if conn.execute('SET', lock_key, lock_value, "NX", "EX", expire):
                return True
            elif conn.execute('TTL', lock_key) == -1:      
                conn.execute('EXPIRE', lock_key, expire)
            gevent.sleep(sleep_time)
            etime = time.time()
            wait_time += etime - stime
            if wait_time >= timeout:
                break
    assert 0,
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: redis分布式锁原子性设置超时问题