redis高并发下的处理考勤打卡数据

背景
    最近公司用户量上来了,因此,对考勤打卡的瓶颈也就越发明显。每到打卡高峰期,公司APP就打开很慢,甚至服务开挂。针对这些问题,检查服务器发现,原来是考勤接口并发上来不停请求数据库导致的CPU剧增。因此,升级了服务器,提升了配置,但是还是不能抗住压力。因此,自己百度发现redis是个好东西,可以做缓存数据库,缓解mysql压力。因此,写下这篇文章。
主要运用
-   1.redis:hset,del,hKeys...
-   2.定时任务
-   3.注意:设计时,是以每天为单位,异步同步redis缓存数据到mysql。
代码模块
  • 1.redis基础封装
<?php
/**
 * Created by PhpStorm.
 * User: FengYun
 * Date: 2019/12/6
 * Time: 9:47
 */

namespace App\Services\Sign;


use App\Services\ComService;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Redis;

class RedisBaseService extends ComService
{

    private $config;

    private $redis;
    protected function __construct()
    {
        $this->redis = app('redis.connection');
    }


    private function _getEnable()
    {
        $conf = $this->config;

        return $conf['enable'];
    }




    /**
     * 查询key
     * @param $key
     * @return mixed
     */
    public function keys($key){
        return $this->redis->keys($key);
    }

    /**
     * 设置位图
     * @param $key
     * @param $offset
     * @param  bool $value
     * @param int $time
     */
    public function setBit($key, $offset,$value,$time=0)
    {

        $result = $this->redis->setbit($key,$offset,$value);
        if($time>0){
            $this->redis->expire($key,$time);
        }

        return $result;
    }

    /**
     * 获取位图
     * @param $key
     * @param $offset
     * @return mixed
     */
    public function getBit($key,$offset)
    {

        return $this->redis->getBit($key,$offset);
    }



    /**
     * 统计位图
     * @param $key
     * @return mixed
     */
    public function bitCount($key)
    {
        if(!$this->_getEnable()){
            return null;
        }
        return $this->redis->bitCount($key);
    }


    /**
     * 位图操作
     * @param $operation
     * @param $reKey
     * @param array ...$key
     * @return mixed
     */
    public function bitOp($operation,$reKey,...$key)
    {
        if(!$this->_getEnable()){
            return null;
        }
        return $this->redis->bitOp($operation,$reKey,$key);
    }

    /**
     * 计算在某段位图中 1或0第一次出现的位置
     * @param $key
     * @param $bit 1/0
     * @param $start
     * @param null $end
     * @return mixed
     */
    public function bitPos($key,$bit,$start,$end=null)
    {
        if(!$this->_getEnable()){
            return null;
        }
        return $this->redis->bitPos($key,$bit,$start,$end);
    }

    /**
     * 删除位图
     * @param $key
     * @return mixed
     */
    public function del($key)
    {
        return $this->redis->del($key);
    }

    /**
     * 写入
     * @param $key
     * @param $hashKey
     * @param $value
     * @return bool
     */
    public function hSet($key, $hashKey, $value )
    {
       return $this->redis->hset($key,$hashKey,$value);
    }

    /**
     * 批量获取
     * @param $key
     * @return bool
     */
    public function hGetAll($key)
    {
        return $this->redis->hGetAll($key);
    }

    /**
     * @param $key
     * @param $hashKey
     * @return bool
     */
    public function hExists($key,$hashKey)
    {
        return $this->redis->hExists($key,$hashKey);
    }


    /**
     * @param $key
     * @return bool
     */
    public function hKeys($key)
    {
        return $this->redis->hKeys($key);
    }
    /**
     * @param $key
     * @return bool
     */
    public function hLen($key)
    {
        return $this->redis->hLen($key);
    }


    /**
     * 管道技术批量入库
     * @param $key
     * @param $value
     */
    public function setPipe($key,$value)
    {

        $this->redis->multi();

        $this->redis->set('sign_'.$key,$value);
        $this->redis->get('sign_'.$key);

        $replies=$this->redis->exec();
        print_r($replies);
        
    }
}
  • 2.打卡封装(根据需要而定,可省略)
<?php
/**
 * Created by PhpStorm.
 * User: FengYun
 * Date: 2019/12/6
 * Time: 10:13
 */

namespace App\Services\Sign;


class RedisSignService extends RedisBaseService
{

    private $keySign= 'sign_';//签到记录key
    private $key = 0;
    public function __construct($keySign)
    {
        parent::__construct();
        //格式:前缀_2019_10_11:265|E234FD|20191206101112|1 =>1/0
    }



}
  • 3.打卡数据组装封装
<?php
/**
 * Created by PhpStorm.
 * User: Moer
 * Date: 2019/12/6
 * Time: 9:46
 */

namespace App\Services\Sign;




class SignService
{

