一个redis-lock的疑问

        payed_key = VALENTINES_DAY_2019_IS_PAYED_DATE_FORMAT % (uid, t_uid, date_str,event_name)
    # 拿到所有的文件,进行扣钱,发送操作
        with events_cacher_v2.get_redis_lock(payed_key):
            if events_cacher_v2.redis_con.get(payed_key):
                raise xxxxxxx('xxxx')

            events_cacher_v2.redis_con.setex(payed_key, value='1', time=20)
            show_id = show_cacher.get_user_current_show_id(t_uid)
            credits_dict = money_cacher.get_user_credits_by_uid(uid)

            if credits_dict.get('credits', 0) < VALENTINES_DAY_CREDITS_2019:
                raise xxxxxxx('golds not enough')
            # 扣钱
            try:
                money_cacher.A2B_send_credits(uid, t_uid, VALENTINES_DAY_CREDITS_2019, VALENTINES_DAY_GIFT_ID_2019, 1, show_id, send_time=datetime.now())
                user_cacher.A2B_send_credits(uid, t_uid, VALENTINES_DAY_CREDITS_2019, VALENTINES_DAY_GIFT_ID_2019, 1, show_id)
            except Exception:
                raise xxxxxxx('golds not enough')

            # 发送语音+文字
            imgae_dict = {
                "width": 1242,
                "height": 932,
                "type": 0,
                "uri": image_url,
                "urls": [
                    result_image_url
                ]
            }
            audio_dict = {
                "audio_url": audio_url,
                "duration": audio_duration
            }
            send_money_voice(t_uid, uid, audio_dict, imgae_dict, text=msg)
               
            # 标记redis-key
            events_cacher_v2.redis_con.setex(payed_key, value='1', time=DEFAULT_15DAYS_EXPIRE_TIME)


其中:

def get_redis_lock(self, key):
    return acquire_redis_lock(
        django_settings.noflush_redis_con,
        '__%s:lock__' % key,
        expire=DEFAULT_REDIS_LOCK_EXPIRE_TIME,
        auto_renewal=True)


def acquire_redis_lock(redis_cache, name, expire=None, auto_renewal=False):
    from common.redis_cache import RedisCache
    if isinstance(redis_cache, RedisCache):
        real_redis_con = redis_cache.vip_redis_con
    else:
        assert isinstance(redis_cache, StrictRedis)
        real_redis_con = redis_cache

    return redis_lock.Lock(redis_client=real_redis_con, name=name, expire=expire, auto_renewal=auto_renewal)


代码如上,但是上线之后,一直有重复购买,重复扣钱的情况发生,理论上的一天只允许购买一次的效果达不到

 

 

想了一下是否有可能

  • (1)、客户端1获取锁成功
  • (2)、客户端1在某个操作上阻塞了很长时间
  • (3)、过期时间到,锁自动释放
  • (4)、客户端2获取到了对应同一个资源的锁
  • (5)、客户端1从阻塞中恢复过来,认为自己依旧持有锁,继续操作同一个资源,导致互斥性失效

 

或者(1)、客户端1获取锁成功。

  • (2)、客户端1访问共享资源。
  • (3)、客户端1为了释放锁,先执行'GET'操作获取随机字符串的值。
  • (4)、客户端1判断随机字符串的值,与预期的值相等。
  • (5)、客户端1由于某个原因阻塞住了很长时间。
  • (6)、过期时间到了,锁自动释放了。
  • (7)、客户端2获取到了对应同一个资源的锁。
  • (8)、客户端1从阻塞中恢复过来,执行DEL操纵,释放掉了客户端2持有的锁。

 

 

2020-3-24回顾

好稚嫩的写法啊,hhh

这地方完全没必要用锁的,利用redis的单进程性质做一个setnx就可以了,失败了就认为已经购买了。或者再加一层sql检查就好了。

你可能感兴趣的:(工作疑问)