基于Redis的分布式锁

class CDisLockObj : public CIMutex
{
public:
    CDisLockObj(CRedisDBInterface &cRedisDB, const std::string& strKey) 
        : m_cRedisDB(cRedisDB), m_strKey(strKey), m_strUUID(COperateUUID::GenerateUUID()) {};  //注意:此处必须生成一个UUID


    virtual ~CDisLockObj(){};


    virtual bool DoLock(int nTimeOut = nNO_END_LOCKER)
    {
        if (nNO_BLOCK_LOCKER == nTimeOut)
        {
            return TryLock();
        }
        else if (nNO_END_LOCKER >= nTimeOut)
        {
            return Lock(DEFAULT_MAX_OVERTIME);
        }
        else
        {
            return Lock(nTimeOut);
        }
    }


    virtual bool DoUnLock(void)
    {
        return ReleaseLock();
    }


private:
    bool Lock(uint64_t nExpire = DEFAULT_OVERTIME, int nSleepTime = DEFAULT_SLEEPTIME)
    {
        bool bIsLockAvailable = true;
        TC_TimeProvider::getInstance()->getNow(&m_tmTimerRecord);
        do
        {
            timeval tmCurrentTime;
            TC_TimeProvider::getInstance()->getNow(&tmCurrentTime);
            long tmSpacems = (tmCurrentTime.tv_sec - m_tmTimerRecord.tv_sec) * 1000 + (tmCurrentTime.tv_usec - m_tmTimerRecord.tv_usec) / 1000;
            if (long(nExpire * 1000) < tmSpacems)
            {
                bIsLockAvailable = false;
                LOG_ERROR("Lock key:" << m_strKey << " overtime, uuid:" << m_strUUID << ", nExpire:" << nExpire);
                break;
            }


            if (EMRStatus::EM_KVDB_SUCCESS == m_cRedisDB.SetEXNX(m_strKey, m_strUUID, nExpire))  //注意:这里的value里面放的是UUID
            {
                LOG_INFO("CDistributedLock lock key:" << m_strKey << ", uuid:" << m_strUUID << ", nExpire:" << nExpire << " success");
                break;
            }
            usleep(nSleepTime);
        } while (true);
        return bIsLockAvailable;
    }


    bool TryLock(uint64_t nExpire = DEFAULT_OVERTIME)
    {
        if (EMRStatus::EM_KVDB_SUCCESS == m_cRedisDB.SetEXNX(m_strKey, m_strUUID, nExpire))
        {
            LOG_INFO("CDistributedLock trylock key:" << m_strKey << ", uuid:" << m_strUUID << ", nExpire:" << nExpire << " success");
            return true;
        }
        else
        {
            LOG_WARN("CDistributedLock trylock key:" << m_strKey << ", uuid:" << m_strUUID << ", nExpire:" << nExpire << " failed");
            return false;
        }
    }


    bool ReleaseLock()
    {
        std::string strValue;
        if (EMRStatus::EM_KVDB_SUCCESS != m_cRedisDB.Get(m_strKey, strValue))   //每次解锁前需要检查锁中内容,必须是自己实际加的锁,别人加的锁是不能解的;
        {
            LOG_ERROR("CDistributedLock release key:" << m_strKey << " failed, can not get value");
            return false;
        }


        if (strValue != m_strUUID)
        {
            LOG_ERROR("CDistributedLock release key:" << m_strKey << " failed, uuid:" << m_strUUID << " != value:" << strValue);
            return false;
        }


        if (EMRStatus::EM_KVDB_SUCCESS != m_cRedisDB.DelKey(m_strKey))
        {
            LOG_ERROR("CDistributedLock release key:" << m_strKey << " failed. uuid:" << m_strUUID);
            return false;               
        }
        LOG_INFO("CDistributedLock release key:" << m_strKey << " success, uuid:" << m_strUUID);
        return true;
    }


private:
    CRedisDBInterface &m_cRedisDB;
    std::string m_strKey;            
    timeval m_tmTimerRecord;  //基点时间,以此为基础进行计时
    std::string m_strUUID;          //保证解锁、加锁 操作对应的同一把锁,不能是其他人的锁;
};



#endif


分布式锁设计原理:

加锁:

       必须加的是一把可识别出是自己的加的锁;

检查加锁是否成功:

       超时,则加锁失败;

解锁:

      可以解开自己加的锁;

      仅可以解开 自己等的那把锁;


分布式锁遇到的问题:

1、数据库中已存在锁的情况下,如何破锁?

redis:通过设定 key的过期时间,自动破锁;

mysql:通过加锁时做记录,解锁时做判定,解开的是自己等待的那把锁;


2、多线程释放锁的时候,不应释放别人加的锁?

redis:加锁成功后,记录value作为uuid;解锁时判断uuid是否合理,来进行解锁;

mysql:加锁成功后,记录uuid,删除时,where语句中带uuid。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值