美文网首页
MySQL:主从复制转换错误问题

MySQL:主从复制转换错误问题

作者: 重庆八怪 | 来源:发表于2021-06-09 17:25 被阅读0次

    一、关于错误

    image.png

    总的来说这个错误还是比较常见,我们需要做的就是比较主从表的结构,这里主要对他的处理流程进行一个简单的解析。

    二、错误抛错点

    这个错误的抛错点为table_def::compatible_with函数,抛错类型为ER_SLAVE_CONVERSION_FAILED,抛错的时机为row log event在从库应用的时候会进行类型检查,见栈1。下列是抛错的部分:

            rli->report(report_level, ER_SLAVE_CONVERSION_FAILED, //转换出错报错
                        ER(ER_SLAVE_CONVERSION_FAILED),
                        col, db_name, tbl_name,
                        source_type.c_ptr_safe(), target_type.c_ptr_safe());
    

    这里我知道了上面的报错语句中varchar(80(bytes))为主库的类型和长度而varchar(20(bytes) latin1为从库的类型和长度,这里稍微观察一下可以发现,主库的信息中不包含字符集信息,而从库却包含了,但是从长度我们可以猜测出这里的80(bytes)可能为utf8mb4类型。主库的这些信息来自binlog,我们可以发现binlog中并没有每个字段的字符集信息,因此这里使用长度进行判定。

    三、table_def的信息来源

    这个信息在比较字段类型的时候起到了关键作用,如下:

      ulong m_size;           // Number of elements in the types array
      unsigned char *m_type;  // 字段类型
      uint m_field_metadata_size;
      uint16 *m_field_metadata; //可变字段长度信息
      uchar *m_null_bits; // 字段是否可以为空的一个位图
      uint16 m_flags;         // Table flags
      uchar *m_memory;
    

    我们发现这里实际上就是我们map event的信息,那么可以确认从库中本结构体的信息来源为map event,其中m_field_metadata为可变长度信息,那么这个信息的来源就是map event调用Field_varstring::do_save_field_metadata填入的,实际上他来自field_length这样一个值,这个值在建表的时候就已经加入字符集进行了计算,Create_field::create_length_to_internal_length负责进行转换见栈2,下面是一个varchar类型的转换:

      case MYSQL_TYPE_VARCHAR:
        length*= charset->mbmaxlen;//这里就是进行了转换,如果是utf8mb4那么charset->mbmaxlen为4
        key_length= length;
        pack_length= calc_pack_length(sql_type, length);
    

    四、比较的方式

    上面我们已经知道通过map event读取了主库端表的信息,那么接下来就可以获取从库的字段信息与其进行比较了,如果比较出现问题,则抛错。

    table_def::compatible_with 函数调入
    
    ->通过从库表的信息,获取字段的总长度
      ->循环每一个字段
        ->调用can_convert_field_to,通过上面的table_def信息(map event代表主库的信息)和从库当前表的字段进行比较
           ->比较类型是否相同
             ->如果相等,且没有metadata,则说明不是可变字段,则直接返回true
             ->如果存在metadata则比较长度,如果不相等则返回false
               我们刚才已经说了字段的长度已经加入了字符集,因此如果字符集不同即便同样的长度
               这里也会比较不成功,当然这里受到参数slave_type_conversions的影响,但是这个参数 
               一般不会设置因此忽略掉,这里通过order字段返回比较是否相等。
           ->如果字段类型不同,但是metadata为0,也就是非可变类型,主要对timestamp,datetime,time几个类型进行转换判断
              如果能够转换也返回true
           ->最后对其他情况进行判定,但是其他情况中如果slave_type_conversions没有设置则直接返回为false
        ->如果can_convert_field_to返回为false,那么则进入报错流程,进行报错信息的组装,单上从库表的字符集,一同报错。
    

    这里我们主要讨论的是slave_type_conversions没有设置的情况,这也是通常的情况,如果设置了slave_type_conversions还有其他逻辑。其中is_conversion_ok函数主要是slave_type_conversions参数的实现,而order则是在slave_type_conversions设置了的情况下需要继续判断的一个返回值。

    这里对于字段的长度比较也比较简单直接调了Field::compatible_field_size,简单做compare函数。

    五、字符集

    我们可以发现这里的字符集完全来自字段,而和我们的通常看到的服务端参数的字符集没有太大的联系:

    +---------------------------+----------------------------------------------+
    | Variable_name             | Value                                        |
    +---------------------------+----------------------------------------------+
    | character_set_client      | utf8mb4                                      |
    | character_set_connection  | utf8mb4                                      |
    | character_set_database    | utf8mb4                                      |
    | character_set_filesystem  | binary                                       |
    | character_set_results     | utf8mb4                                      |
    | character_set_server      | utf8mb4                                      |
    | character_set_system      | utf8                                         |
    | character_sets_dir        | /opt/mysql/mysql3306/install/share/charsets/ |
    | ft_query_extra_word_chars | OFF                                          |
    +---------------------------+----------------------------------------------+
    

    如果出现字符集转换,那么会调入my_convert_internal/my_convert进行字符集的转换。

    附录:栈

    栈1:

    #0  Field::compatible_field_size (this=0x7fff95812310, field_metadata=20, rli_arg=0x41eeb50, mflags=1, order_var=0x7fffd8082994)
        at /opt/percona-server-locks-detail-5.7.22/sql/field.cc:1805
    #1  0x000000000182c074 in can_convert_field_to (field=0x7fff95812310, source_type=MYSQL_TYPE_VARCHAR, metadata=20, rli=0x41eeb50, mflags=1, order_var=0x7fffd8082994)
        at /opt/percona-server-locks-detail-5.7.22/sql/rpl_utility.cc:411
    #2  0x000000000182c74f in table_def::compatible_with (this=0x7fff9572a660, thd=0x7fff958630e0, rli=0x41eeb50, table=0x7fff954f2d80, conv_table_var=0x7fffd8082ba8)
        at /opt/percona-server-locks-detail-5.7.22/sql/rpl_utility.cc:622
    #3  0x00000000017e7e11 in Rows_log_event::do_apply_event (this=0x347e160, rli=0x41eeb50) at /opt/percona-server-locks-detail-5.7.22/sql/log_event.cc:11332
    #4  0x00000000017f7c6e in Log_event::do_apply_event_worker (this=0x347e160, w=0x41eeb50) at /opt/percona-server-locks-detail-5.7.22/sql/log_event.cc:765
    #5  0x00000000018720b0 in Slave_worker::slave_worker_exec_event (this=0x41eeb50, ev=0x347e160) at /opt/percona-server-locks-detail-5.7.22/sql/rpl_rli_pdb.cc:1890
    #6  0x000000000187414e in slave_worker_exec_job_group (worker=0x41eeb50, rli=0x4081020) at /opt/percona-server-locks-detail-5.7.22/sql/rpl_rli_pdb.cc:2671
    #7  0x000000000184bc29 in handle_slave_worker (arg=0x41eeb50) at /opt/percona-server-locks-detail-5.7.22/sql/rpl_slave.cc:6239
    #8  0x00000000018ce0b2 in pfs_spawn_thread (arg=0x403d020) at /opt/percona-server-locks-detail-5.7.22/storage/perfschema/pfs.cc:2190
    #9  0x00007ffff7bc6ea5 in start_thread () from /lib64/libpthread.so.0
    #10 0x00007ffff66748dd in clone () from /lib64/libc.so.6
    

    栈2:

    #0  Create_field::create_length_to_internal_length (this=0x7fff94006c50) at /opt/percona-server-locks-detail-5.7.22/sql/field.cc:10397
    #1  0x00000000015f9b85 in mysql_prepare_create_table (thd=0x7fff94000b70, error_schema_name=0x7fff94006b40 "testmap", error_table_name=0x7fff94006578 "test7", create_info=0x7fffdac349a0, 
        alter_info=0x7fffdac34410, tmp_table=false, db_options=0x7fffdac33f58, file=0x7fff94006ee0, key_info_buffer=0x7fffdac34280, key_count=0x7fffdac3427c, select_field_count=0)
        at /opt/percona-server-locks-detail-5.7.22/sql/sql_table.cc:3882
    #2  0x00000000015fdf3c in create_table_impl (thd=0x7fff94000b70, db=0x7fff94006b40 "testmap", table_name=0x7fff94006578 "test7", error_table_name=0x7fff94006578 "test7", 
        path=0x7fffdac34070 "./testmap/test7", create_info=0x7fffdac349a0, alter_info=0x7fffdac34410, internal_tmp_table=false, select_field_count=0, no_ha_table=false, 
        is_trans=0x7fffdac342fe, key_info=0x7fffdac34280, key_count=0x7fffdac3427c) at /opt/percona-server-locks-detail-5.7.22/sql/sql_table.cc:5394
    #3  0x00000000015feb09 in mysql_create_table_no_lock (thd=0x7fff94000b70, db=0x7fff94006b40 "testmap", table_name=0x7fff94006578 "test7", create_info=0x7fffdac349a0, 
        alter_info=0x7fffdac34410, select_field_count=0, is_trans=0x7fffdac342fe) at /opt/percona-server-locks-detail-5.7.22/sql/sql_table.cc:5675
    #4  0x00000000015fec31 in mysql_create_table (thd=0x7fff94000b70, create_table=0x7fff940065b8, create_info=0x7fffdac349a0, alter_info=0x7fffdac34410)
        at /opt/percona-server-locks-detail-5.7.22/sql/sql_table.cc:5723
    #5  0x000000000156aec5 in mysql_execute_command (thd=0x7fff94000b70, first_level=true) at /opt/percona-server-locks-detail-5.7.22/sql/sql_parse.cc:3429
    #6  0x0000000001571bed in mysql_parse (thd=0x7fff94000b70, parser_state=0x7fffdac355b0) at /opt/percona-server-locks-detail-5.7.22/sql/sql_parse.cc:5901
    #7  0x000000000156673d in dispatch_command (thd=0x7fff94000b70, com_data=0x7fffdac35d90, command=COM_QUERY) at /opt/percona-server-locks-detail-5.7.22/sql/sql_parse.cc:1490
    #8  0x00000000015655c5 in do_command (thd=0x7fff94000b70) at /opt/percona-server-locks-detail-5.7.22/sql/sql_parse.cc:1021
    #9  0x00000000016a635c in handle_connection (arg=0x3b9f360) at /opt/percona-server-locks-detail-5.7.22/sql/conn_handler/connection_handler_per_thread.cc:312
    #10 0x00000000018ce0b2 in pfs_spawn_thread (arg=0x416c750) at /opt/percona-server-locks-detail-5.7.22/storage/perfschema/pfs.cc:2190
    #11 0x00007ffff7bc6ea5 in start_thread () from /lib64/libpthread.so.0
    #12 0x00007ffff66748dd in clone () from /lib64/libc.so.6
    

    相关文章

      网友评论

          本文标题:MySQL:主从复制转换错误问题

          本文链接:https://www.haomeiwen.com/subject/igmeeltx.html