主从复制1677,cannot be converted from type 'varchar(96(bytes))' 异常
背景:公司在做敏感信息加密存储的事情,加密后的字符串长度现在的字段长度放不下(当前:varchar(32)),需要扩充字段长度到varchar(255)。直接操作的话,当前场景Online DDL方式又不支持。一般情况下,为了不影响业务,让用户无感知,不影响使用体验,应当使用pt-osc或gh-ost这些开源工具来做,但是公司性质属于某企,使用的数据库使某云产品,现在杯具了!不允许使用这些开源工具。。。(xxoo),好吧。言归正传,没得办法,上面有要求只好采用备库扩充-HA切换-原主库扩充的方案了。现在备库扩充字段后遇到了 cannot be converted from type 'varchar(96(bytes))' to type 'varchar(765(bytes) utf8)' 错误。过程如下:
备库上临时关闭binlog写入
mysql> set session sql_log_bin=0;
Query OK, 0 rows affected (0.00 sec)
mysql> ALTER TABLE user_base_064 MODIFY COLUMN bank_account VARCHAR(255) DEFAULT NULL COMMENT 'ceshi-account';
Query OK, 368570 rows affected (25.64 sec)
Records: 368570Duplicates: 0Warnings: 0
ok,备库操作完毕,HA切换前先检查一波复制延迟情况,然后悲剧了。复制出现了异常。
检查了error.log发现报错信息
2025-03-27T16:48:41.047694+08:00 600 Slave SQL for channel '': Worker 1 failed executing transaction '498a666b-58cd-11ec-8c73-e8611f2a1bfb:84027231' at master log mysql-bin.004836, end_log_pos 15449089; Column 15 of table 'clw_user_okvo_0016.user_base_064' cannot be converted from type 'varchar(96(bytes))' to type 'varchar(765(bytes) utf8)', Error_code: 1677
好吧,去检查下回放relay log哪个事务异常了
mysqlbinlog -vv --base64-output=decode-rows slave-relay.000006 | less
SET @@SESSION.GTID_NEXT= '498a666b-58cd-11ec-8c73-e8611f2a1bfb:84027231'/*!*/;
# at 6930
#250327 16:47:01 server id 694425475end_log_pos 15448785 CRC32 0x750e6776 Query thread_id=17152062 exec_time=0 error_code=0
SET TIMESTAMP=1743065221/*!*/;
BEGIN
/*!*/;
# at 7016
#250327 16:47:01 server id 694425475end_log_pos 15448954 CRC32 0x1b549b94 Table_map: `clw_user_okvo_0016`.`user_base_064` mapped to number 161
# at 7185
#250327 16:47:01 server id 694425475end_log_pos 15449089 CRC32 0x7533c912 Write_rows: table id 161 flags: STMT_END_F
### INSERT INTO `clw_user_okvo_0016`.`user_base_064`
### SET
### @1=228850496 /* LONGINT meta=0 nullable=0 is_null=0 */
### @2=NULL /* LONGINT meta=96 nullable=1 is_null=1 */
### @3=NULL /* LONGINT meta=0 nullable=1 is_null=1 */
### @4=NULL /* LONGINT meta=18 nullable=1 is_null=1 */
### @5='2' /* VARSTRING(18) meta=18 nullable=1 is_null=0 */
### @6='80103864118|2006916' /* VARSTRING(765) meta=765 nullable=1 is_null=0 */
### @7=NULL /* VARSTRING(765) meta=96 nullable=1 is_null=1 */
### @8=NULL /* VARSTRING(765) meta=96 nullable=1 is_null=1 */
### @9=NULL /* VARSTRING(765) meta=765 nullable=1 is_null=1 */
### @10='80103864118' /* VARSTRING(3000) meta=3000 nullable=1 is_null=0 */
### @11='2006916' /* VARSTRING(96) meta=96 nullable=1 is_null=0 */
### @12=118 /* INT meta=0 nullable=1 is_null=0 */
### @13='2025-03-08 08:15:51' /* DATETIME(0) meta=0 nullable=1 is_null=0 */
### @14=0 /* INT meta=0 nullable=1 is_null=0 */
### @15=NULL /* INT meta=96 nullable=1 is_null=1 */
### @16=NULL /* INT meta=96 nullable=1 is_null=1 */
### @17=NULL /* INT meta=96 nullable=1 is_null=1 */
### @18=NULL /* INT meta=96 nullable=1 is_null=1 */
### @19=NULL /* INT meta=18 nullable=1 is_null=1 */
### @20=NULL /* INT meta=0 nullable=1 is_null=1 */
### @21=NULL /* INT meta=24 nullable=1 is_null=1 */
### @22=NULL /* INT meta=384 nullable=1 is_null=1 */
### @23=1 /* INT meta=0 nullable=1 is_null=0 */
### @24=NULL /* INT meta=96 nullable=1 is_null=1 */
### @25=NULL /* INT meta=384 nullable=1 is_null=1 */
### @26=NULL /* INT meta=0 nullable=1 is_null=1 */
### @27=0 /* INT meta=0 nullable=1 is_null=0 */
### @28=1 /* INT meta=0 nullable=1 is_null=0 */
### @29=NULL /* INT meta=96 nullable=1 is_null=1 */
### @30=NULL /* INT meta=96 nullable=1 is_null=1 */
### @31=NULL /* INT meta=96 nullable=1 is_null=1 */
### @32=NULL /* INT meta=96 nullable=1 is_null=1 */
### @33='2025-03-08 08:15:51' /* DATETIME(0) meta=0 nullable=1 is_null=0 */
### @34=NULL /* DATETIME(0) meta=0 nullable=1 is_null=1 */
### @35='2025-03-08 08:15:51' /* DATETIME(0) meta=0 nullable=1 is_null=0 */
### @36=1 /* INT meta=0 nullable=1 is_null=0 */
### @37=NULL /* INT meta=765 nullable=1 is_null=1 */
### @38=NULL /* INT meta=0 nullable=1 is_null=1 */
### @39=NULL /* INT meta=48 nullable=1 is_null=1 */
### @40=NULL /* INT meta=192 nullable=1 is_null=1 */
### @41=NULL /* INT meta=0 nullable=1 is_null=1 */
# at 7320
#250327 16:47:01 server id 694425475end_log_pos 15449120 CRC32 0xf5f6b3ba Xid = 4956006199
COMMIT/*!*/;
又通过审计日志查到了原始的SQL语句
replace INTO user_base ( user_type, phone, password, ou_code, reg_channel, reg_time, is_id_checked, user_state, is_internal_employee, is_order_charge, create_time, update_time, state ) VALUES ( '2', '80103864118|2006916', '80103864118', '2006916', 118, '2025-03-08 08:15:50.623', 0, 1, 0, 1, '2025-03-08 08:15:50.623', '2025-03-08 08:15:50.623', 1 ) ;
又检查了主备库的表结构情况,字符集,数据库版本等情况,完全一致。表结构如下:
CREATE TABLE `user_base` (
`id` bigint(20) NOT NULL AUTO_INCREMENT ,
`user_code` varchar(32) DEFAULT NULL,
`merge_userid` bigint(20) DEFAULT NULL,
`merge_type` varchar(6) DEFAULT NULL,
`user_type` varchar(6) DEFAULT NULL,
`phone` varchar(255) DEFAULT NULL,
`login_alias` varchar(32) DEFAULT NULL,
`user_name` varchar(32) DEFAULT NULL,
`real_name` varchar(255) DEFAULT NULL,
`password` varchar(1000) DEFAULT NULL,
`ou_code` varchar(32) DEFAULT NULL,
`reg_channel` int(6) DEFAULT NULL,
`reg_time` datetime DEFAULT CURRENT_TIMESTAMP,
`is_id_checked` int(6) DEFAULT '0',
`bank_code` varchar(32) DEFAULT NULL,
`bank_account` varchar(32) DEFAULT NULL,
`bank_account_no` varchar(32) DEFAULT NULL,
`bank_account_phone` varchar(32) DEFAULT NULL,
`user_level` varchar(6) DEFAULT NULL,
`cancel_time` datetime DEFAULT NULL,
`cancel_reason` varchar(8) DEFAULT NULL,
`cancel_content` varchar(128) DEFAULT NULL,
`user_state` int(6) DEFAULT '1',
`record_phone` varchar(32) DEFAULT NULL,
`token` varchar(128) DEFAULT NULL,
`operator_id` int(11) DEFAULT NULL,
`is_internal_employee` int(6) DEFAULT '0',
`is_order_charge` int(6) DEFAULT '1',
`referee_name` varchar(32) DEFAULT NULL,
`referee_phone` varchar(32) DEFAULT NULL,
`referee_unit` varchar(32) DEFAULT NULL,
`build_operator` varchar(32) DEFAULT NULL,
`create_time` datetime DEFAULT CURRENT_TIMESTAMP ,
`updater` int(11) DEFAULT NULL ,
`update_time` datetime DEFAULT NULL ,
`state` int(6) DEFAULT '1' ,
`remark` varchar(255) DEFAULT NULL ,
`is_proprietary_merchant` int(6) DEFAULT NULL ,
`stop_code` varchar(16) DEFAULT NULL,
`corp_bank` varchar(64) DEFAULT NULL ,
`channel_type` int(6) DEFAULT NULL ,
PRIMARY KEY (`id`),
KEY `phone` (`phone`),
KEY `login_alias` (`login_alias`),
KEY `token` (`token`),
KEY `Phone_UserType` (`phone`,`user_type`),
KEY `oucode` (`ou_code`) USING BTREE,
KEY `oucode_usertype` (`user_type`,`ou_code`) USING BTREE,
KEY `ubase_uptime_id` (`update_time`),
KEY `reg_time_ind` (`reg_time`),
KEY `phone_ind` (`phone`),
KEY `reg_channel_ind` (`reg_channel`),
KEY `id_type_ind` (`id`,`user_type`)
) ENGINE=InnoDB AUTO_INCREMENT=228874244 DEFAULT CHARSET=utf8;
之前,作者多使用gh-ost或gh-ost工具,再不济了使用Online DDL方式,备库操作-HA切换方式接触不多,现在请求各方大佬给点提示,需要往哪方面注意下。不胜感激!
受限,varchar类型从30扩展到255应该是可以支持online ddl的
其次,你遇到这个报错,大概率是主从服务器的字符集(或校验集)设置不一致,可以从server层、database层、table层、字段层、连接层等多个角度分别排查下 yejr 发表于 2025-3-28 10:24
受限,varchar类型从30扩展到255应该是可以支持online ddl的
其次,你遇到这个报错,大概率是主从服务器的 ...
叶老师,varchar(30) -> varchar(255),使用的是utf8字符集,跨越了255字节分界线,Online 方式会返回not support xxx的错误信息。
这个问题也排查到了原因,进行了复现,是slave_type_conversions参数设置为空的原因。设置为ALL_NON_LOSSY就OK了。:lol lizibin 发表于 2025-3-28 17:29
叶老师,varchar(30) -> varchar(255),使用的是utf8字符集,跨越了255字节分界线,Online 方式会返回not ...
临时设置 slave_type_conversions = ALL_NON_LOSSY 看起来似乎可行,但从本上还是要保证主从字符集、校验集及表结构全部一致的吧。
你的主从环境为啥会出现不一致的问题呢。 yejr 发表于 2025-3-28 17:39
临时设置 slave_type_conversions = ALL_NON_LOSSY 看起来似乎可行,但从本上还是要保证主从字符集、校验 ...
字符集这些核对过是一致的。主从为啥不一致,是因为要扩字段,在主库上操作会锁表,只能备库操作-HA切换这样子进行,备库上不加这个参数就复制异常了。:lol lizibin 发表于 2025-3-28 18:54
字符集这些核对过是一致的。主从为啥不一致,是因为要扩字段,在主库上操作会锁表,只能备库操作-HA切换 ...
终于看明白了,为啥某企就不让用开源的第三方工具呢 当前场景不支持在线DDL,又不让用第三方开源工具。
页:
[1]