如何用PHP与MySQL实现服务器在线率(Uptime)统计?
正确实现服务器在线率统计(PHP + MySQL)
我来帮你搞定这个在线率统计的问题!首先先纠正一下你提到的公式小偏差——在线率的正确计算应该是在线检测次数 ÷ 总检测次数 × 100%,如果用在线次数除以离线次数,遇到服务器全天在线的情况会直接触发除以0的错误。接下来我们一步步解决准确性和性能问题:
一、修正SQL语句(解决准确性与性能痛点)
你之前的SQL用了CROSS JOIN和DATE(created_at),前者会产生不必要的笛卡尔积拖慢查询,后者会让created_at字段的索引直接失效,这俩都是性能差的核心原因。下面是优化后的SQL:
1. 统计单台服务器当日在线率
SELECT server_id, ROUND( (SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) / COUNT(*)) * 100, 2 ) AS uptime_percentage FROM stats WHERE server_id = ? -- 替换为目标服务器ID AND created_at >= CURDATE() AND created_at < CURDATE() + INTERVAL 1 DAY GROUP BY server_id;
2. 统计所有服务器当日在线率
SELECT server_id, ROUND( (SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) / COUNT(*)) * 100, 2 ) AS uptime_percentage FROM stats WHERE created_at >= CURDATE() AND created_at < CURDATE() + INTERVAL 1 DAY GROUP BY server_id;
3. 扩展到7天/30天在线率
只需要修改WHERE条件的时间范围即可:
-- 7天在线率 WHERE created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY) AND created_at < NOW() -- 30天在线率 WHERE created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY) AND created_at < NOW()
二、性能优化关键:添加联合索引
你的Cron每10分钟写入一条数据,数据量会快速增长,必须加索引才能保证查询速度。推荐创建这个联合索引:
CREATE INDEX idx_server_created_status ON stats(server_id, created_at, status);
这个索引能让数据库快速定位到指定服务器、指定时间范围的记录,同时直接从索引里获取status值,不需要回表查询,性能提升非常明显。
三、PHP代码实现示例
下面是一个简单的PHP示例,展示如何从数据库获取并展示服务器在线率:
<?php // 数据库连接配置 $host = '你的数据库地址'; $dbname = '你的数据库名'; $username = '数据库用户名'; $password = '数据库密码'; try { $pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8mb4", $username, $password); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // 获取单台服务器当日在线率(替换410060为目标服务器ID) $serverId = 410060; $stmt = $pdo->prepare(" SELECT ROUND( IFNULL( (SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) / NULLIF(COUNT(*), 0)) * 100, 0 ), 2 ) AS uptime_percentage FROM stats WHERE server_id = ? AND created_at >= CURDATE() AND created_at < CURDATE() + INTERVAL 1 DAY "); $stmt->execute([$serverId]); $result = $stmt->fetch(PDO::FETCH_ASSOC); if ($result) { echo "服务器ID {$serverId} 当日在线率:{$result['uptime_percentage']}%"; } else { echo "该服务器今日暂无检测数据"; } } catch(PDOException $e) { echo "数据库错误: " . $e->getMessage(); } ?>
四、额外优化建议
- 避免全表扫描:永远不要用
DATE(created_at) = CURDATE()这种写法,改用范围查询created_at >= CURDATE() AND created_at < CURDATE() + INTERVAL 1 DAY,这样才能利用索引。 - 处理除数为0的情况:上面的SQL用了
NULLIF(COUNT(*), 0)和IFNULL,避免了服务器无检测记录时的除以0错误。 - 缓存统计结果:如果网站访问量较大,不要每次请求都实时计算,可以把在线率结果缓存到Redis或者本地文件中,比如每小时重新计算一次,进一步提升性能。
内容的提问来源于stack exchange,提问作者Dumitru
相关产品推荐
相关产品推荐