    public static function userSignIn($params)
    {

        $sign_date = $params['sign_date'];
        $sign_time = $params['sign_time'];
        $company_id = $params['company_id'];
        $uuid = $params['uuid'];
        $sign_type = $params['sign_type'];
        $sign_date = date('Ymd',strtotime($sign_date));
        $keySign = 'sign_' . $sign_date .':'. $company_id .'|'. $uuid .'|'. date('YmhHis') .'|'. $sign_type;
        $signLogModel = new RedisSignService($keySign);

        try{
            /*格式:前缀_打卡日期:公司id|员工id|打卡时间|打卡方式
              sign_20191011:265|125043|20191206101112|1
            */
            $key = 'sign_' . $sign_date ; //sign_20191011
            $hashKey = $uuid .'|'. date('His',strtotime($sign_time)); //125043|143705
            $value = $company_id . '|' . $uuid .'|'. date('H:i:s',strtotime($sign_time)) .'|' .$sign_type;
            //公司|用户uuid|打卡时间|打卡类型 265|125043|20191206101112|1

            //写入redis数据
           $res = $signLogModel->hSet($key,$hashKey,$value);
           if(!$res){
               return false;
           }

        }catch (\Exception $e){
            return ['msg'=>$e->getMessage(),'code'=> $e->getCode()];
        }
    }
}
  • 4.模拟并发情况
<?php

namespace App\Http\Controllers\Order;

use App\Model\OrderSpikeModel;
use App\Services\Order\OrderService;
use App\Services\Sign\CronSignLogService;
use App\Services\Sign\SignService;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Redis;

class OrderController extends Controller
{
    public function signIn(Request $request)
    {
        $params['sign_date'] = date('Y-m-d',time());
        $params['sign_time'] = date('YmdHis',time());
        $params['company_id'] = rand(100,200);
        $params['uuid'] = '10000001';
        $params['sign_type'] = 1;
//        SignService::userSignIn($params);

        $start = microtime(true);
        for ($i=0;$i<10000;$i++){
//            sleep(1);
            $params['sign_date'] = date('Y-m-d',time());
            $params['sign_time'] = date('Y-m-d H:i:s',time());
            $params['company_id'] = rand(1,2000);
            $params['uuid'] = $i ;
            $params['sign_type'] = 1;
            try{
                SignService::userSignIn($params);

            }catch (\Exception $e){
                return ['msg'=>$e->getMessage(),'status'=>false];
            }


        }
        $end = microtime(true);
        echo '耗时'.round($end-$start,3)."秒<br/>";
        echo '内存(Now memory_get_usage): ' . memory_get_usage() . "<br />";
        return true;
    }
}
  • 5.定时同步到mysql
<?php
/**
 * 定时任务入库
 * Created by PhpStorm.
 * User: FengYun
 * Date: 2019/12/6
 * Time: 10:50
 */

namespace App\Services\Sign;


use App\Model\OrderSpikeModel;
use App\Services\ComService;

class CronSignLogService extends ComService
{
    public function __construct()
    {

    }
    public static function addUserSignLogToMysql()
    {

        $data = [];
        //获取需要同步的日期或者年月日
        $day = '20191210';
        $sign_type = self::SIGN_TYPE_APP;//APP打开
        $uuid = 0;
        $company_id = 0;
        $signLogModel = new RedisSignService($uuid,$company_id,$day,$sign_type);
        //格式:前缀_20191209:192|10000001|20191201014803|1

        //$keys = $signLogModel->keys('sign_'. $day .':*');

        $sign_key = 'sign_'.$day;
        $keys = $signLogModel->hGetAll($sign_key);
        $today = date('Y-m-d',time()) .' ';
        foreach ($keys as $key){

            //公司|用户uuid|打卡时间|打卡类型
            $userArr = explode('|',$key);
            //获取用户信息 公司id,用户id,签到时间,签到类型
            $data[] = [
                'company_id'=> $userArr[0],
                'uuid'=> $userArr[1],
                'sign_time'=> $today . $userArr[2],
                'sign_type'=> $userArr[3],
            ];

        }
//        var_dump($data);die;
        try{
            $is_success = false;
            if($data){
                //数据入库
                $insert_res = OrderSpikeModel::insert($data);
                //入库成功后,删除入库数据
                if($insert_res){
                    $isExist = $signLogModel->hLen($sign_key);
                    if($isExist > 0){
                        $is_success = $signLogModel->del($sign_key);
                    }
                }
            }
            return $is_success;
        }catch (\Exception $e){
            return ['msg'=> '操作入库失败,error:'. $e->getMessage(),'code'=> $e->getCode(),'status'=>false];
        }

    }
}

注:定时同步redis数据到mysql数据库。这里同步数据时,可以采用队列异步同步到msyql,根据自己喜欢而定。
总结
高并发下缓存数据库不为是一个很好的抗压工具,建议多使用,切勿滥用!
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值