MySQL ReplicateWe often see an int column of a table that needs to be changed to unsigned-int and then unsigned-bigint due to the value being out of range. Sometimes, there may even be blockers that prevent us from directly altering the table or applying pt-online-schema-change on the primary, which requires the rotation solution: apply the change on the replica first, switch over the writes to the replica, and then apply the change on the previous primary. In this case, MySQL will have to replicate unsigned-int to unsigned-bigint for a while.

One might think it is obvious and straightforward that MySQL should be able to replicate unsigned-int to unsigned-bigint because unsigned-bigint has a larger size(8 bytes) which covers unsigned-int(4 bytes). It is partly true, but there are some tricks in practice. This blog will show you those tricks through the scenarios.

Let’s understand the scenarios and issues that one may face when replicating from unsigned-int in primary to unsigned-bigint in replica. For scenarios one and two, we will only focus on the binlog_format=ROW, because with binlog_format=STATEMENT,  “If the statement run on the source would also execute successfully on the replica, it should also replicate successfully” – MySQL doc. However, for scenario three, we tested for both binlog_format=ROW and binlog_format=STATEMENT.

First, set up the table. Here we have a table test_replication_differ_type with unsigned int in primary and unsigned bigint in replica.

Primary: Server version: MySQL 8.0.28

Table: 

Replica: Server version: MySQL 8.0.28

Table: 

Scenario one: Insert into an unsigned int in primary and replicate to unsigned bigint

on Primary:

on Replica:

Analysis and solution:

Immediately we see an error stopping us from replicating. Now there’s a configuration option “slave_type_conversions” which controls the type conversion mode used on the replica. The reason for this replication error is because of that, on the replica, the slave_type_conversions variable is NOT set by default.

Setting slave_type_conversions to the mode ALL_LOSSY will not work because: ”requiring either lossy conversions or no conversion at all are permitted; for example, enabling only this mode permits an INT column to be converted to TINYINT (a lossy conversion), but not a TINYINT column to an INT column (non-lossy).– MySQL doc 

Setting slave_type_conversions to the mode ALL_NON_LOSSY will work because the mode: “This mode permits conversions that do not require truncation or other special handling of the source value; that is, it permits conversions where the target type has a wider range than the source type.”  – MySQL doc 

Don’t be confused by the name, ALL_LOSSY and ALL_NON_LOSSY are not exclusive, instead, they can be parallelly added to permit both modes:

On Replica:

On Primary:

On Replica:

Thus, if you want to replicate with different data types, consider using the mode ALL_NON_LOSSY and/or ALL_LOSSY in the global variable slave_type_conversions appropriately.

Scenario two: Insert a value out-of-range of signed int and replicate to unsigned bigint 

The range value of a signed int is (-2147483648, 2147483647). Let’s try to insert 2147483647+1 into an unsigned int column on the primary and observe how that replicates. Should be okay right?

On Primary:

On Replica:

Uh-oh, we insert 2147483648 on the primary but got a 0 on the replica.

Analysis and solution:

Checking the binlog file of primary, we can see that the unsigned int value 2147483648 was written as @2=-2147483648 (2147483648) as shown below.

However, “When an integer type is promoted, its signedness is not preserved. By default, the replica treats all such values as signed”, and so -2147483648 (2147483648) is treated as signed value -2147483648, and eventually turns into 0 as for the minimum of unsigned bigint is 0.

We need to tell the MySQL replica to treat the value -2147483648 (2147483648) as unsigned int instead of signed int by adding a mode ALL_UNSIGNED to the variable slave_type_conversions as below example:

On Replica

On Primary

On Replica

We can also add the mode as ALL_SIGNED, but this time, it is not like ALL_LOSSY and ALL_NON_LOSSY which are parallel. ALL_SIGNED has a higher priority than ALL_UNSIGNED, which means MySQL will first treat the value as signed if possible, otherwise treat the value as unsigned.

On Replica

On Primary

On Replica

Scenario three: Replica with an extra column and not all common columns are of the same data type.

The documentation reads: “In addition, when the replica’s copy of the table has more columns than the source’s copy, each column common to the tables must use the same data type in both tables.– MySQL doc 

Let us verify if that’s the behavior.

On Replica

On Primary

On Replica

Now, as our experiment says, the statement in the documentation isn’t correct. The fact is that even if the replicate_col has data type bigint(20) unsigned on the replica other than the int(10) unsigned on the primary, and the replica has an extra column extra_col, it can still replicate well.

I already filed a bug report to Oracle MySQL.

Conclusion

In this blog, I illustrated how to set up the variable slave_type_conversions, the purpose and the difference between ALL_NON_LOSSY and ALL_LOSSY modes, as well as ALL_UNSIGNED and ALL_SIGNED.

Specifically, in order to have MySQL replicate from unsigned-int to unsigned-bigint correctly, we need to set up the variable slave_type_conversions=’ALL_NON_LOSSY,ALL_UNSIGNED’ OR slave_type_conversions=’ALL_NON_LOSSY,ALL_LOSSY,ALL_UNSIGNED’

Also please note that when the replica has an extra column than the primary, as long as the common columns are the same name and in the same order and before the extra column, even when a common column data type is different from the primary, the replication can still go well.

Subscribe
Notify of
guest

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
lefred

Nice post Edwin, just two comments:
1) please use the new terminology and commands (ex: SHOW REPLICA STATUS\G)
2) use html code like pre for a better layout of the post.
Cheers,