redis實現使用者簽到,統計活躍使用者,使用者線上狀態

來源:互聯網
上載者:User

在開發的過程中,我們可能會遇到使用者簽到、統計當天的活躍使用者、以及每個使用者的線上狀態的開發需求,我們可能會用傳統的方法,根據相應的需求設計資料庫表等,這樣耗費的儲存空間大,以及效能方面也不會太好,下面會為大家介紹簡單,使用的方法。

在介紹實現方法前,會先給大家介紹Redis中的一個關鍵詞‘bitmap’

BitMap是什麼

就是通過一個bit位來表示某個元素對應的值或者狀態,其中的key就是對應元素本身。我們知道8個bit可以組成一個Byte,所以bitmap本身會極大的節省儲存空間。

Redis中的BitMap

Redis從2.2.0版本開始新增了setbit,getbit,bitcount等幾個bitmap相關命令。雖然是新命令,但是並沒有新增新的資料類型,因為setbit等命令只不過是在set上的擴充。

setbit命令介紹

指令 SETBIT key offset value
複雜度 O(1)
設定或者清空key的value(字串)在offset處的bit值(只能只0或者1)。

空間佔用、以及第一次分配空間需要的時間

在一台2010MacBook Pro上,offset為2^32-1(分配512MB)需要~300ms,offset為2^30-1(分配128MB)需要~80ms,offset為2^28-1(分配32MB)需要~30ms,offset為2^26-1(分配8MB)需要8ms。<來自官方文檔>
大概的空間佔用計算公式是:($offset/8/1024/1024)MB

使用情境一:使用者簽到

很多網站都提供了簽到功能(這裡不考慮資料落地事宜),並且需要展示最近一個月的簽到情況,如果使用bitmap我們怎麼做?一言不合亮代碼!

<?php$redis = new Redis();$redis->connect('127.0.0.1');//使用者uid$uid = 1;//記錄有uid的key$cacheKey = sprintf("sign_%d", $uid);//開始有簽到功能的日期$startDate = '2017-01-01';//今天的日期$todayDate = '2017-01-21';//計算offset$startTime = strtotime($startDate);$todayTime = strtotime($todayDate);$offset = floor(($todayTime - $startTime) / 86400);echo "今天是第{$offset}天" . PHP_EOL;//簽到//一年一個使用者會佔用多少空間呢?大約365/8=45.625個位元組,好小,有木有被驚呆?$redis->setBit($cacheKey, $offset, 1);//查詢簽到情況$bitStatus = $redis->getBit($cacheKey, $offset);echo 1 == $bitStatus ? '今天已經簽到啦' : '還沒有簽到呢';echo PHP_EOL;//計算總簽到次數echo $redis->bitCount($cacheKey) . PHP_EOL;/*** 計算某段時間內的簽到次數* 很不幸啊,bitCount雖然提供了start和end參數,但是這個說的是字串的位置,而不是對應"位"的位置* 幸運的是我們可以通過get命令將value取出來,自己解析。並且這個value不會太大,上面計算過一年一個使用者只需要45個位元組* 給我們的網站定一個小目標,運行30年,那麼一共需要1.31KB(就問你屌不屌?)*///這是個錯誤的計算方式echo $redis->bitCount($cacheKey, 0, 20) . PHP_EOL;

使用情境二:統計活躍使用者

使用時間作為cacheKey,然後使用者ID為offset,如果當日活躍過就設定為1
那麼我該如果計算某幾天/月/年的活躍使用者呢(暫且約定,統計時間內只有有一天線上就稱為活躍),有請下一個redis的命令
命令 BITOP operation destkey key [key ...]
說明:對一個或多個儲存二進位位的字串 key 進行位元操作,並將結果儲存到 destkey 上。
說明:BITOP 命令支援 AND 、 OR 、 NOT 、 XOR 這四種操作中的任意一種參數

//日期對應的活躍使用者
$data = array(
'2017-01-10' => array(1,2,3,4,5,6,7,8,9,10),
'2017-01-11' => array(1,2,3,4,5,6,7,8),
'2017-01-12' => array(1,2,3,4,5,6),
'2017-01-13' => array(1,2,3,4),
'2017-01-14' => array(1,2)
);

//大量設定活躍狀態
foreach($data as $date=>$uids) {
$cacheKey = sprintf("stat_%s", $date);
foreach($uids as $uid) {
$redis->setBit($cacheKey, $uid, 1);
}
}

$redis->bitOp('AND', 'stat', 'stat_2017-01-10', 'stat_2017-01-11', 'stat_2017-01-12') . PHP_EOL;
//總活躍使用者:6
echo "總活躍使用者:" . $redis->bitCount('stat') . PHP_EOL;

$redis->bitOp('AND', 'stat1', 'stat_2017-01-10', 'stat_2017-01-11', 'stat_2017-01-14') . PHP_EOL;
//總活躍使用者:2
echo "總活躍使用者:" . $redis->bitCount('stat1') . PHP_EOL;

$redis->bitOp('AND', 'stat2', 'stat_2017-01-10', 'stat_2017-01-11') . PHP_EOL;
//總活躍使用者:8
echo "總活躍使用者:" . $redis->bitCount('stat2') . PHP_EOL;

使用情境三:使用者線上狀態

前段時間開發一個項目,對方給我提供了一個查詢目前使用者是否線上的介面。不瞭解對方是怎麼做的,自己考慮了一下,使用bitmap是一個節約空間效率又高的一種方法,只需要一個key,然後使用者ID為offset,如果線上就設定為1,不線上就設定為0,和上面的情境一樣,5000W使用者只需要6MB的空間。

//大量設定線上狀態$uids = range(1, 500000);
foreach($uids as $uid) {
$redis->setBit('online', $uid, $uid % 2);
}
//一個一個擷取狀態
$uids = range(1, 500000);
$startTime = microtime(true);
foreach($uids as $uid) {
echo $redis->getBit('online', $uid) . PHP_EOL;
}
$endTime = microtime(true);
//在我的電腦上,擷取50W個使用者的狀態需要25秒
echo "total:" . ($endTime - $startTime) . "s";


/**
* 對於批量的擷取,上面是一種效率低的辦法,實際可以通過get擷取到value,然後自己計算
* 具體計算方法改天再寫吧,之前寫的代碼找不見了。。。
*/

其實BitMap可以運用的情境很多很多(當然也會受到一些限制),思維可以繼續擴散~歡迎小夥伴給我留言探討~

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.