本文目录导读:

我来为您提供一个完整的PHP指标异常检测方案,包含多种检测算法和实现示例。
基础异常检测类
<?php
class AnomalyDetector
{
private array $data;
private float $threshold;
public function __construct(array $data, float $threshold = 3.0)
{
$this->data = $data;
$this->threshold = $threshold;
}
/**
* 基于标准差的异常检测(Z-Score)
*/
public function zScoreDetection(): array
{
$mean = $this->calculateMean();
$stdDev = $this->calculateStdDev($mean);
if ($stdDev == 0) {
return [];
}
$anomalies = [];
foreach ($this->data as $index => $value) {
$zScore = abs(($value - $mean) / $stdDev);
if ($zScore > $this->threshold) {
$anomalies[$index] = [
'value' => $value,
'z_score' => $zScore,
'mean' => $mean,
'std_dev' => $stdDev
];
}
}
return $anomalies;
}
/**
* 基于IQR(四分位距)的异常检测
*/
public function iqrDetection(): array
{
$sortedData = $this->data;
sort($sortedData);
$q1 = $this->calculatePercentile($sortedData, 25);
$q3 = $this->calculatePercentile($sortedData, 75);
$iqr = $q3 - $q1;
$lowerBound = $q1 - 1.5 * $iqr;
$upperBound = $q3 + 1.5 * $iqr;
$anomalies = [];
foreach ($this->data as $index => $value) {
if ($value < $lowerBound || $value > $upperBound) {
$anomalies[$index] = [
'value' => $value,
'bounds' => [
'lower' => $lowerBound,
'upper' => $upperBound
]
];
}
}
return $anomalies;
}
/**
* 基于移动平均线的异常检测
*/
public function movingAverageDetection(int $windowSize = 5): array
{
$anomalies = [];
$count = count($this->data);
for ($i = $windowSize; $i < $count; $i++) {
$window = array_slice($this->data, $i - $windowSize, $windowSize);
$mean = array_sum($window) / $windowSize;
$stdDev = $this->calculateStdDev($mean, $window);
$currentValue = $this->data[$i];
$deviation = abs($currentValue - $mean);
if ($stdDev > 0 && ($deviation / $stdDev) > $this->threshold) {
$anomalies[$i] = [
'value' => $currentValue,
'mean' => $mean,
'std_dev' => $stdDev,
'deviation' => $deviation
];
}
}
return $anomalies;
}
/**
* 季节性和趋势检测(STL分解)
*/
public function seasonalDetection(int $seasonLength = 24): array
{
$seasonal = [];
$trend = [];
$residual = [];
$count = count($this->data);
// 计算季节性成分
for ($i = 0; $i < $seasonLength; $i++) {
$seasonValues = [];
for ($j = $i; $j < $count; $j += $seasonLength) {
$seasonValues[] = $this->data[$j];
}
$seasonal[$i] = array_sum($seasonValues) / count($seasonValues);
}
// 计算趋势和残差
for ($i = 0; $i < $count; $i++) {
$seasonalComponent = $seasonal[$i % $seasonLength];
// 简单趋势检测
if ($i > 0) {
$trend[$i] = $this->data[$i] - $this->data[$i-1];
} else {
$trend[$i] = 0;
}
$residual[$i] = $this->data[$i] - $seasonalComponent - $trend[$i];
}
// 检测残差的异常
$residualMean = array_sum($residual) / $count;
$residualStdDev = $this->calculateStdDev($residualMean, $residual);
$anomalies = [];
foreach ($residual as $index => $value) {
if ($residualStdDev > 0 && abs($value - $residualMean) > $this->threshold * $residualStdDev) {
$anomalies[$index] = [
'value' => $this->data[$index],
'residual' => $value,
'seasonal' => $seasonal[$index % $seasonLength],
'trend' => $trend[$index]
];
}
}
return $anomalies;
}
/**
* 基于机器学习的检测(简单阈值)
*/
public function mlBasedDetection(): array
{
// 使用K-means聚类简化版
$clusters = $this->simpleKMeans(2);
$anomalies = [];
foreach ($clusters as $clusterId => $clusterData) {
$clusterMean = array_sum($clusterData) / count($clusterData);
$clusterStdDev = $this->calculateStdDev($clusterMean, $clusterData);
foreach ($clusterData as $index => $value) {
$zScore = abs($value - $clusterMean) / ($clusterStdDev ?: 1);
if ($zScore > $this->threshold) {
$anomalies[$index] = [
'value' => $value,
'cluster' => $clusterId,
'z_score' => $zScore
];
}
}
}
return $anomalies;
}
/**
* 综合检测(多种算法结合)
*/
public function compositeDetection(): array
{
$zScoreAnomalies = $this->zScoreDetection();
$iqrAnomalies = $this->iqrDetection();
$movingAnomalies = $this->movingAverageDetection();
// 计算综合评分
$composite = [];
foreach ($this->data as $index => $value) {
$score = 0;
$methods = [];
if (isset($zScoreAnomalies[$index])) {
$score += 2;
$methods[] = 'Z-Score';
}
if (isset($iqrAnomalies[$index])) {
$score += 1;
$methods[] = 'IQR';
}
if (isset($movingAnomalies[$index])) {
$score += 2;
$methods[] = 'Moving Average';
}
if ($score >= 3) { // 至少需要两种方法确认
$composite[$index] = [
'value' => $value,
'score' => $score,
'methods' => $methods,
'timestamp' => time()
];
}
}
return $composite;
}
/**
* 实时监控类
*/
public function realTimeDetection($newValue, $windowSize = 100): bool
{
// 滑动窗口更新
array_push($this->data, $newValue);
if (count($this->data) > $windowSize) {
array_shift($this->data);
}
$zScoreAnomalies = $this->zScoreDetection();
$lastIndex = count($this->data) - 1;
return isset($zScoreAnomalies[$lastIndex]);
}
// 辅助方法
private function calculateMean(): float
{
return array_sum($this->data) / count($this->data);
}
private function calculateStdDev($mean, $data = null): float
{
$data = $data ?: $this->data;
$variance = 0;
$count = count($data);
foreach ($data as $value) {
$variance += pow($value - $mean, 2);
}
return sqrt($variance / ($count - 1));
}
private function calculatePercentile(array $sortedData, float $percentile): float
{
$index = ($percentile / 100) * (count($sortedData) - 1);
$floor = floor($index);
$ceil = ceil($index);
if ($floor == $ceil) {
return $sortedData[$index];
}
$valueAtFloor = $sortedData[$floor];
$valueAtCeil = $sortedData[$ceil];
return $valueAtFloor + ($index - $floor) * ($valueAtCeil - $valueAtFloor);
}
private function simpleKMeans(int $k): array
{
// 简化版K-means实现
$centroids = array_slice($this->data, 0, $k);
$clusters = [];
for ($iterations = 0; $iterations < 10; $iterations++) {
$clusters = array_fill(0, $k, []);
foreach ($this->data as $index => $value) {
$nearestCentroid = 0;
$minDistance = PHP_FLOAT_MAX;
foreach ($centroids as $clusterId => $centroid) {
$distance = abs($value - $centroid);
if ($distance < $minDistance) {
$minDistance = $distance;
$nearestCentroid = $clusterId;
}
}
$clusters[$nearestCentroid][$index] = $value;
}
// 更新质心
foreach ($clusters as $clusterId => $clusterData) {
if (!empty($clusterData)) {
$centroids[$clusterId] = array_sum($clusterData) / count($clusterData);
}
}
}
return $clusters;
}
}
实时监控系统实现
<?php
class MetricsMonitor
{
private PDO $db;
private Redis $redis;
private array $config;
public function __construct(PDO $db, Redis $redis, array $config = [])
{
$this->db = $db;
$this->redis = $redis;
$this->config = array_merge([
'retention_days' => 30,
'alert_threshold' => 3.0,
'check_interval' => 60,
'enabled_methods' => ['zscore', 'iqr', 'moving']
], $config);
}
/**
* 实时监控入口
*/
public function monitor(string $metricKey, $value): void
{
// 记录指标
$this->recordMetric($metricKey, $value);
// 获取历史数据
$historicalData = $this->getHistoricalData($metricKey, 1000);
if (count($historicalData) < 30) {
return; // 数据量不足
}
// 执行异常检测
$detector = new AnomalyDetector($historicalData, $this->config['alert_threshold']);
$anomalies = $detector->compositeDetection();
if (!empty($anomalies)) {
$this->triggerAlert($metricKey, $value, $anomalies);
}
// 缓存最新状态
$this->cacheStatus($metricKey, $value);
}
/**
* 记录指标到数据库
*/
private function recordMetric(string $metricKey, $value): void
{
$stmt = $this->db->prepare(
"INSERT INTO metrics (metric_key, value, created_at) VALUES (?, ?, NOW())"
);
$stmt->execute([$metricKey, $value]);
// 清理过期数据
$this->cleanupOldData();
}
/**
* 获取历史数据
*/
private function getHistoricalData(string $metricKey, int $limit = 1000): array
{
$cacheKey = "metrics:{$metricKey}";
$cached = $this->redis->get($cacheKey);
if ($cached) {
return json_decode($cached, true);
}
$stmt = $this->db->prepare(
"SELECT value FROM metrics WHERE metric_key = ? ORDER BY created_at DESC LIMIT ?"
);
$stmt->execute([$metricKey, $limit]);
$data = $stmt->fetchAll(PDO::FETCH_COLUMN);
$data = array_reverse($data);
// 缓存5分钟
$this->redis->setex($cacheKey, 300, json_encode($data));
return $data;
}
/**
* 触发告警
*/
private function triggerAlert(string $metricKey, $value, array $anomalies): void
{
$alertData = [
'metric' => $metricKey,
'value' => $value,
'anomalies' => $anomalies,
'timestamp' => time()
];
// 发送到告警队列
$this->redis->lpush('alerts:queue', json_encode($alertData));
// 记录日志
error_log("[METRIC ALERT] {$metricKey}: " . json_encode($anomalies));
}
/**
* 缓存指标状态
*/
private function cacheStatus(string $metricKey, $value): void
{
$statusKey = "status:{$metricKey}";
$status = [
'last_value' => $value,
'updated_at' => time(),
'count' => $this->redis->incr("count:{$metricKey}")
];
$this->redis->setex($statusKey, 300, json_encode($status));
}
/**
* 清理过期数据
*/
private function cleanupOldData(): void
{
$retentionDate = date('Y-m-d H:i:s', strtotime("-{$this->config['retention_days']} days"));
$stmt = $this->db->prepare("DELETE FROM metrics WHERE created_at < ?");
$stmt->execute([$retentionDate]);
}
/**
* 批量监控
*/
public function batchMonitor(array $metrics): array
{
$results = [];
foreach ($metrics as $key => $value) {
$this->monitor($key, $value);
$results[$key] = [
'detected' => $this->checkAnomaly($key, $value),
'timestamp' => time()
];
}
return $results;
}
/**
* 检查单个指标是否异常
*/
private function checkAnomaly(string $metricKey, $value): bool
{
$historicalData = $this->getHistoricalData($metricKey, 100);
$detector = new AnomalyDetector($historicalData);
return $detector->realTimeDetection($value);
}
}
自适应阈值检测
<?php
class AdaptiveAnomalyDetector extends AnomalyDetector
{
private array $weights;
private float $learningRate;
public function __construct(array $data, float $threshold = 3.0)
{
parent::__construct($data, $threshold);
$this->learningRate = 0.1;
$this->weights = array_fill(0, count($data), 1.0);
}
/**
* 自适应EWMA(指数加权移动平均)检测
*/
public function ewmaDetection(float $alpha = 0.3): array
{
$ewma = [];
$ewmaVariance = [];
$anomalies = [];
if (empty($this->data)) {
return [];
}
$ewma[0] = $this->data[0];
$ewmaVariance[0] = pow($this->data[0] - $ewma[0], 2);
for ($i = 1; $i < count($this->data); $i++) {
// 更新EWMA
$ewma[$i] = $alpha * $this->data[$i] + (1 - $alpha) * $ewma[$i-1];
// 更新方差
$ewmaVariance[$i] = $alpha * pow($this->data[$i] - $ewma[$i], 2) +
(1 - $alpha) * $ewmaVariance[$i-1];
$stdDev = sqrt($ewmaVariance[$i]);
// 自适应阈值
$adaptiveThreshold = $this->threshold;
if ($stdDev < 0.1 * ($ewma[$i] + 1)) {
$adaptiveThreshold += 0.5; // 波动小时提高阈值
} elseif ($stdDev > $ewma[$i] * 0.5) {
$adaptiveThreshold -= 0.5; // 波动大时降低阈值
}
if ($stdDev > 0 && abs($this->data[$i] - $ewma[$i]) > $adaptiveThreshold * $stdDev) {
$anomalies[$i] = [
'value' => $this->data[$i],
'ewma' => $ewma[$i],
'std_dev' => $stdDev,
'adaptive_threshold' => $adaptiveThreshold,
'anomaly_score' => abs($this->data[$i] - $ewma[$i]) / ($stdDev + 0.001)
];
}
}
return $anomalies;
}
/**
* 基于梯度提升的检测
*/
public function gradientDetection(): array
{
$gradients = [];
$anomalies = [];
// 计算梯度(变化率)
for ($i = 1; $i < count($this->data); $i++) {
$change = $this->data[$i] - $this->data[$i-1];
$percentageChange = ($this->data[$i-1] != 0) ?
($change / $this->data[$i-1]) : 0;
$gradients[$i] = $percentageChange;
}
// 检测梯度异常
$gradientMean = array_sum($gradients) / count($gradients);
$gradientStdDev = $this->calculateStdDev($gradientMean, $gradients);
foreach ($gradients as $index => $gradient) {
$zScore = abs($gradient - $gradientMean) / ($gradientStdDev + 1e-10);
if ($zScore > $this->threshold) {
$anomalies[$index] = [
'value' => $this->data[$index],
'gradient' => $gradient,
'z_score' => $zScore,
'type' => $gradient > 0 ? 'spike' : 'drop'
];
}
}
return $anomalies;
}
/**
* 学习率自适应调整
*/
public function adaptiveLearning(float $error): void
{
$this->learningRate *= (1 + $error);
$this->learningRate = min(0.5, max(0.01, $this->learningRate));
}
}
配置和管理
<?php
class AnomalyDetectionConfig
{
private array $config;
public function __construct(array $config = [])
{
$this->config = array_merge([
'default_threshold' => 3.0,
'detection_methods' => ['zscore', 'iqr', 'moving', 'ewma'],
'data_retention' => 90,
'alert_frequency' => 300,
'max_alerts_per_hour' => 10,
'min_data_points' => 30,
'rolling_window' => 1000,
'enable_ml' => true,
'ml_model_path' => '/models/anomaly_detection.pkl'
], $config);
}
public function get(string $key, $default = null)
{
return $this->config[$key] ?? $default;
}
public function set(string $key, $value): void
{
$this->config[$key] = $value;
}
public function validate(): array
{
$errors = [];
if ($this->config['default_threshold'] <= 0) {
$errors[] = "Threshold must be positive";
}
if ($this->config['data_retention'] < 30) {
$errors[] = "Data retention period too short";
}
if ($this->config['min_data_points'] < 10) {
$errors[] = "Minimum data points too low for reliable detection";
}
return $errors;
}
}
使用示例
<?php
// 基础使用
$data = [10, 12, 11, 13, 12, 11, 14, 15, 12, 100, 13, 12, 11, 14];
$detector = new AnomalyDetector($data, 2.5);
$anomalies = $detector->compositeDetection();
echo "基础异常检测结果:\n";
print_r($anomalies);
// 实时监控
$pdo = new PDO('mysql:host=localhost;dbname=monitoring', 'user', 'pass');
$redis = new Redis();
$redis->connect('localhost', 6379);
$monitor = new MetricsMonitor($pdo, $redis, [
'threshold' => 3.0,
'check_interval' => 60
]);
$monitor->monitor('cpu_usage', 95.5);
// 自适应检测
$adaptiveDetector = new AdaptiveAnomalyDetector($data, 3.0);
$ewmaAnomalies = $adaptiveDetector->ewmaDetection();
$gradientAnomalies = $adaptiveDetector->gradientDetection();
echo "EWMA检测结果:\n";
print_r($ewmaAnomalies);
echo "梯度检测结果:\n";
print_r($gradientAnomalies);
数据库表结构
-- 指标表
CREATE TABLE metrics (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
metric_key VARCHAR(255) NOT NULL,
value DECIMAL(12,4) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_metric_key (metric_key),
INDEX idx_created_at (created_at)
) ENGINE=InnoDB;
-- 告警表
CREATE TABLE alerts (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
metric_key VARCHAR(255) NOT NULL,
severity ENUM('info', 'warning', 'critical') DEFAULT 'warning',
description TEXT,
data JSON,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
status ENUM('open', 'acknowledged', 'resolved') DEFAULT 'open',
INDEX idx_metric_key (metric_key),
INDEX idx_status (status)
) ENGINE=InnoDB;
-- 检测配置表
CREATE TABLE detection_configs (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
metric_key VARCHAR(255) NOT NULL UNIQUE,
threshold DECIMAL(5,2) DEFAULT 3.00,
methods JSON,
window_size INT DEFAULT 1000,
update_frequency INT DEFAULT 300,
CONSTRAINT chk_threshold CHECK (threshold > 0)
) ENGINE=InnoDB;
这个方案提供了:
- 多种检测算法:Z-Score、IQR、移动平均、EWMA、梯度检测等
- 实时监控能力:实时检测和历史数据分析
- 自适应学习:根据数据特征动态调整阈值
- 完整的告警机制:告警记录、状态管理和通知
- 灵活配置:可按指标定制检测策略
您可以根据具体需求选择合适的检测方法或组合使用。