Use redis Bitmap bitmap to implement check-in function (PHP version)

Time:2020-3-31

What needs to be optimized, please point out in your comments

I. demand

Record user sign in, query user sign in

2、 Technical proposal

1. Use MySQL (max’u time field is consecutive check-in days)

Use redis Bitmap bitmap to implement check-in function (PHP version)
Train of thought:
(1) When the user checks in, insert a record, and query whether to check in yesterday according to create time. If there is a check in, max time is on the original basis + 1. Otherwise, max time = 0
(2) Check in is detected. Query whether the record exists according to user “ID and create” time. If it does not exist, it means it is not checked in

2. Use redis bitmap function

Train of thought:
(1) Each user has a separate redis record every month, such as 00101010101010. From left to right, it represents 01-31 days (there are several days in each month, just a few days)
(2) In the early morning of the 8th day of each month, the redis records are uniformly moved to MySQL, as shown in the figure below

Use redis Bitmap bitmap to implement check-in function (PHP version)

(3) Query the current month from redis, and get from MySQL last month

3. Scheme comparison

Example: 10000 users sign in 365 days
Scheme 1: insert 3.65 million records into MySQL
·Frequent requests for database logging waste server overhead.
·As time goes on, the data increases dramatically
·The efficiency of massive data retrieval is not high. At the same time, only create time can be used as the interval query condition, and the large amount of data must be slow

Scheme 2: insert 12W records into MySQL
·Save space. Each user only occupies 1 bit of space and 1 W users generate 10000 bit = 1050 bytes of data every day, which is about 1 KB
·Fast memory operation

3、 Implementation (scenario 2)

1. Key structure

Prefix: year, month: user ID -- sign: 2019: 10:01
Enquiries:
Single: keys sign? 2019? 10? 01
All: keys sign_*
Months: keys sign*

2. MySQL table structure

Use redis Bitmap bitmap to implement check-in function (PHP version)

3. Code (List 1 calling method and 3 classes)

(1) Check in method

public static function userSignIn($userId)
    {
        $time = Time();
        $today = date('d', $time);
        $year = date('Y', $time);
        $month = date('m', $time);
        $signModel = new Sign($userId,$year,$month);
        //1. Check in information of users today
        $todaySign = $signModel->getSignLog($today);
        if ($todaySign) {
            Return self:: jsonarr (- 1, 'you have checked in', []);
        }
        try {
            Db::startTrans();
            $signModel->setSignLog($today);
            //4. Bonus points
            if (self::SING_IN_SCORE > 0) {
                $dataScore['order_id'] = $userId.'_'.$today;
                $datascore ['type '] = 2; // 2. Check in
                $datascore ['remark '] =' check in to get points';
                Finance::updateUserScore(Finance::OPT_ADD, $userId, self::SING_IN_SCORE, $dataScore);
            }
            $code = '0';
            $MSG = 'sign in succeeded';
            $score = self::SING_IN_SCORE;
            Db::commit();
        } catch (\Exception $e) {
            Db::rollback();
            $code = '-2';
            $MSG = 'sign in failed';
            $score = 0;
        }
        return self::jsonArr($code, $msg, ['score' => $score]);
    }

(2) Redis base class

<?php

namespace app\common\redis\db1;

/**
 *Redis operation class
 */
class RedisAbstract
{

    /**
     *Connected Libraries
     * @var int
     */
    Protected $_db = 1; // database name
    Protected $_tablename = '; // table name

    static $redis = null;

    public function __construct()
    {
        return $this->getRedis();
    }

    public function _calcKey($id)
    {
        return $this->_tableName . $id;
    }

    /**
     * find key
     * @param $key
     * @return array
     * @throws \Exception
     * @author wenzhen-chen
     */
    public function keys($key)
    {
        return $this->getRedis()->keys($this->_calcKey($key));
    }

    /**
     *Get the setting parameters of whether to turn on caching
     *
     * @return boolean
     */
    public function _getEnable()
    {
        $conf = Config('redis');
        return $conf['enable'];
    }

    /**
     *Get redis connection
     *
     * @staticvar null $redis
     * @return \Redis
     * @throws \Exception
     */
    public function getRedis()
    {
        if (!self::$redis) {
            $conf = Config('redis');
            if (!$conf) {
                Throw new \ exception ('redis connection must be set ');
            }

            self::$redis = new \Redis();
            self::$redis->connect($conf['host'], $conf['port']);
            self::$redis->select($this->_db);
        }
        return self::$redis;
    }

    /**
     *Set bitmap
     * @param $key
     * @param $offset
     * @param $value
     * @param int $time
     * @return int|null
     * @throws \Exception
     * @author wenzhen-chen
     */
    public function setBit($key, $offset, $value, $time = 0)
    {
        if (!$this->_getEnable()) {
            return null;
        }
        $result = $this->getRedis()->setBit($key, $offset, $value);
        if ($time) {
            $this->getRedis()->expire($key, $time);
        }
        return $result;
    }

