本文目录导读:

在PHP项目中,面对海量日志的存储压力,可以采取以下几种有效的采样策略来降低存储成本,同时尽可能保留关键业务数据。
固定比例采样
- 实现方式:例如只记录 10% 的请求日志。
- 优点:简单、性能损耗低。
- 适用场景:访问量极大、每个请求日志结构相似、不需要追踪单次请求的全链路。
// 示例:根据请求ID或随机数决定是否记录
if (mt_rand(1, 10) === 1) {
// 记录日志(写入文件/数据库/发送到日志中心)
}
基于错误/异常的全量采样
- 核心逻辑:正常请求按一定比例采样,但所有的 ERROR、WARNING、CRITICAL 级别日志必须全量记录。
- 实现方式:在日志记录入口处判断日志等级。
// Monolog 或自定义日志类中的判断
function shouldLog($level) {
// 错误级别无条件记录
if ($level >= Logger::WARNING) {
return true;
}
// INFO/DEBUG 级别按采样率记录
return (mt_rand(1, 100) <= $sampleRate);
}
- 优点:既能控制正常日志体积,又能不遗漏故障信息。
时间窗口采样
- 原理:在单位时间(如每分钟)内,只保留前
N条或一定百分比。 - 适用场景:高峰期流量突增时,避免日志存储被瞬间冲垮。
- 实现:使用 Redis 或内存计数器(需注意并发安全)。
// 伪代码:使用 Redis 的 INCR(带过期时间)
$key = 'log:sampling:' . date('Y-m-d H:i'); // 每分钟一个 key
$count = $redis->incr($key);
$redis->expire($key, 70); // 设置略大于1分钟的过期时间
if ($count > $maxPerMinute) {
return; // 抛弃本条日志
}
// 记录日志
基于请求速率/频率的动态降级
- 逻辑:当系统检测到请求量超过某个阈值(如正常流量的2倍),自动触发更低的采样率(如从10%降到1%)。
- 适用场景:应对DDoS、爬虫攻击或突发峰值。
- 实现:结合监控系统(如 Prometheus)或阈值的本地状态。
// 根据当前请求数判断采样率
$currentRPS = getCurrentRequestsPerSecond(); // 从缓存或统计服务获取
if ($currentRPS > 5000) {
$sampleRate = 1; // 只记录1%
} elseif ($currentRPS > 2000) {
$sampleRate = 5;
} else {
$sampleRate = 50; // 正常情况记录50%
}
if (mt_rand(1, 100) <= $sampleRate) {
// 记录日志
}
基于请求特征的定向采样
- 按用户:对重要用户(VIP/付费用户)全量记录,普通用户采样。
- 按 API 接口:核心接口(如支付、下单)全量记录,普通列表查询接口采样。
- 按 HTTP 状态码:4xx、5xx全量,2xx、3xx采样。
- 按耗时:响应时间超过1秒(慢请求)全量记录。
// 判断是否需要记录
if ($response->getStatusCode() >= 400 || $requestTime > 1.0) {
return true; // 全量
}
// 按接口采样
if (in_array($route, ['/api/payment', '/api/order/create'])) {
return true;
}
// 普通请求降采样
return (mt_rand(1, $sampleRate) === 1);
日志采样 + 压缩与归档
- 近期日志:按采样策略存储(如保持7天)。
- 过期日志:全量或高比例数据压缩后归档到廉价存储(对象存储 S3、HDFS)。
- 热数据(7天内):使用上述采样策略,控制存储量。
- 冷数据(>30天):按天打包压缩(gzip),删除重复或非关键字段。
- 可检索性:对归档日志按时间、错误码建立索引(如通过 Elasticsearch 索引过期日志的摘要)。
实施建议与注意事项
- 优先级:首先实现 错误/异常全量 + 正常请求概率采样 组合,这是性价比最高的方案。
- 配置化:将采样率、时间窗口等参数放到配置中心(如 etcd、Nacos),无需重启即可动态调整。
- 写入异步化:使用消息队列(如 RabbitMQ、Kafka)或协程(Swoole/Fiber)异步写入日志,避免采样逻辑影响主请求性能。
- 监控采样效果:记录采样前后的日志量比率,确保采样策略未导致关键信息丢失。
- 与日志收集工具配合:如 Filebeat、Fluentd 等日志收集代理也可以在日志收集端做采样,减少传输带宽。
示例代码组合(简单实用)
class LoggerSampler {
private $errorSampling = true; // 错误全量
private $normalRate = 10; // 正常请求记录10%
private $slowThreshold = 2.0; // 超过2秒的请求全量
public function shouldLog($level, $requestTime, $statusCode) {
// 错误、慢请求、4xx/5xx 全量
if ($level >= Logger::ERROR || $requestTime > $this->slowThreshold || $statusCode >= 400) {
return true;
}
// 正常请求采样
return (mt_rand(1, 100) <= $this->normalRate);
}
}
总结推荐组合方案
错误/异常(全量) + 慢请求(全量) + 关键接口(全量) + 其他请求(10%随机采样)
日志文件:每天按比例保留,7天后删除;归档时全量保存到低成本存储。
这样既能保证故障排查能力,又能将存储成本降低80%-90%,如果日志量依然巨大,再结合时间窗口限流和异步处理。