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

打印 上一主题 下一主题

主题 595|帖子 595|积分 1785

1.问题:

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

通过k8s发现sport服务在50几天内重启了40几次,机器上内存比较紧缺,暂时只能重启,占用内存高的问题也先不解决。
看下之前加锁的代码:
acquire_lock
  1.  def acquire_lock(redis_client, lock_key, lock_value, expire=10, timeout=5):
  2.     """
  3.     获取锁
  4.     :param redis_client: redis;连接
  5.     :param lock_key: lock key
  6.     :param lock_value: lock value
  7.     :param expire: lock key过期时间
  8.     :param timeout: 等待超时
  9.     :return:
  10.     """
  11.     assert isinstance(redis_client, RedisClient), [type(RedisClient), RedisClient]
  12.     wait_time = 0
  13.     sleep_time = 0.05
  14.     with redis_client.ctx_conn as conn:
  15.         while True:
  16.             stime = time.time()
  17.             if conn.execute('SETNX', lock_key, lock_value):
  18.                 conn.execute('EXPIRE', lock_key, expire)
  19.                 return True
  20.             elif not conn.execute('TTL', lock_key):
  21.                 conn.execute('EXPIRE', lock_key, expire)
  22.             logger.info("acquire_lock :%s" % lock_key)
  23.             gevent.sleep(sleep_time)
  24.             etime = time.time()
  25.             wait_time += etime - stime
  26.             if wait_time >= timeout:
  27.                 break
  28.     assert 0, [conn, lock_key, lock_value, expire, timeout]
复制代码
从现状来分析,应该是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
  1.  def acquire_lock(redis_client, lock_key, lock_value, expire=10, timeout=5):
  2.     """
  3.     获取锁
  4.     :param redis_client: redis;连接
  5.     :param lock_key: lock key
  6.     :param lock_value: lock value
  7.     :param expire: lock key过期时间
  8.     :param timeout: 等待超时
  9.     :return:
  10.     """
  11.     assert isinstance(redis_client, RedisClient), [type(RedisClient), RedisClient]
  12.     wait_time = 0
  13.     sleep_time = 0.05
  14.     logger.info("acquire_lock lock_key:%s lock_value:%s" % (lock_key, lock_value))
  15.     with redis_client.ctx_conn as conn:
  16.         while True:
  17.             stime = time.time()
  18.             if conn.execute('SET', lock_key, lock_value, "NX", "EX", expire):
  19.                 return True
  20.             elif conn.execute('TTL', lock_key) == -1:      
  21.                 conn.execute('EXPIRE', lock_key, expire)
  22.             gevent.sleep(sleep_time)
  23.             etime = time.time()
  24.             wait_time += etime - stime
  25.             if wait_time >= timeout:
  26.                 break
  27.     assert 0, [conn, lock_key, lock_value, expire, timeout]
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

水军大提督

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表