    /**
     *Get bitmap
     * @param $key
     * @param $offset
     * @return int|null
     * @throws \Exception
     * @author wenzhen-chen
     */
    public function getBit($key, $offset)
    {
        if (!$this->_getEnable()) {
            return null;
        }
        return $this->getRedis()->getBit($key, $offset);
    }

    /**
     *Statistical bitmap
     * @param $key
     * @return int|null
     * @throws \Exception
     * @author wenzhen-chen
     */
    public function bitCount($key)
    {
        if (!$this->_getEnable()) {
            return null;
        }
        return $this->getRedis()->bitCount($key);
    }

    /**
     *Bitmap operation
     * @param $operation
     * @param $retKey
     * @param mixed ...$key
     * @return int|null
     * @throws \Exception
     * @author wenzhen-chen
     */
    public function bitOp($operation, $retKey, ...$key)
    {
        if (!$this->_getEnable()) {
            return null;
        }
        return $this->getRedis()->bitOp($operation, $retKey, $key);
    }

    /**
     *Calculates the first occurrence of 1 or 0 in a bitmap segment
     * @param $key
     * @param $bit 1/0
     * @param $start
     * @param null $end
     * @return int|null
     * @throws \Exception
     * @author wenzhen-chen
     */
    public function bitPos($key, $bit, $start, $end = null)
    {
        if (!$this->_getEnable()) {
            return null;
        }
        return $this->getRedis()->bitpos($key, $bit, $start, $end);
    }

    /**
     *Delete data
     * @param $key
     * @return int|null
     * @throws \Exception
     * @author wenzhen-chen
     */
    public function del($key)
    {
        if (!$this->_getEnable()) {
            return null;
        }
        return $this->getRedis()->del($key);
    }

}

(3) Sign in redis operation class

<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2019/9/30
 * Time: 14:42
 */

namespace app\common\redis\db1;


class Sign extends RedisAbstract
{
    Public $key sign ='sign '; // sign in record key

    public function __construct($userId,$year,$month)
    {
        parent::__construct();
        //Set the key of the current user's check-in record
        $this->keySign = $this->keySign . '_' . $year . '_' . $month . ':' . $userId;
    }

    /**
     *User sign in
     * @param $day
     * @return int|null
     * @throws \Exception
     * @author wenzhen-chen
     */
    public function setSignLog($day)
    {
        return $this->setBit($this->keySign, $day, 1);
    }

    /**
     *Check in record
     * @param $day
     * @return int|null
     * @throws \Exception
     * @author wenzhen-chen
     */
    public function getSignLog($userId,$day)
    {
        return $this->getBit($this->keySign, $day);
    }

    /**
     *Delete sign in record
     * @return int|null
     * @throws \Exception
     * @author wenzhen-chen
     */
    public function delSignLig()
    {
        return $this->del($this->keySign);
    }
}

(4) Regularly update to MySQL class

<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2019/10/4
 * Time: 19:03
 */

namespace app\common\business;

use app\common\mysql\SignLog;
use app\common\redis\db1\Sign;

class Cron
{
    /**
     *Synchronize user sign in records
     * @throws \Exception
     */
    public static function addUserSignLogToMysql()
    {
        $data = [];
        $time = Time();
        //1. Calculate the year and month of last month
        $dataTime = Common::getMonthTimeByKey(0);
        $year = date('Y', $dataTime['start_time']);
        $month = date('m', $dataTime['start_time']);
        //2. Query the key of the check-in record
        $signModel = new Sign(0, $year, $month);
        $keys = $signModel->keys('sign_' . $year . '_' . $month . ':*');
        foreach ($keys as $key) {
            $bitlog = ''; // user's attendance record in the current month
            $userData = explode(':', $key);
            $userId = $userData[1];
            //3. Cycle to query whether the user has signed in (it is not stored by the number of days per month, but directly stored for 31 days)
            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' => $time
            ];
        }
        //4. Insert log
        if ($data) {
            $logModel = new SignLog();
            $logModel->insertAll($data, '', 100);
        }

    }
}

Recommended Today

Configure Apache to support PHP in the Apache main configuration file httpd.conf Include custom profile in

In Apache’s main configuration file / conf/ http.conf Add at the bottom Include “D:workspace_phpapache-php.conf” The file path can be any In D: workspace_ Create under PHP file apache- php.conf file Its specific content is [html] view plain copy PHP-Module setup LoadFile “D:/xampp/php/php5ts.dll” LoadModule php5_module “D:/xampp/php/php5apache2_2.dll” <FilesMatch “.php$”> SetHandler application/x-httpd-php </FilesMatch> <FilesMatch “.phps$”> SetHandler application/x-httpd-php-source </FilesMatch> […]