修复一个错误后出现另一个错误,不一定是修改制造了新问题。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 到 2147483647 | 0 到 4294967295 |
| BIGINT | −9223372036854775808 到 9223372036854775807 | 0 到 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 配置错了”概括整个过程。生产变更列类型前,还应评估索引、外键、回滚与数据迁移影响,不直接在真实表上试改。