PHP项目统计慢跑恢复时间数据的方案
慢跑恢复时间数据统计",我理解可能有两种场景,先帮你区分一下,再给出对应的PHP实现方案。

先明确需求场景
场景A:运动App/健康类项目 — 统计用户跑步后的心率恢复时间(运动生理学概念:跑后心率回落到某个区间所需时间)。
场景B:系统性能监控 — 统计接口/任务"慢"的恢复时间(如慢查询恢复、服务从慢状态恢复正常的耗时)。
下面以场景A(运动数据)为主给方案,场景B在最后补充,如果你说的是另一个,告诉我再调整。
数据模型设计(运动恢复时间)
-- 跑步记录表
CREATE TABLE run_records (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
start_time DATETIME NOT NULL,
end_time DATETIME NOT NULL,
distance DECIMAL(8,2), -- 公里
avg_pace INT, -- 平均配速(秒/公里)
avg_hr INT, -- 平均心率
max_hr INT, -- 最大心率
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_user_time (user_id, start_time)
);
-- 心率采样表(核心:用于计算恢复时间)
CREATE TABLE hr_samples (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
run_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
sample_time DATETIME NOT NULL,
heart_rate INT NOT NULL,
phase TINYINT DEFAULT 0, -- 0=运动中 1=恢复期
INDEX idx_run (run_id, sample_time)
);
-- 恢复时间结果表(由脚本异步计算写入)
CREATE TABLE recovery_stats (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
run_id BIGINT NOT NULL UNIQUE,
user_id BIGINT NOT NULL,
hr_at_stop INT, -- 停止时心率
hr_1min INT, -- 1分钟心率
hr_2min INT,
hr_drop_1min INT, -- 1分钟心率下降值(HRR1)
recovery_time_sec INT, -- 恢复到静息心率区间耗时
computed_at DATETIME,
INDEX idx_user (user_id)
);
关键指标:HRR1(跑后1分钟心率下降值)是运动医学公认的恢复能力指标,通常下降 >12 bpm 为正常。
核心计算逻辑(PHP)
class RecoveryCalculator
{
private PDO $db;
// 静息心率阈值:恢复到 静息+10 视为"恢复"
private int $restingHr = 70;
public function __construct(PDO $db)
{
$this->db = $db;
}
/**
* 计算某次跑步的恢复数据
*/
public function calcForRun(int $runId): ?array
{
// 1. 取出该跑步的所有心率采样(按时间排序)
$stmt = $this->db->prepare(
"SELECT sample_time, heart_rate, phase
FROM hr_samples
WHERE run_id = :rid
ORDER BY sample_time ASC"
);
$stmt->execute([':rid' => $runId]);
$samples = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (count($samples) < 2) {
return null;
}
// 2. 找到"停止跑步"的临界点(最后一个 phase=0 的样本)
$stopIdx = null;
foreach ($samples as $i => $s) {
if ((int)$s['phase'] === 0) {
$stopIdx = $i;
}
}
if ($stopIdx === null || !isset($samples[$stopIdx + 1])) {
return null;
}
$stopTime = strtotime($samples[$stopIdx]['sample_time']);
$hrAtStop = (int)$samples[$stopIdx]['heart_rate'];
// 3. 找 1min / 2min 心率点
$hr1 = $this->findHrNear($samples, $stopTime + 60);
$hr2 = $this->findHrNear($samples, $stopTime + 120);
$hrr1 = $hr1 !== null ? $hrAtStop - $hr1 : null;
// 4. 计算恢复到静息区间所需时间
$targetHr = $this->restingHr + 10;
$recoveryTime = null;
foreach (array_slice($samples, $stopIdx + 1) as $s) {
if ((int)$s['heart_rate'] <= $targetHr) {
$recoveryTime = strtotime($s['sample_time']) - $stopTime;
break;
}
}
$result = [
'run_id' => $runId,
'hr_at_stop' => $hrAtStop,
'hr_1min' => $hr1,
'hr_2min' => $hr2,
'hr_drop_1min' => $hrr1,
'recovery_time_sec' => $recoveryTime,
];
$this->save($result);
return $result;
}
/**
* 找最接近目标时间点的心率样本(容差±15秒)
*/
private function findHrNear(array $samples, int $targetTs): ?int
{
$best = null;
$minDiff = PHP_INT_MAX;
foreach ($samples as $s) {
$diff = abs(strtotime($s['sample_time']) - $targetTs);
if ($diff < $minDiff) {
$minDiff = $diff;
$best = (int)$s['heart_rate'];
}
}
return $minDiff <= 15 ? $best : null;
}
private function save(array $r): void
{
$sql = "INSERT INTO recovery_stats
(run_id, user_id, hr_at_stop, hr_1min, hr_2min,
hr_drop_1min, recovery_time_sec, computed_at)
VALUES (:run_id, :user_id, :hr_stop, :hr1, :hr2,
:hrr1, :recovery, NOW())
ON DUPLICATE KEY UPDATE
hr_at_stop = VALUES(hr_at_stop),
hr_1min = VALUES(hr_1min),
hr_2min = VALUES(hr_2min),
hr_drop_1min = VALUES(hr_drop_1min),
recovery_time_sec = VALUES(recovery_time_sec),
computed_at = NOW()";
// ... bind & execute
}
}
批量统计与性能优化
关键原则:实时接口只查汇总表,重计算异步跑。
// 批量补算(建议放到定时任务/队列,分页处理避免内存爆)
$pageSize = 500;
$lastId = 0;
while (true) {
$runs = $db->query(
"SELECT id, user_id FROM run_records
WHERE id > $lastId
AND id NOT IN (SELECT run_id FROM recovery_stats)
ORDER BY id LIMIT $pageSize"
)->fetchAll();
if (!$runs) break;
foreach ($runs as $run) {
$calc->calcForRun((int)$run['id']);
$lastId = $run['id'];
}
}
性能优化要点:
hr_samples按(run_id, sample_time)建联合索引,避免全表扫。- 单次跑步采样数据先一次性拉全在内存算,不要在循环里逐条查库。
- 大量历史数据用分批 + 队列(Redis/RabbitMQ),别在 Web 请求里同步算。
- 汇总统计(如某用户平均恢复时间)在
recovery_stats上用 SQL 聚合:
SELECT user_id,
AVG(hr_drop_1min) AS avg_hrr1,
AVG(recovery_time_sec) AS avg_recovery,
COUNT(*) AS run_count
FROM recovery_stats
WHERE user_id = :uid
AND computed_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY user_id;
高频查询的用户统计结果可缓存到 Redis(如 TTL 1 小时),写时失效。
如果其实是场景B(系统慢恢复时间)
那"统计"通常指:记录服务从慢/故障状态恢复到正常的耗时,做法:
CREATE TABLE slow_recovery_log (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
service VARCHAR(64),
metric VARCHAR(64), -- 如 p99_latency
slow_start DATETIME, -- 进入慢阈值时间
normal_time DATETIME, -- 恢复正常时间
duration_ms INT, -- 恢复耗时
INDEX idx_service (service, slow_start)
);
用 PHP 定时采集指标,状态机检测 正常 → 慢 → 正常 的跳变并记录 duration,再做 P50/P95 聚合分析。
能否确认一下你指的是哪种场景? 如果是运动数据,还需要确认:心率采样是实时上传还是事后批量导入?是否已有静息心率数据?这样我可以把计算逻辑再校准。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播