首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 統計線上人數兩大經典方案:AltaVista 反向連結法與 MySQL Session 表法完整比較

     編輯:PHP 相關知識

方案一:搜尋引擎反向連結統計法 (已淘汰)

早期利用 AltaVista `link:` 語法查詢指向網站的連結數,作為「線上人數」估算。現已完全失效 (AltaVista 2013 關閉),僅供歷史參考。

<?php
// 歷史參考代碼 - 不再可用
$url = 'https://www.example.com';
$encoded = rawurlencode($url);
$apiUrl = "https://www.altavista.com/cgi-bin/query?kl=XX&pg=q&text=yes&q=link:$encoded&search=Search";

$lines = @file($apiUrl);
$totalLinks = 0;
foreach ($lines as $line) {
    if (preg_match('/About (.*) pages found/', $line, $m)) {
        $totalLinks = (int)$m[1];
        break;
    } elseif (strpos($line, 'no document matching') !== false) {
        $totalLinks = 0;
        break;
    }
}
echo "$totalLinks 人連結到 $url";
?>

方案二:MySQL 活躍 Session 表法 (經典實作)

建立資料表記錄訪客 IP、最後活動時間、請求 URI,定期清理過期記錄。

資料表結構

CREATE TABLE `online_users` (
    `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
    `ip` VARCHAR(45) NOT NULL,              -- 支援 IPv6
    `last_active` DATETIME NOT NULL,        -- 最後活動時間
    `uri` VARCHAR(255) DEFAULT NULL,        -- 當前頁面
    `user_id` INT UNSIGNED DEFAULT NULL,    -- 登入用戶 ID (可選)
    `session_id` VARCHAR(128) DEFAULT NULL, -- Session ID (可選)
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_ip` (`ip`),              -- 同 IP 視為同一訪客
    KEY `idx_last_active` (`last_active`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

核心邏輯 (online.php - 每頁引入)

<?php
// 設定
$duration = 180; // 線上判定秒數 (3 分鐘)
$pdo = new PDO('mysql:host=localhost;dbname=stats;charset=utf8mb4', 'user', 'pass', [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]);

$ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
$uri = $_SERVER['REQUEST_URI'] ?? '/';
$now = date('Y-m-d H:i:s');
$expireTime = date('Y-m-d H:i:s', time() - $duration);

// 1. 清理過期記錄 (機率性執行,減少寫入壓力)
if (mt_rand(1, 100) <= 10) { // 10% 機率
    $pdo->exec("DELETE FROM online_users WHERE last_active < '$expireTime'");
}

// 2. 更新或插入當前訪客
$stmt = $pdo->prepare("
    INSERT INTO online_users (ip, last_active, uri) 
    VALUES (?, ?, ?)
    ON DUPLICATE KEY UPDATE last_active = VALUES(last_active), uri = VALUES(uri)
");
$stmt->execute([$ip, $now, $uri]);

// 3. 統計線上人數
$stmt = $pdo->query("SELECT COUNT(*) FROM online_users WHERE last_active >= '$expireTime'");
$onlineCount = (int)$stmt->fetchColumn();

echo "當前線上人數: $onlineCount";
?>

進階版:支援登入用戶、Session 綁定、Redis 加速

<?php
class OnlineCounter {
    private $pdo;
    private $redis;
    private $duration;
    private $table = 'online_users';
    
    public function __construct(PDO $pdo, $duration = 180, Redis $redis = null) {
        $this->pdo = $pdo;
        $this->duration = $duration;
        $this->redis = $redis;
    }
    
    public function hit(array $info = []): int {
        $ip = $info['ip'] ?? ($_SERVER['REMOTE_ADDR'] ?? 'cli');
        $uri = $info['uri'] ?? ($_SERVER['REQUEST_URI'] ?? '/');
        $userId = $info['user_id'] ?? null;
        $sessionId = $info['session_id'] ?? (session_id() ?: null);
        $key = "online:$ip";
        
        // Redis 快速計數 (排行榜/即時顯示)
        if ($this->redis) {
            $this->redis->hSet($key, [
                'ip' => $ip,
                'uri' => $uri,
                'user_id' => $userId ?? '',
                'session_id' => $sessionId ?? '',
                'updated' => time()
            ]);
            $this->redis->expire($key, $this->duration + 60);
            
            // 定期同步至 MySQL (排程器執行)
            return $this->getCountFromRedis();
        }
        
        // MySQL 直接模式
        $expire = date('Y-m-d H:i:s', time() - $this->duration);
        $now = date('Y-m-d H:i:s');
        
        $sql = "INSERT INTO {$this->table} (ip, last_active, uri, user_id, session_id) 
                VALUES (?, ?, ?, ?, ?)
                ON DUPLICATE KEY UPDATE last_active=?, uri=?, user_id=?, session_id=?";
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute([$ip, $now, $uri, $userId, $sessionId, $now, $uri, $userId, $sessionId]);
        
        // 機率性清理
        if (mt_rand(1, 100) <= 5) {
            $this->pdo->exec("DELETE FROM {$this->table} WHERE last_active < '$expire'");
        }
        
        return $this->getCount();
    }
    
    public function getCount(): int {
        $expire = date('Y-m-d H:i:s', time() - $this->duration);
        $stmt = $this->pdo->prepare("SELECT COUNT(*) FROM {$this->table} WHERE last_active >= ?");
        $stmt->execute([$expire]);
        return (int)$stmt->fetchColumn();
    }
    
    public function getCountFromRedis(): int {
        // 掃描所有 online:* keys (生產環境建議用 SCAN 避免阻塞)
        $keys = $this->redis->keys('online:*');
        $count = 0;
        $now = time();
        foreach ($keys as $key) {
            $data = $this->redis->hGetAll($key);
            if (isset($data['updated']) && ($now - (int)$data['updated']) < $this->duration) {
                $count++;
            }
        }
        return $count;
    }
    
    public function getList(int $limit = 50): array {
        // 取得線上用戶列表 (含頁面、用戶資訊)
        $expire = date('Y-m-d H:i:s', time() - $this->duration);
        $stmt = $this->pdo->prepare("
            SELECT ip, uri, user_id, last_active 
            FROM {$this->table} 
            WHERE last_active >= ? 
            ORDER BY last_active DESC 
            LIMIT ?
        ");
        $stmt->execute([$expire, $limit]);
        return $stmt->fetchAll();
    }
}
?>

方案比較總結

維度AltaVista 反向連結MySQL Session 表Redis + MySQL 混合
可用性❌ 已失效✅ 成熟穩定✅ 高效能
即時性小時級延遲秒級毫秒級
準確度極低 (連結≠人)高 (IP+時間)高
擴展性無中 (寫入瓶頸)極高
功能豐富度無基本統計列表、排行、用戶關聯

現代最佳實踐建議

  1. Redis Sorted Set:`ZADD online:{date} timestamp ip`,`ZCOUNT` 統計、`ZRANGE` 列表,極致效能
  2. Google Analytics / Matomo:專業分析工具,無需自建
  3. WebSocket / Server-Sent Events:真即時雙向通訊,精確線上狀態
  4. CDN 邊緣統計:Cloudflare Workers、Fastly Compute@Edge 邊緣計數
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved