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

解析 MySQL Binlog 起止時間:PHP 實現直接讀取二進制日誌格式

     編輯:MySQL 綜合教學

背景

mysqlbinlog 工具需順序讀取全文件才能取得結束時間,大檔案耗時極長。MySQL Binlog 格式公開,可直接解析文件頭與尾部事件快速獲取起止時間。

Binlog 格式關鍵點

  • 文件頭:0xFE 0x62 0x69 0x6E(fe bin)。
  • 第一個事件:Format Description Event(類型 15),含建立時間戳。
  • 最後一個事件:Rotate Event(類型 4),含下一個 Binlog 檔名與位置,其事件頭記錄結束時間戳。
  • 事件頭固定 19 字節:timestamp(4) + type_code(1) + server_id(4) + event_length(4) + next_position(4) + flags(2)。

PHP 解析類別完整代碼

<?php
/**
 * 讀取 Binlog 起止時間、下一檔名、Server 版本
 */
class BinlogInfo {
    const EVENT_HEAD_SIZE = 19;
    const FORMAT_DESCRIPTION_EVENT_DATA_SIZE = 59;
    const BINLOG_HEAD = "\xfebin";
    const FORMAT_DESCRIPTION_EVENT = 15;
    const ROTATE_EVENT = 4;

    private $eventHeadPackStr = '';
    private $formatDescriptionEventDataPackStr = '';

    public function __construct() {
        $this->eventHeadPackStr = $this->buildPackStr([
            'timestamp' => 'l',
            'type_code' => 'c',
            'server_id' => 'l',
            'event_length' => 'l',
            'next_position' => 'l',
            'flags' => 's',
        ]);
        $this->formatDescriptionEventDataPackStr = $this->buildPackStr([
            'binlog_version' => 's',
            'server_version' => 'a50',
            'create_timestamp' => 'l',
            'head_length' => 'c',
        ]);
    }

    private function buildPackStr(array $struct): string {
        return implode('/', array_map(fn($v, $k) => $v . $k, $struct, array_keys($struct)));
    }

    protected function isBinlog($file): bool {
        rewind($file);
        return fread($file, strlen(self::BINLOG_HEAD)) === self::BINLOG_HEAD;
    }

    protected function readFormatDescriptionEvent($file): ?array {
        fseek($file, strlen(self::BINLOG_HEAD), SEEK_SET);
        $head = unpack($this->eventHeadPackStr, fread($file, self::EVENT_HEAD_SIZE));
        if ($head['type_code'] !== self::FORMAT_DESCRIPTION_EVENT) return null;
        $data = unpack($this->formatDescriptionEventDataPackStr, fread($file, self::FORMAT_DESCRIPTION_EVENT_DATA_SIZE));
        return ['head' => $head, 'data' => $data];
    }

    protected function readRotateEvent($file): ?array {
        $bufsize = 100;
        $size_pos = 8; // 64bit position
        fseek($file, -$bufsize, SEEK_END);
        $buf = fread($file, $bufsize);
        $min_begin = strlen(self::BINLOG_HEAD) + self::EVENT_HEAD_SIZE + $size_pos;
        $ok = false;
        for ($i = $bufsize - 1; $i > $min_begin; $i--) {
            if ($buf[$i] === "\0") { $ok = true; break; }
        }
        if (!$ok) return null;
        $next_filename = substr($buf, $i + 1);
        $head_str = substr($buf, $i + 1 - $size_pos - self::EVENT_HEAD_SIZE, self::EVENT_HEAD_SIZE);
        $head = unpack($this->eventHeadPackStr, $head_str);
        if ($head['type_code'] !== self::ROTATE_EVENT) return null;
        return ['head' => $head, 'nextFile' => $next_filename];
    }

    public function read(string $path): ?array {
        $file = fopen($path, 'r');
        if (!$file || !$this->isBinlog($file)) { if ($file) fclose($file); return null; }
        $fde = $this->readFormatDescriptionEvent($file);
        $re  = $this->readRotateEvent($file);
        fclose($file);
        if (!$fde || !$re) return null;
        return [
            'beginAt'      => $fde['head']['timestamp'],
            'endAt'        => $re['head']['timestamp'],
            'nextFile'     => $re['nextFile'],
            'serverVersion'=> rtrim($fde['data']['server_version'], "\0"),
        ];
    }
}

// 使用範例
$info = (new BinlogInfo())->read('/var/lib/mysql/mysql-bin.000001');
print_r($info);

輸出範例

Array
(
    [beginAt] => 1704067200
    [endAt] => 1704153600
    [nextFile] => mysql-bin.000002
    [serverVersion] => 5.7.42-log
)

優勢

  • 不依賴 mysqlbinlog,無需順序掃描全文件。
  • 毫秒級讀取首尾事件,適合大型 Binlog 管理、備份窗口核對、自動化清理腳本。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved