(目录)

问题

使用binlog2sql解析MySQL8.0时发现列与值的对应关系错误,无法直接使用解析出的语句进行回滚

原因

SELECT FROM COLUMNS表不会自动排序, 安装binlog2sql时使用的pymysqlreplication依赖包版本较低,其中也没有对列进行排序。 参考: MySQL :: MySQL 8.0 Reference Manual :: 26.3.8 The INFORMATION_SCHEMA COLUMNS Table

处理方法

更改pymysqlreplication/binlogstream.py中的代码。在__get_table_information中添加上order by ORDINAL_POSITION asc 更改后的记录

def __get_table_information(self, schema, table):
    for i in range(1, 3):
        try:
            if not self.__connected_ctl:
                self.__connect_to_ctl()

            cur = self._ctl_connection.cursor()
            cur.execute(
                """
                    SELECT
                        COLUMN_NAME, COLLATION_NAME, CHARACTER_SET_NAME,
                        COLUMN_COMMENT, COLUMN_TYPE, COLUMN_KEY
                    FROM
                        information_schema.columns
                    WHERE
                        table_schema = %s AND table_name = %s
                    ORDER BY ORDINAL_POSITION ASC
                    """, (schema, table))

            return cur.fetchall()
        except pymysql.OperationalError as error:
            code, message = error.args
            if code in MYSQL_EXPECTED_ERROR_CODES:
                self.__connected_ctl = False
                continue
            else:
                raise error

排查过程

检查数据库信息

首先列与值的对应关系错误,但是列名和值都是没问题的,怀疑是从information_schema.columns从获取列信息时出现问题,然后查询information_schema.columns表,发现查询出现问题的表时,结果中的ORDINAL_POSITION不是升序排列的,没有问题的表结果是按照ORDINAL_POSITION升序排列的,进而怀疑是在获取列信息时没有进行排序。

检查源码

首先检查binlog2sql的代码,发现没有对information_schema.columns中的查询,估计是从import中进行的调用,查看解析SQL的执行过程 binlog2sql.py文件中binlog2sql.process_binlog() 中调用BinLogStreamReader from pymysqlreplication import BinLogStreamReader 继续排查pymysqlreplication中的内容 当前安装的pymysqlreplication版本0.13,查看对应源码 BinLogStreamReader在binlogstream.py文件中,其中有一个函数__get_table_information(self, schema, table)确认是获取列信息时没有排序 内容是从information_schema.columns中获取列信息,源记录:

def __get_table_information(self, schema, table):
    for i in range(1, 3):
        try:
            if not self.__connected_ctl:
                self.__connect_to_ctl()

            cur = self._ctl_connection.cursor()
            cur.execute(
                """
                    SELECT
                        COLUMN_NAME, COLLATION_NAME, CHARACTER_SET_NAME,
                        COLUMN_COMMENT, COLUMN_TYPE, COLUMN_KEY
                    FROM
                        information_schema.columns
                    WHERE
                        table_schema = %s AND table_name = %s
                    """, (schema, table))

            return cur.fetchall()
        except pymysql.OperationalError as error:
            code, message = error.args
            if code in MYSQL_EXPECTED_ERROR_CODES:
                self.__connected_ctl = False
                continue
            else:
                raise error