当前位置:首页 > PHP教程 > php高级应用 > 列表

PHP使用redis位图bitMap 实现签到功能

发布:smiling 来源: PHP粉丝网  添加日期:2021-12-27 15:36:57 浏览: 评论:0 

这篇文章主要介绍了PHP使用redis位图bitMap 实现签到功能,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下。

一、需求

记录用户签到,查询用户签到

二、技术方案

1、使用mysql(max_time字段为连续签到天数)

思路:

(1)用户签到,插入一条记录,根据create_time查询昨日是否签到,有签到则max_time在原基础+1,否则,max_time=0

(2)检测签到,根据user_id、create_time查询记录是否存在,不存在则表示未签到

2、使用redis位图功能

思路:

(1)每个用户每个月单独一条redis记录,如00101010101010,从左往右代表01-31天(每月有几天,就到几天)

(2)每月8号凌晨,统一将redis的记录,搬至mysql,记录如图

(3)查询当月,从redis查,上月则从mysql获取

3、方案对比

举例:一万个用户签到365天

方案1、mysql 插入365万条记录

· 频繁请求数据库做一些日志记录浪费服务器开销。

·  随着时间推移数据急剧增大

· 海量数据检索效率也不高,同时只能用时间create_time作为区间查询条件,数据量大肯定慢

方案2、mysql 插入12w条记录

· 节省空间,每个用户每天只占用1bit空间 1w个用户每天产生10000bit=1050byte 大概为1kb的数据

· 内存操作存度快

3、实现(方案2)

(1)key结构

前缀_年份_月份:用户id -- sign_2019_10:01

查询:

单个:keys sign_2019_10_01

全部:keys sign_*

月份:keys sign_2019_10:*

(2)mysql表结构

(3)代码(列出1个调用方法,与三个类)

·签到方法

  1. public static function userSignIn($userId
  2.   { 
  3.     $time = Time(); 
  4.     $today = date('d'$time); 
  5.     $year = date('Y'$time); 
  6.     $month = date('m'$time); 
  7.     $signModel = new Sign($userId,$year,$month); 
  8.     //1、查询用户今日签到信息 
  9.     $todaySign = $signModel->getSignLog($today); 
  10.     if ($todaySign) { 
  11.       return self::jsonArr(-1, '您已经签到过了', []); 
  12.     } 
  13.     try { 
  14.       Db::startTrans(); 
  15.       $signModel->setSignLog($today); 
  16.       //4、赠送积分 
  17.       if (self::SING_IN_SCORE > 0) { 
  18.         $dataScore['order_id'] = $userId.'_'.$today
  19.         $dataScore['type'] = 2;//2、签到 
  20.         $dataScore['remark'] = '签到获得积分'
  21.         Finance::updateUserScore(Finance::OPT_ADD, $userId, self::SING_IN_SCORE, $dataScore); 
  22.       } 
  23.       $code = '0'
  24.       $msg = '签到成功'
  25.       $score = self::SING_IN_SCORE; 
  26.       Db::commit(); 
  27.     } catch (\Exception $e) { 
  28.       Db::rollback(); 
  29.       $code = '-2'
  30.       $msg = '签到失败'
  31.       $score = 0; 
  32.     } 
  33.     return self::jsonArr($code$msg, ['score' => $score]); 
  34.   } 

·redis基类

  1. <?php 
  2. namespace app\common\redis\db1; 
  3. /** 
  4.  * redis操作类 
  5.  */ 
  6. class RedisAbstract 
  7.   /** 
  8.    * 连接的库 
  9.    * @var int 
  10.    */ 
  11.   protected $_db = 1;//数据库名 
  12.   protected $_tableName = '';//表名 
  13.   static $redis = null; 
  14.   public function __construct() 
  15.   { 
  16.     return $this->getRedis(); 
  17.   } 
  18.   public function _calcKey($id
  19.   { 
  20.     return $this->_tableName . $id
  21.   } 
  22.   /** 
  23.    * 查找key 
  24.    * @param $key 
  25.    * @return array 
  26.    * @throws \Exception 
  27.    * @author wenzhen-chen 
  28.    */ 
  29.   public function keys($key
  30.   { 
  31.     return $this->getRedis()->keys($this->_calcKey($key)); 
  32.   } 
  33.   /** 
  34.    * 获取是否开启缓存的设置参数 
  35.    * 
  36.    * @return boolean 
  37.    */ 
  38.   public function _getEnable() 
  39.   { 
  40.     $conf = Config('redis'); 
  41.     return $conf['enable']; 
  42.   } 
  43.   /** 
  44.    * 获取redis连接 
  45.    * 
  46.    * @staticvar null $redis 
  47.    * @return \Redis 
  48.    * @throws \Exception 
  49.    */ 
  50.   public function getRedis() 
  51.   { 
  52.     if (!self::$redis) { 
  53.       $conf = Config('redis'); 
  54.       if (!$conf) { 
  55.         throw new \Exception('redis连接必须设置'); 
  56.       } 
  57.       self::$redis = new \Redis(); 
  58.       self::$redis->connect($conf['host'], $conf['port']); 
  59.       self::$redis->select($this->_db); 
  60.     } 
  61.     return self::$redis
  62.   } 
  63.   /** 
  64.    * 设置位图 
  65.    * @param $key 
  66.    * @param $offset 
  67.    * @param $value 
  68.    * @param int $time 
  69.    * @return int|null 
  70.    * @throws \Exception 
  71.    * @author wenzhen-chen 
  72.    */ 
  73.   public function setBit($key$offset$value$time = 0) 
  74.   { 
  75.     if (!$this->_getEnable()) { 
  76.       return null; 
  77.     } 
  78.     $result = $this->getRedis()->setBit($key$offset$value); 
  79.     if ($time) { 
  80.       $this->getRedis()->expire($key$time); 
  81.     } 
  82.     return $result
  83.   } 
  84.   /** 
  85.    * 获取位图 
  86.    * @param $key 
  87.    * @param $offset 
  88.    * @return int|null 
  89.    * @throws \Exception 
  90.    * @author wenzhen-chen 
  91.    */ 
  92.   public function getBit($key$offset
  93.   { 
  94.     if (!$this->_getEnable()) { 
  95.       return null; 
  96.     } 
  97.     return $this->getRedis()->getBit($key$offset); 
  98.   } 
  99.   /** 
  100.    * 统计位图 
  101.    * @param $key 
  102.    * @return int|null 
  103.    * @throws \Exception 
  104.    * @author wenzhen-chen 
  105.    */ 
  106.   public function bitCount($key
  107.   { 
  108.     if (!$this->_getEnable()) { 
  109.       return null; 
  110.     } 
  111.     return $this->getRedis()->bitCount($key); 
  112.   } 
  113.   /** 
  114.    * 位图操作 
  115.    * @param $operation 
  116.    * @param $retKey 
  117.    * @param mixed ...$key 
  118.    * @return int|null 
  119.    * @throws \Exception 
  120.    * @author wenzhen-chen 
  121.    */ 
  122.   public function bitOp($operation$retKey, ...$key
  123.   { 
  124.     if (!$this->_getEnable()) { 
  125.       return null; 
  126.     } 
  127.     return $this->getRedis()->bitOp($operation$retKey$key); 
  128.   } 
  129.   /** 
  130.    * 计算在某段位图中 1或0第一次出现的位置 
  131.    * @param $key 
  132.    * @param $bit 1/0 
  133.    * @param $start 
  134.    * @param null $end 
  135.    * @return int|null 
  136.    * @throws \Exception 
  137.    * @author wenzhen-chen 
  138.    */ 
  139.   public function bitPos($key$bit$start$end = null) 
  140.   { 
  141.     if (!$this->_getEnable()) { 
  142.       return null; 
  143.     } 
  144.     return $this->getRedis()->bitpos($key$bit$start$end); 
  145.   } 
  146.   /** 
  147.    * 删除数据 
  148.    * @param $key 
  149.    * @return int|null 
  150.    * @throws \Exception 
  151.    * @author wenzhen-chen 
  152.    */ 
  153.   public function del($key
  154.   { 
  155.     if (!$this->_getEnable()) { 
  156.       return null; 
  157.     } 
  158.     return $this->getRedis()->del($key); 
  159.   } 

·签到redis操作类

  1. <?php 
  2. /** 
  3.  * Created by PhpStorm. 
  4.  * User: Administrator 
  5.  * Date: 2019/9/30 
  6.  * Time: 14:42 
  7.  */ 
  8. namespace app\common\redis\db1; 
  9. class Sign extends RedisAbstract 
  10.   public $keySign = 'sign';//签到记录key 
  11.   public function __construct($userId,$year,$month
  12.   { 
  13.     parent::__construct(); 
  14.     //设置当前用户 签到记录的key 
  15.     $this->keySign = $this->keySign . '_' . $year . '_' . $month . ':' . $userId
  16.   } 
  17.   /** 
  18.    * 用户签到 
  19.    * @param $day 
  20.    * @return int|null 
  21.    * @throws \Exception 
  22.    * @author wenzhen-chen 
  23.    */ 
  24.   public function setSignLog($day
  25.   { 
  26.     return $this->setBit($this->keySign, $day, 1); 
  27.   } 
  28.   /** 
  29.    * 查询签到记录 
  30.    * @param $day 
  31.    * @return int|null 
  32.    * @throws \Exception 
  33.    * @author wenzhen-chen 
  34.    */ 
  35.   public function getSignLog($userId,$day
  36.   { 
  37.     return $this->getBit($this->keySign, $day); 
  38.   } 
  39.   /** 
  40.    * 删除签到记录 
  41.    * @return int|null 
  42.    * @throws \Exception 
  43.    * @author wenzhen-chen 
  44.    */ 
  45.   public function delSignLig() 
  46.   { 
  47.     return $this->del($this->keySign); 
  48.   } 

· 定时更新至mysql的类

  1. <?php 
  2. /** 
  3.  * Created by PhpStorm. 
  4.  * User: Administrator 
  5.  * Date: 2019/10/4 
  6.  * Time: 19:03 
  7.  */ 
  8. namespace app\common\business; 
  9. use app\common\mysql\SignLog; 
  10. use app\common\redis\db1\Sign; 
  11. class Cron 
  12.   /** 
  13.    * 同步用户签到记录 
  14.    * @throws \Exception 
  15.    */ 
  16.   public static function addUserSignLogToMysql() 
  17.   { 
  18.     $data = []; 
  19.     $time = Time(); 
  20.     //1、计算上月的年份、月份 
  21.     $dataTime = Common::getMonthTimeByKey(0); 
  22.     $year = date('Y'$dataTime['start_time']); 
  23.     $month = date('m'$dataTime['start_time']); 
  24.     //2、查询签到记录的key 
  25.     $signModel = new Sign(0, $year$month); 
  26.     $keys = $signModel->keys('sign_' . $year . '_' . $month . ':*'); 
  27.     foreach ($keys as $key) { 
  28.       $bitLog = '';//用户当月签到记录 
  29.       $userData = explode(':'$key); 
  30.       $userId = $userData[1]; 
  31.       //3、循环查询用户是否签到(这里没按每月天数存储,直接都存31天了) 
  32.       for ($i = 1; $i <= 31; $i++) { 
  33.         $isSign = $signModel->getBit($key$i); 
  34.         $bitLog .= $isSign
  35.       } 
  36.       $data[] = [ 
  37.         'user_id' => $userId
  38.         'year' => $year
  39.         'month' => $month
  40.         'bit_log' => $bitLog
  41.         'create_time' => $time
  42.         'update_time' => $time 
  43.       ]; 
  44.     } 
  45.     //4、插入日志 
  46.     if ($data) { 
  47.       $logModel = new SignLog(); 
  48.       $logModel->insertAll($data'', 100); 
  49.     } 
  50.   } 
  51. }

Tags: redis bitMap PHP签到功能

分享到: