zoukankan      html  css  js  c++  java
  • PHP缓存锁原理及利用

    原文链接
    :https://blog.csdn.net/tim_phper/article/details/54949404

    概述:

    项目当中经常要考虑数据高并发的情况,为了避免并发导致出现一些资源重复请求的问题,可以使用缓存加锁机制。 
    例如取微信access_token不加锁可能会导致非常严重的后果。

    准备:

    缓存锁,顾名思义,当然离不开缓存,这篇文章用到的redis缓存,可以根据自己的需要要选择合适缓存。 
    缓存锁的原理是在进行操作A之前,先在缓存中存放一个唯一的key,然后就进行对应操作A,而如果同时有其他一样的操作B并发时,因为操作B也需要存放key才能进行操作,而key是唯一的,所以操作B是无法进行存放一样key,也就是说操作B还没开始就被中断了。而当操作A完成之后,或者报错之后就进行释放锁,也就是从缓存当中删除对应的key。除此之外,当操作A操作时间过长时,我们也应该释放锁。具体流程如下图: 
    这里写图片描述

    分析:

    我这里用的是CI的框架,并不是原生的,但CI的框架也很容易看懂。

    //缓存锁class
    class Locker {
    
        private $_CI;
    
        public function __construct() {
            $this->_CI = & get_instance();
        }
    
        /**
         * 取锁
         * @param type $key
         * @param type $timeout 默认锁能锁10秒,10秒后锁自动解除
         * @return type
         */
        public function lock($key, $timeout = 10) {
            $now = time();
            $cache_key = $this->_get_key($key);
    
            // 如果redis中没有$cache_key,则加锁成功
            if ($this->_CI->cache->setnx($cache_key, $now) === true) {
                return true;
            }
    
            // 如果加锁时间超过最大锁时间,则自动解锁,并将锁赋予新的进程
            // 特别注意:并发情况下,使用getset方法可以保证只有一个进程获取到锁
            // 只有当$last_set_time == $_last_set_time时才是保证当前进程获取到锁
            $last_set_time = $this->_CI->cache->get($cache_key);
            if ($now - $last_set_time > $timeout) {
                $_last_set_time = $this->_CI->cache->getset($cache_key, $now);
                if ($last_set_time == $_last_set_time) {
                    return true;
                }
            }
    
            return false;
        }
    
        /**
         * 释放锁
         * @param string $key
         * @return  boolean
         */
        public function release($key) {
            $this->_CI->cache->delete($this->_get_key($key));
        }
    
        private $_lock_key_prefix = 'lock_';
        private function _get_key($key) {
            return $this->_lock_key_prefix . $key;
        }
    
    }
      //缓存锁利用过程
     ////////////////////////////// 加锁进行拿奖 开始 加锁 ////////////////////////
        $lock_key = 'get_prize_lock_' . $user->id;  //创建唯一key
        if ( ! $this->locker->lock($lock_key)) {  //判断缓存中是否已经存在唯一key
            return $this->send_json(false, '系统繁忙,请稍后重试...','',2);
        }
    
        // 检查是否已领取过
        $where = array(
            'act_uuid' => $act_uuid,
            'openid' => $user->wx_user_open_id,
            'share_from_result_id < ' => 1,
        );
        $count = $this->data_result_model->count_by($where);
        if ( $count > 0 ){
            $this->locker->release($lock_key);   //每次操作出错都释放锁
            return $this->send_json(true, '您已领取过卡券礼包了噢.');
        }
        //进行数据操作
           foreach ($coupon_pkg as $code => $coupon) {
            $dateTime = date('Y-m-d H:i:s');
           $record = array(
                'user_id' => $user->user_id,
                'act_uuid' => $act_uuid,
    
            );
    
            $this->eggs_data_result_model->add($record);
        }
        ////////////////////////////// 加锁进行拿奖 结束 释放锁 ////////////////////////
        $this->locker->release($lock_key);  //操作完成释放锁
    
        return $this->send_json(true, '卡券礼包已成功发放。',$act_uuid);  //操作完成

    总结:

    PHP处理数据时,并发情况经常出现,缓存锁机制真的是好处理方法,不过值得注意是经常检查缓存的运行状况,因为一旦缓存挂了,那么整个系统都会出错,无法正常运行的。

  • 相关阅读:
    Objective-C Runtime 运行时之四:Method Swizzling
    App启动加载广告页面思路
    关于CoreData和SQLite多线程访问时的线程安全问题
    HIVE学习(待更新)
    流处理环境搭建
    CAJ2PDF
    ArcMap加载在线地图
    学习opencv(持续更新)
    风险和策略(待更新)
    区块链入门教程(转)
  • 原文地址:https://www.cnblogs.com/yszr/p/9324043.html
Copyright © 2011-2022 走看看