本篇文章為大家帶來了關於PHP的相關知識,其中主要介紹了怎麼實現統計在線人數的問題,可以利用表統計方式、用redis有序集合統計、用hyperloglog做統計等等,下面一起來看一下,希望對大家有幫助。
推薦學習:《PHP影片教學》
一個業務系統網站每天人數的訪問量是多少,線上人數是多少?這種業務我們在開發中就要預留,也是在我們的設計範圍內的咯!因為一個正在經營的網站,每天都會用到統計。
那線上人數是如何統計的呢,這裡有幾種方案,程式碼用 laravel 框架。可以作為開發中參考。
用資料表統計線上人數,這種方式只能用在並發量不大的情況下。
首先我們先新建表:user_login
編輯
user_login 表
模擬使用者登錄,不存在用戶就存入表,存在的則更新登入資訊
// 客户端唯一的识别码 $client_id = session()->getId(); //用户是否已存在 $user = DB::table('user_login') ->where('token', $client_id) ->first(); //不存在则插入数据 if (empty($user)) { $data = [ 'token' => $client_id, 'username' => 'user_' . $client_id, // 模拟用户 'uid' => mt_rand(10000000, 99999999), //模拟用户id 'create_time' => date('Y-m-d H:i:s'), 'update_time' => date('Y-m-d H:i:s') ]; DB::table('user_login')->insert($data); } else { // 存在则更新用户登录信息 DB::table('user_login') ->where('token', $client_id) ->update([ 'update_time' => date('Y-m-d H:i:s') ]); }
這裡還需要定期清理無任何操作的用戶,假如用戶一個小時內無任何操作,我們可以記為無效用戶
程式碼如下:
// 客户端唯一的识别码 $client_id = session()->getId(); //用户是否已存在 $user = DB::table('user_login') ->where('token', $client_id) ->first(); //不存在则插入数据 if (empty($user)) { $data = [ 'token' => $client_id, 'username' => 'user_' . $client_id, // 模拟用户 'uid' => mt_rand(10000000, 99999999), //模拟用户id 'create_time' => date('Y-m-d H:i:s'), 'update_time' => date('Y-m-d H:i:s') ]; DB::table('user_login')->insert($data); } else { // 存在则更新用户登录信息 DB::table('user_login') ->where('token', $client_id) ->update([ 'update_time' => date('Y-m-d H:i:s') ]); }
我們可以實現的功能:
1)目前線上人數
2)某時段內在線上人數
3)最新上線的使用者
4)指定使用者是否線上
// 可实现功能一:当前总共在线人数 $c = DB::table('user_login')->count(); echo '当前在线人数:' . $c . '<br />'; // 可实现功能二:某时间段内在线人数 $begin_date = '2020-08-13 09:00:00'; $end_date = '2020-08-13 18:00:00'; $c = DB::table('user_login') ->where('create_time', '>=', $begin_date) ->where('create_time', '<=', $end_date) ->count(); echo $begin_date . '-' . $end_date . '在线人数:' . $c . '<br />'; // 可实现功能三:最新上线的用户 $newest = DB::table('user_login') ->orderBy('create_time', 'DESC') ->limit(10) ->get(); echo '最新上线的用户有:'; foreach ($newest as $value) { echo $value->username . ' '; } echo '<br />'; // 可实现功能四:指定用户是否在线 $username = 'user_1111'; $online = DB::table('user_login') ->where('username', $username) ->exists(); echo $username . ($online ? '在线' : '不在线');
因為是記憶體中,所以效率很高,可以統計某個時段的線上人數,可以做各種聚合操作。但是如果線上人數比較多的情況下,會比較佔用記憶體。還有一點:
無法透過用戶操作時間清除掉無效用戶,只有手動登出的用戶才會從集合中刪除。
程式碼如下:
// 客户端唯一的识别码 $client_id = session()->getId(); echo $client_id . '<br />'; // 按日期生成key $day = date('Ymd'); $key = 'online:' . $day; // 是否在线 $is_online = Redis::zScore($key, $client_id); if (empty($is_online)) { // 不在线,加入当前客户端 Redis::zAdd($key, time(), $client_id); } // 可实现功能一:当前总共在线人数 $c = Redis::zCard($key); echo '当前在线人数:' . $c . '<br />'; // 可实现功能二:某时间段内在线人数 $begin_date = '2020-08-13 09:00:00'; $end_date = '2020-08-13 18:00:00'; $c = Redis::zCount($key, strtotime($begin_date), strtotime($end_date)); echo $begin_date . '-' . $end_date . '在线人数:' . $c . '<br />'; // 可实现功能三:最新上线的用户,时间从小到大排序 $newest = Redis::zRangeByScore($key, '-inf', '+inf', ['limit' => [0, 50]]); echo '最新上线的用户有:'; foreach ($newest as $value) { echo $value . ' '; } echo '<br />'; // 可实现功能四:指定用户是否在线 $username = $client_id; $online = Redis::zScore($key, $client_id);; echo $username . ($online ? '在线' : '不在线') . '<br />'; // 可实现功能五:昨天和今天都上线的客户 $yestoday = Carbon::yesterday()->toDateString(); $yes_key = str_replace('-', '', $yestoday); $members = []; Redis::pipeline(function ($pipe) use ($key, $yes_key, &$members) { Redis::zinterstore('new_key', [$key, $yes_key], ['aggregate' => 'min']); $members = Redis::zRangeByScore('new_key', '-inf', '+inf', ['limit' => [0, 50]]); //dump($members); }); echo '昨天和今天都上线的用户有:'; foreach ($members as $value) { echo $value . ' '; }
#跟有序集合方式不同,hyperloglog 十分節約空間,但是實現的功能也非常單一,只能統計線上人數,不能實現其餘的任何功能。
Redis HyperLogLog 是用來做基數統計的演算法,HyperLogLog 的優點是,在輸入元素的數量或體積非常非常大時,計算基數所需的空間總是固定的、而且是很小的。
在 Redis 裡面,每個 HyperLogLog 鍵只需要花費 12 KB 內存,就可以計算接近 2^64 個不同元素的基 數。這和計算基數時,元素越多耗費記憶體就越多的集合形成鮮明對比。
但是,因為 HyperLogLog 只會根據輸入元素來計算基數,而不會儲存輸入元素本身,所以 HyperLogLog 不能像集合一樣,傳回輸入的各個元素。
// note HyperLogLog 只需要知道在线总人数 for ($i=0; $i < 6; $i++) { $online_user_num = mt_rand(10000000, 99999999); //模拟在线人数 var_dump($online_user_num); for ($j=1; $j < $online_user_num; $j++) { $user_id = mt_rand(1, 100000000); $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 就是透過一個 bit 位元來表示某個元素對應的值或狀態,其中的 key 就是對應元素本身。我們知道 8 個 bit 可以組成一個 Byte,所以 bitmap 本身會極大的節省儲存空間。
bitmap 常用來做例如用戶簽到、活躍用戶、線上用戶等功能。
程式碼如下
// 模拟当前用户 $uid = request('uid'); $key = 'online_bitmap_' . date('Ymd'); // 设置当前用户在线 Redis::setBit($key, $uid, 1); // 可实现功能1:在线人数 $c = Redis::bitCount($key); echo '在线人数:' . $c . '<br />'; // 可实现功能2:指定用户是否在线 $online = Redis::getBit($key, $uid); echo $uid . ($online ? '在线' : '不在线') . '<br />'; // 可实现功能3:昨天和今天均上线的用户总数 $yestoday = Carbon::yesterday()->toDateString(); $yes_key = str_replace('-', '', $yestoday); $c = 0; Redis::pipeline(function ($pipe) use ($key, $yes_key, &$c) { Redis::bitOp('AND', 'yest', $key, $yes_key); $c = Redis::bitCount('yest'); }); echo '昨天和今天都上线的用户数量有:' . $c . '<br />';
bitmap 消耗的記憶體空間不多, 統計的資訊卻蠻多的,這種方案是值得推薦的。
推薦學習:《PHP影片教學》
以上是以PHP來統計線上人數的四個方法詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!