修复一个错误后出现另一个错误,不一定是修改制造了新问题。Sharding-JDBC 的这次插入排障,先卡在路由取值,再卡在数据库主键容量;后一个问题原本被前一个挡住了。

第一层:分片路由值不符合要求

2019 年使用 4.0.0-RC2 时,INSERT 报错:

java.lang.IllegalArgumentException:
Sharding value must implements Comparable.
  InsertClauseShardingConditionEngine.getRouteValue
  InsertClauseShardingConditionEngine.fillShardingCondition
  ShardingInsertOptimizeEngine.optimize
  PreparedStatementRoutingEngine.route

原 SQL 含 image_id 参数。后来我改为配置分布式主键生成,并取消该列数据库自增,原错误不再出现。

这记录了当时的处理,但不能推导为“分片字段不能自增”。需先看分片列到底取到了什么参数,是 null、普通数值、复杂对象还是尚未生成的键;数据库自增、全局唯一性和分片路由也是不同问题。原配置没有留存,无法把其中某个改动认定为唯一根因。

第二层:ID 生成了,却写不进 INT

接着出现 image_id 超范围。生成的 Snowflake ID 约 18 位,而表里仍是 INT(11)。我先改成 INT(20),依旧失败;改为 BIGINT 后插入恢复。

括号里的数是旧版整数显示宽度,不扩大底层容量:

类型有符号范围无符号范围
INT−2147483648 到 21474836470 到 4294967295
BIGINT−9223372036854775808 到 92233720368547758070 到 18446744073709551615

因此把 INT(11) 改成 INT(20) 不是把存储升级到 20 位。是否有符号需与生成器和代码类型一起设计。MySQL 8.0 整数范围

不要只改数据库

Java 中通常用 Long 承载有符号 64 位 ID,同时核对 Mapper、DTO 和数据库列;BIGINT UNSIGNED 的完整范围不能全部放进 Java Long。

JavaScript Number 的最大安全整数是 9007199254740991,许多 Snowflake ID 超过它。跨 JSON 接口时,把 ID 作为十进制字符串传递,避免前端解析数字后已经丢失精度;事后再转字符串救不回来。

站内 Snowflake ID 工具 可以辅助理解位结构,但不能替代全链路类型设计。

每次都重新读取最新根因

路由错误解决后,语句继续执行才碰到容量错误。排查时分别记录“取到哪个分片值”“生成了哪个主键”“数据库接受什么类型”,不要用一条笼统的“分布式 ID 配置错了”概括整个过程。生产变更列类型前,还应评估索引、外键、回滚与数据迁移影响,不直接在真实表上试改。