REDIS實踐之線上人數統計幾種方案分析
阿新 • • 發佈:2019-02-15
線上人數統計業務是我們開發web肯定要設計的業務邏輯,本文就會給出幾種設計方案,來分析下各個方案的優缺點:
- 使用有序集合
- 使用集合
- 使用hyperloglog
- 使用bitmap
方案一:使用有序集合
$start_time = mktime(0, 0, 0, 9, 5); //monday for ($i=0; $i < 6; $i++) { $day_start_time = $start_time + 86400 * $i; //every day begin time $day_end_time = $day_start_time + 86400; //every day end time $online_user_num = mt_rand(100000, 300000); //online user between 100000 and 300000 for ($j=1; $j < $online_user_num; $j++) { $user_id = mt_rand(1, 1000000); $redis->zadd('000|online_users_day_'.$i, mt_rand($day_start_time, $day_end_time), $user_id); } }
好了記下來我們就來看看都能統計出哪些資訊來吧
//note: 統計每天的線上總人數 for ($i=0; $i < 6; $i++) { print_r($redis->zsize('000|online_users_day_'.$i). "\n"); } //note: 統計最近6天都線上的人數 var_dump($redis->zInter('000|online_users_day_both_6', [ '000|online_users_day_0', '000|online_users_day_1', '000|online_users_day_2', '000|online_users_day_3', '000|online_users_day_4', '000|online_users_day_5' ] )); //note: 統計出近6天中共有多少上線 $redis->zunion('000|online_users_day_total_6', ['000|online_users_day_0', '000|online_users_day_1', '000|online_users_day_2', '000|online_users_day_3', '000|online_users_day_4', '000|online_users_day_5']); //note: 統計某個時間段總共線上使用者 print_r($redis->zcount('000|online_users_day_5', mktime(13, 0, 0, 9, 10), mktime(14, 0, 0, 9, 10))); //note: 統計某個時間段線上使用者名稱單 print_r($redis->zrangebyscore('000|online_users_day_5', mktime(13, 0, 0, 9, 10), mktime(14, 0, 0, 9, 10), array('withscores' => TRUE)));
不單單隻有這些, 我們還能統計出早, 中, 午, 晚 等等時間段的使用者線上情況,還有很多其他的,這就讓我們發揮想象吧,是不是挺多的? 只是確實也相當耗費記憶體空間
方案二:使用集合
還是先來成使用者線上記錄資料://note set 一般聚合
for ($i=0; $i < 6; $i++) {
$online_user_num = mt_rand(100000, 300000); //online user between 100000 and 300000
for ($j=1; $j < $online_user_num; $j++) {
$user_id = mt_rand(1, 1000000);
$redis->sadd('001|online_users_day_'.$i, $user_id);
}
}
好了記下來我們就來看看都能統計出哪些資訊來吧
//note 判斷某個使用者是否線上
var_dump($redis->sIsMember('001|online_users_day_5', 100030));
//note 每天線上使用者總量的統計
for ($i=0; $i < 6; $i++) {
print_r($redis->ssize('001|online_users_day_'.$i). "\n");
}
//note 對不同時間段的線上使用者名稱單進行聚合
print_r($redis->sInterStore('001|online_users_day_both_4and5', '001|online_users_day_4', '001|online_users_day_5'). "\n");
//note 對指定的時間段的線上使用者名稱單進行統計
print_r($redis->sUnionStore('001|online_users_day_total_4add5', '001|online_users_day_4', '001|online_users_day_5'). "\n");
//note 哪天上線哪天沒上線
print_r($redis->sDiffStore('001|online_users_day_diff_4jian5', '001|online_users_day_4', '001|online_users_day_5'). "\n");
是不是也挺不錯的,先不要著急, 我們接著往下看
方案三:使用hyperloglgo
先來成使用者線上記錄資料:// note HyperLogLog 只需要知道線上總人數
for ($i=0; $i < 6; $i++) {
$online_user_num = mt_rand(100000, 300000); //online user between 100000 and 300000
var_dump($online_user_num);
for ($j=1; $j < $online_user_num; $j++) {
$user_id = mt_rand(1, 1000000);
$redis->pfadd('002|online_users_day_'.$i, [$user_id]);
}
}
這種方案,我們來看看都能實現哪些業務呢
$count = 0;
for ($i=0; $i < 3; $i++) {
$count += $redis->pfcount('002|online_users_day_'.$i);
print_r($redis->pfcount('002|online_users_day_'.$i). "\n");
}
var_dump($count);
//note 3 days total online num
var_dump($redis->pfmerge('002|online_users_day_both_3', ['002|online_users_day_0', '002|online_users_day_1', '002|online_users_day_2']));
var_dump($redis->pfcount('002|online_users_day_both_3'));
好少啊,是的, 這種方案僅僅只能統計出某個時間段線上人數的總量, 對線上使用者的名單卻無能為力,但是卻挺節省記憶體的,對統計資料要求不多情況下 ,我們便可以考慮這種方案。
方案四:使用bitmap
筆者對這種方案其實挺喜歡的,消耗的記憶體空間不多, 統計的資訊卻挺多的,還是老步驟,先來生成資料://note bitmap 綜合前面3個的優缺點
for ($i=0; $i < 6; $i++) {
$online_user_num = mt_rand(100000, 300000); //online user between 100000 and 300000
for ($j=1; $j < $online_user_num; $j++) {
$user_id = mt_rand(1, 1000000);
$redis->setbit('003|online_users_day_'.$i, $user_id, 1);
}
}
接下來我們看看能滿足的統計資訊吧
//note userid today whether online
var_dump($userid = mt_rand(1, 1000000));
var_dump($redis->getbit('003|online_users_day_5', $userid));
//note how many user is online
var_dump($redis->bitcount('003|online_users_day_5'));
//note 6 days both online
var_dump($redis->bitop('AND', '003|online_users_day_both_6', '003|online_users_day_0', '003|online_users_day_1', '003|online_users_day_2', '003|online_users_day_3', '003|online_users_day_4', '003|online_users_day_5'));
var_dump($redis->bitcount('003|online_users_day_both_6'));
//note 6 days total online
var_dump($redis->bitop('OR', '003|online_users_day_total_6', '003|online_users_day_0', '003|online_users_day_1', '003|online_users_day_2', '003|online_users_day_3', '003|online_users_day_4', '003|online_users_day_5'));
var_dump($redis->bitcount('003|online_users_day_total_6'));
//note 6 days only one online
var_dump($redis->bitop('XOR', '003|online_users_day_only_one_6', '003|online_users_day_0', '003|online_users_day_1', '003|online_users_day_2', '003|online_users_day_3', '003|online_users_day_4', '003|online_users_day_5'));
var_dump($redis->bitcount('003|online_users_day_only_one_6'));
怎麼樣? 是不是集合能統計的 這傢伙也能統計出來? 而且消耗的內容還少。 對於這幾種方案其實各有各的好處, 根據業務統計資訊 來取相應的方案來實施吧,這樣記憶體利用也就更合理了