首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 批次處理實作指南:cron、資料庫佇列、檔案佇列三種架構詳解

     編輯:PHP 綜合知識

為什麼需要批次處理?

Web 請求的容忍延遲通常 < 200ms。若任務耗時超過 1-2 秒(如發送萬封郵件、生成大型報表、資料同步),必須移至背景離線執行,避免阻塞用戶介面、觸發逾時或資源耗盡。

核心架構:cron + 輕量腳本

KISS 原則:無需常駐多進程/多線程守護進程,利用系統內建 cron 定時啟動 PHP CLI 腳本,處理完即退出,天然免疫記憶體洩漏、死鎖、無限迴圈風險。

cron 時間表達式速查

分 時 日 月 週  用戶  命令
0  23 * * *  www  /usr/bin/php /path/to/script.php   # 每天 23:00
15 * * * *  www  /usr/bin/php /path/to/script.php   # 每小時 15 分
*/5 9-18 * * 1-5 www  /usr/bin/php /path/to/script.php # 工作日 9-18 點每 5 分鐘
MAILTO=admin@example.com  # 錯誤郵件通知

Windows 使用「工作排程器」同樣可定時執行 php.exe script.php。

模式一:專用郵件佇列(資料庫表)

資料表結構

CREATE TABLE mailouts (
  id MEDIUMINT NOT NULL AUTO_INCREMENT,
  from_address TEXT NOT NULL,
  to_address TEXT NOT NULL,
  subject TEXT NOT NULL,
  content TEXT NOT NULL,
  PRIMARY KEY (id)
);

核心類 Mailouts

class Mailouts {
  static function get_db() { return DB::connect('mysql://user:pass@localhost/db'); }
  static function add($from, $to, $subject, $content) { /* INSERT */ }
  static function delete($id) { /* DELETE */ }
  static function get_all() { /* SELECT * */ }
}

前端入佇列

Mailouts::add('noreply@site.com', 'user@domain.com', '主題', '內容');

背景發送腳本 mailout_send.php

foreach (Mailouts::get_all() as $msg) {
  mail($msg[2], $msg[3], $msg[4], "From: $msg[1]");
  Mailouts::delete($msg[0]); // 逐封刪除,防重發
}

由 cron 定時執行 mailout_send.php。

模式二:通用任務佇列(資料庫)

支援任意 PHP 函式延遲執行,參數以鍵值對儲存。

資料表

CREATE TABLE processing_items (id MEDIUMINT AUTO_INCREMENT, function TEXT, PRIMARY KEY(id));
CREATE TABLE processing_args (id MEDIUMINT AUTO_INCREMENT, item_id MEDIUMINT, key_name TEXT, value TEXT, PRIMARY KEY(id));

核心類 ProcessingItems

class ProcessingItems {
  static function add($function, array $args) { /* 寫入 items + args */ }
  static function get_all() { /* JOIN 讀取 function + args 陣列 */ }
  static function delete($id) { /* 刪除 items + args */ }
}

使用範例

// 前端
ProcessingItems::add('sendReport', ['user_id' => 123, 'type' => 'monthly']);

// 背景處理器
foreach (ProcessingItems::get_all() as $item) {
  call_user_func_array($item['function'], [$item['args']]);
  ProcessingItems::delete($item['id']);
}

模式三:檔案系統佇列(無資料庫依賴)

將任務序列化為檔案存放於目錄(如 batch_items/),每個檔案一個任務。

BatchFiles 類關鍵方法


static function add($function, $args) {
  $path = BATCH_DIRECTORY . time() . '_' . uniqid();
  $fh = fopen($path, 'w');
  fwrite($fh, $function . "\n");
  foreach ($args as $k=>$v) fwrite($fh, "$k:$v\n");
  fclose($fh);
}
static function get_all() {
  $rows = [];
  if ($dh = opendir(BATCH_DIRECTORY)) {
    while (($file = readdir($dh)) !== false) {
      $path = BATCH_DIRECTORY . $file;
      if (is_file($path)) {
        $fh = fopen($path, 'r');
        $item['id'] = $path;
        $item['function'] = trim(fgets($fh));
        $item['args'] = [];
        while (($line = fgets($fh)) !== false) {
          list($k, $v) = explode(':', trim($line), 2);
          $item['args'][$k] = $v;
        }
        fclose($fh);
        $rows[] = $item;
      }
    }
    closedir($dh);
  }
  return $rows;
}
static function delete($id) { unlink($id); }

處理器邏輯與資料庫版本幾乎相同,僅替換類名。介面一致,便於未來遷移。

進階考量與最佳實踐

議題建議
任務冪等性設計任務可重複執行不產生副作用(如發送郵件前檢查狀態)。
失敗重試增加 attempts、last_error 欄位,失敗不刪除,cron 重跑時重試,超過閾值移入死信表/目錄並告警。
優先級資料庫表加 priority 欄位,ORDER BY priority DESC, id ASC。
併發控制單一 cron 執行;若需多進程,用 flock() 或資料庫 SELECT ... FOR UPDATE SKIP LOCKED 協調。
監控記錄處理耗時、成功/失敗計數,對接 Prometheus/Grafana 或簡單日誌告警。
現代替代方案對於高併發、複雜流程,考慮 Redis + PHP Worker (Swoole/RoadRunner)、Laravel Queue (Redis/Database/SQS)、RabbitMQ/Kafka 等成熟佇列中介軟體。

總結

從簡單的 cron + 檔案/資料庫佇列起步,隨業務增長平滑遷移至專業佇列系統。核心原則:**介面抽離、冪等設計、可觀測性、失敗自愈**。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved