From 8eaa672ed68ba413e329d64ecab5c2466efb05c8 Mon Sep 17 00:00:00 2001 From: augushong Date: Tue, 28 Jul 2026 06:00:15 +0800 Subject: [PATCH] =?UTF-8?q?feat(nginx-log):=20NginxLogClean=20=E5=AE=9A?= =?UTF-8?q?=E6=97=B6=E4=BB=BB=E5=8A=A1=20+=20timer=20config=20=E6=B3=A8?= =?UTF-8?q?=E5=86=8C=EF=BC=88=E5=AE=8C=E6=88=90=203=20=E4=B8=AA=20nginx=20?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/tools/controller/timer/NginxLogClean.php | 20 ++ extend/base/common/command/timer/config.php | 16 ++ .../controller/timer/NginxLogCleanBase.php | 172 ++++++++++++++++++ 3 files changed, 208 insertions(+) create mode 100644 app/tools/controller/timer/NginxLogClean.php create mode 100644 extend/base/tools/controller/timer/NginxLogCleanBase.php diff --git a/app/tools/controller/timer/NginxLogClean.php b/app/tools/controller/timer/NginxLogClean.php new file mode 100644 index 0000000..3430afe --- /dev/null +++ b/app/tools/controller/timer/NginxLogClean.php @@ -0,0 +1,20 @@ + 1, 'run_type' => 'auto', ], + [ + 'name' => 'nginx_log_aggregate', + 'type' => 'site', + 'target' => '/tools/timer.NginxLogStatAggregate/do', + 'frequency' => 3600, + 'concurrency' => 1, + 'run_type' => 'auto', + ], + [ + 'name' => 'nginx_log_clean', + 'type' => 'site', + 'target' => '/tools/timer.NginxLogClean/do', + 'frequency' => 86400, + 'concurrency' => 1, + 'run_type' => 'auto', + ], ]; diff --git a/extend/base/tools/controller/timer/NginxLogCleanBase.php b/extend/base/tools/controller/timer/NginxLogCleanBase.php new file mode 100644 index 0000000..244de19 --- /dev/null +++ b/extend/base/tools/controller/timer/NginxLogCleanBase.php @@ -0,0 +1,172 @@ + 0 才执行,0 = 永久跳过) + if ($rawDays > 0) { + $rawDeleted = $this->cleanRawTable($rawDays); + } + + // 4. stat 表清理(statDays > 0 才执行,0 = 永久跳过) + if ($statDays > 0) { + $statDeleted = $this->cleanStatTables($statDays); + } + + return json_encode([ + 'raw_deleted' => $rawDeleted, + 'stat_deleted' => $statDeleted, + ], JSON_UNESCAPED_UNICODE); + } + + /** + * 分批清理 raw 表 ul_nginx_access_log. + * + * 时间阈值基于 time_local(nginx 记录的请求时间戳),不是 create_time(入库时间)。 + * 每批 5000 行 + usleep 100ms,避免长事务锁表。 + * + * @param int $rawDays 保留天数(>0) + * @return int 累计删除行数 + */ + protected function cleanRawTable(int $rawDays): int + { + $threshold = time() - ($rawDays * 86400); + $total = 0; + + Log::info("nginx_log_clean: raw 阈值 time_local<" . date('Y-m-d H:i:s', $threshold) . " ({$rawDays} 天)"); + + // 循环分批 DELETE:每批 limit 5000,直到该阈值区间内无残留 + while (true) { + // Db::name 自动加表前缀;limit 保证单次 DELETE 行数可控 + $deleted = Db::name('nginx_access_log') + ->where('time_local', '<', $threshold) + ->limit(self::RAW_BATCH_SIZE) + ->delete(); + + if ($deleted <= 0) { + break; + } + + $total += $deleted; + + // 批次间休眠,降低 DB 压力(避免主从延迟) + if ($deleted === self::RAW_BATCH_SIZE) { + usleep(self::RAW_BATCH_USLEEP); + } else { + // 末批(< 5000)说明已清完该阈值区间 + break; + } + } + + Log::info("nginx_log_clean: raw 删除 {$total} 行"); + + return $total; + } + + /** + * 清理 4 张 stat 表(按 stat_date). + * + * stat_date 是 strtotime('today') 起算的当天 0 点时间戳,与 raw 表 time_local 不同: + * - stat 表一行 = 一天的聚合(stat_hour 按小时拆,但 stat_date 仍是当天 0 点) + * - 阈值用 strtotime('today') - statDays*86400(而非 time() - statDays*86400) + * 保证整天边界,不会误删"今天聚合了 N 小时但今天整体还没过期"的数据 + * + * 各表独立 DELETE,不做级联(如 stat_url 不依赖 stat_hour,可单独保留)。 + * + * @param int $statDays 保留天数(>0) + * @return int 累计删除行数(4 张表合计) + */ + protected function cleanStatTables(int $statDays): int + { + // strtotime('today') 返回当天 0 点时间戳(整数) + $threshold = strtotime('today') - ($statDays * 86400); + $total = 0; + + Log::info("nginx_log_clean: stat 阈值 stat_date<" . date('Y-m-d', $threshold) . " ({$statDays} 天)"); + + foreach (self::STAT_TABLES as $table) { + $deleted = Db::name($table) + ->where('stat_date', '<', $threshold) + ->delete(); + $total += $deleted; + + if ($deleted > 0) { + Log::info("nginx_log_clean: {$table} 删除 {$deleted} 行"); + } + } + + Log::info("nginx_log_clean: stat 合计删除 {$total} 行"); + + return $total; + } +}