如何通過binlog獲取我們想要的MySql語句?


前言

MySqlbinlog一般用於我們對數據的恢復,以及從數據庫對主數據庫的復制和更新。
假設此時我們有一個需要查詢和讀取Mysql最近操作DDL的信息,我們需要怎么處理?
聰明的你可能已經想到了,我們可以使用mysqlbinlog工具讀取啊!的確,mysqlbinlog對於statement或者mixed格式的binlog文件確實會很方便讀取,但是你要知道,從Mysql5.7.7開始,row就是默認的binlog_format,此時我們再要去直接通過肉眼去看,恐怕就不是那么容易了。

d9630274-0be7-465a-8a0d-46dbe3d2441a.png

即使我們在通過mysqlbinlog解析時加上-v參數,也只能顯示出這樣的效果:

f764edef-7cbd-421a-a6ea-d468aa803353.png

於是,我寫了一個binlog2sql的初級版本,來實現sql語句的轉換。

實現

實現過程不是很復雜,主要是通過mysqlbinlog來提取我們需要的DDL語句,然后我們再通過我們的方法來把這些語句轉化為我們可以識別的sql語句。

核心代碼:

/**
     * @return $this
     */
    protected function selectFromBinLog()
    {
        $fillFile = Util::getFile(__DIR__ . '/data/file.sql');
        file_put_contents($fillFile, "");
        exec("mysqlbinlog -v --database='" . Conf::__DATABASE__ . "' $this->_binlog_basename/$this->_binlog_file | grep -E -i '###|UPDATE|INSERT|DELETE' >> $fillFile");
        return $this;
    }

    /**
     * @return $this
     */
    protected function parseSql()
    {
        $fillFileHandler = fopen(__DIR__ . '/data/file.sql', 'r');
        $sqlArr = [];
        if ($this->_type == 'ROW') {
            $match = NULL;
            $sqlStr = "";
            while (($sql = fgets($fillFileHandler)) !== false) {
                if (($match = preg_match('/UPDATE|INSERT|DELETE/', $sql)) || strrpos($sql, 'end_log_pos') !== false) {
                    # 如果有指定表
                    if ($match && $this->_table && strpos($sql, $this->_table) === false) continue;
                    $sqlStr == '' || array_push($sqlArr, $sqlStr);
                    $sqlStr = $match ? trim(substr($sql, 3, -1)) . " " : "";
                } elseif (strpos($sql, '@') !== false || strpos($sql, 'SET')) {
                    $sqlStr .= trim(substr($sql, 3, -1)) . " ";
                }
            }
            $sqlStr == '' || array_push($sqlArr, $sqlStr);
        } else {
            # statement 和 mixed格式一樣
            while (($sql = fgets($fillFileHandler)) !== false) {
                $sql = trim($sql);
                if (preg_match('/(UPDATE|INSERT|DELETE)\s+/', $sql)) {
                    array_push($sqlArr, $sql);
                }
            }
        }
        $sqlArr = array_map(function ($value) {
            return preg_replace_callback('/(@(\d+))/', function ($matches) use ($value) {
                $parts = explode('.', $value);
                return $this->getTableColumns(explode('`', array_pop($parts))[1])[$matches[2] - 1];
            }, $value);
        }, $sqlArr);

        $mysqlFile = Util::getFile(__DIR__ . '/data/mysql.sql');

        array_map(function ($value) use ($mysqlFile) {
            file_put_contents($mysqlFile, $value . PHP_EOL, FILE_APPEND);
        }, $sqlArr);
        fclose($fillFileHandler);

        return $this;
    }

    /**
     * @param $table
     * @return array
     */
    protected function getTableColumns($table)
    {
        if (array_key_exists($table, $this->_tableColumns))
            return $this->_tableColumns[$table];
        $tableInfo = $this->select("show full columns from $table");
        if (empty($tableInfo)) Util::dd("$table 不存在");
        return $this->_tableColumns[$table] = array_column($tableInfo, 'Field');
    }

其中有三個主要的方法,selectFromBinLog用於執行mysqlbinlog,用於提取我們所需要的DDLparseSql用於解析我們提取出來的sqlgetTableColumns用於獲取表的字段(主要是針對row模式下的@1,@2之類)。

當我們執行Binlog.phpstart方法之后,就可以把DDL寫入到'./data/mysql.sql'中了,非常方便。

99b20b6e-d44e-4838-9827-e7c02d3fd69e.png


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM