当前位置: 首页 > 后端技术 > PHP

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

时间:2023-03-30 00:36:04 PHP

有待优化。请在评论中指出。1、需要记录用户签到,查询用户签到二、技术方案1、使用mysql(max_time字段为连续签到天数)思路:(1)用户签到,插入一条记录,查询是否是昨天根据create_time签入,如果有签入则max_time在原base+1基础上加1,否则,max_time=0(2)检测签入,根据user_id和create_time检查记录是否存在,不存在即不登录2.redis位图功能使用思路:(1)每个用户每个月有一条redis记录,比如00101010101010,从左到右代表01-31天(有好几天每个月,就几天)(2)每个月8号凌晨,统一将redis记录迁移到mysql,如图(3)查询当前月份,从redis中查询,得到来自上个月的mysql3、方案对比示例:110000用户登录365天。解决方案1、mysql插入365万条记录。 频繁请求数据库做一些日志记录,浪费服务器开销。·数据随时间急剧增加· 海量数据的检索效率不高,同时只能使用时间create_time作为区间查询条件。如果数据量很大,肯定会很慢。方案二、mysql为了节省空间,插入12万条记录。每个用户每天只占用1位空间。10,000个用户每天生成10,000个比特。=1050byte大约是1kb的数据。内存操作存储速度3.实现(方案二)1.key结构prefix_year_month:userid--sign_2019_10:01Query:single:keyssign_2019_10_01all:keyssign_*month:keyssign_2019_10:*2,mysql表结构3,代码(列出1个调用方法和三个类)(1)登录方法publicstaticfunctionuserSignIn($userId){$time=Time();$today=date('d',$time);$year=date('Y',$time);$month=date('m',$time);$signModel=newSign($userId,$year,$month);//1。查询用户今天的签到信息$todaySign=$signModel->getSignLog($today);if($todaySign){returnself::jsonArr(-1,'您已经登录',[]);}尝试{Db::startTrans();$signModel->setSignLog($今天);//4。赠送积分if(self::SING_IN_SCORE>0){$dataScore['order_id']=$userId.'_'.$today;$dataScore['type']=2;//2、签到$dataScore['remark']='登录获取积分';财务::更新用户分数(财务::OPT_ADD,$userId,self::SING_IN_SCORE,$dataScore);}$代码='0';$msg='登录成功';$score=self::SING_IN_SCORE;数据库::提交();}catch(\Exception$e){Db::rollback();$代码='-2';$msg='登录失败';$分数=0;}返回自我::jsonArr($code,$msg,['score'=>$score]);}(2)redis基类getRedis();}公共函数_calcKey($id){返回$this->_tableName。$id;}/***查找key*@param$key*@returnarray*@throws\Exception*@authorwenzhen-chen*/public函数键($key){返回$this->getRedis()->keys($this->_calcKey($key));}/***获取是否开启缓存的设置参数**@returnboolean*/publicfunction_getEnable(){$conf=Config('redis');返回$conf['启用'];}/***获取redis连接**@staticvarnull$redis*@return\Redis*@throws\Exception*/publicfunctiongetRedis(){if(!self::$redis){$conf=Config('redis');if(!$conf){thrownew\Exception('必须设置redis连接');}自我::$redis=new\Redis();self::$redis->connect($conf['host'],$conf['port']);self::$redis->select($this->_db);}返回自我::$redis;}/***设置位图*@param$key*@param$offset*@param$value*@paramint$time*@returnint|null*@throws\Exception*@authorwenzhen-chen*/public函数setBit($key,$offset,$value,$time=0){if(!$this->_getEnable()){returnnull;$result=$this->getRedis()->setBit($key,$offset,$value);如果($time){$this->getRedis()->expire($key,$time);}返回$结果;}/***获取位图*@param$key*@param$offset*@returnint|null*@throws\Exception*@authorwenzhen-chen*/publicfunctiongetBit($key,$offset){if(!$this->_getEnable()){返回空值;}返回$this->getRedis()->getBit($key,$offset);}/***统计位图*@param$key*@returnint|null*@throws\Exception*@authorwenzhen-chen*/publicfunctionbitCount($key){if(!$this->_getEnable()){返回空值;}返回$this->getRedis()->bitCount($key);}/***位图操作*@param$operation*@param$retKey*@parammixed...$key*@returnint|null*@throws\Exception*@authorwenzhen-chen*/publicfunctionbitOp($operation,$retKey,...$key){如果(!$this->_getEnable()){返回空值;}返回$this->getRedis()->bitOp($operation,$retKey,$key);}/***计算位图中第一次出现1或0的位置*@param$key*@param$bit1/0*@param$start*@paramnull$end*@returnint|null*@throws\Exception*@authorwenzhen-chen*/publicfunctionbitPos($key,$bit,$start,$end=null){if(!$this->_getEnable()){返回空;}返回$this->getRedis()->bitpos($key,$bit,$start,$end);}/***删除数据*@param$key*@returnint|null*@throws\Exception*@authorwenzhen-chen*/publicfunctiondel($key){if(!$this->_getEnable()){关于转为空;}返回$this->getRedis()->del($key);}}(3)登录redis操作类keySign=$this->keySign.'_'。$年。'_'。$月。':'。$用户名;}/***用户登录*@param$day*@returnint|null*@throws\Exception*@authorwenzhen-chen*/publicfunctionsetSignLog($day){return$this->setBit($this->keySign,$day,1);}/***查询签到记录*@param$day*@returnint|null*@throws\Exception*@authorwenzhen-chen*/publicfunctiongetSignLog($userId,$day){return$this->getBit($this->keySign,$day);}/***删除登录记录除外*@returnint|null*@throws\Exception*@authorwenzhen-chen*/publicfunctiondelSignLig(){return$this->del($this->keySign);}}(4)定期更新mysql类keys('sign_'.$year.'_'.$month.':*');foreach($keysas$key){$bitLog='';//当月用户签到记录$userData=explode(':',$键);$userId=$userData[1];//3。循环查询用户是否登录(这里不是按照每个月的天数存储,直接存储31天)for($i=1;$i<=31;$i++){$isSign=$signModel->getBit($key,$i);$bitLog.=$isSign;}$data[]=['user_id'=>$userId,'year'=>$year,'month'=>$month,'bit_log'=>$bitLog,'create_time'=>$time,'update_time'=>$时间];}//4。插入日志if($data){$logModel=newSignLog();$logModel->insertAll($data,'',100);}}}