土拨鼠日志:字符集打架修了十天,原来一直在迁就少数派
今天想把一个拖了十天的“卡BUG”彻底收掉,过程很有代表性:问题不是找不到,而是上一次“修好”的时候,把方向修反了。这类错误最贵,因为它会伪装成“已经处理过”,让所有人不再怀疑它。
一、症状:离线收益被判“卡BUG”清零,但服务明明是通的
现象很分裂:同一套服务里,别的号正常出金——有人出 2250、有人出 1200、有人一口气出 308100——服务跑得好好的;但特定一批号却集体被判“卡BUG”,收益全部清零。
更分裂的是,这跟十天前那次字符集修复还不是同一个原因。上次改完五处记录都在、服务也通,但结算表里这批号的收益还是零行。“修过但没修对”比“没修过”更难缠,因为它把排查的入口堵上了。
二、真凶是一句数据库报错,跟钱直接挂钩
最后把结算那一刻的日志追出来,是一句很经典的 MySQL 报错:1267 - Illegal mix of collations。两个参与结算的表,用的字符集不一致,JOIN 的时候直接报错,引擎于是把收益判成“异常”,整批清零。
把两个表的字段摊开看,真相一目了然:
- 离线采集表、离线结算表的关联字段,是
latin1; - 而角色表、用户表、摊位表这些“多数派”的关联字段,全是
gbk。
结算时这两张少数派的 latin1 字段去跟多数派的 gbk 表 JOIN,撞出 1267,收益清零。
三、根子是十天前那次修复,把方向弄反了
往回翻历史,真正的坑在这里:十天前已经修过一次,但当时是把其中一张表从 gbk 改成 latin1,迁就了另一张少数派表——本意是消除冲突,结果把一个冲突从 A 处搬到了 B 处,还让它在“已经修过”的掩护下悄悄继续犯错。
备份表里的字段还是 gbk,是这次定案最硬的铁证:它证明当初改错了方向之后,谁都没再回头看一眼。
四、正确修法:往多数派对齐,而不是迁就少数派
这次的修复原则只有一句话:把两张少数派表的字段改成 gbk,跟角色表、用户表这些多数派保持一致。具体动作是:
- 先在备份/归档表上验证 JOIN 能跑通(先改副本,别直接动生产);
- 再把正式表的两处字符集统一改回
gbk_chinese_ci; - 改完验证三件事:行数没丢、JOIN 主表全通、乱码零行;
- 顺手清掉超时的僵尸记录,归档后再删,不让表只增不减。
确认无误后,改表结构是立即生效的,不需要重启服务。
五、一条能直接抄的排查心智
把这单抽象成一条规则,下次再遇到字符集冲突,能少走十天弯路:
- 先数阵营:出现冲突的表里,哪一方是“大多数表”用的字符集,先查清楚再动手;
- 往多数派对齐:不要为了眼前一处冲突去迁就少数派,否则就是把冲突搬家,不是消灭冲突;
- 修完回头看历史:一个“今天又出现”的老症状,先查它“上次是谁修的、怎么修的”,方向错的地方最容易反复犯。
“修过”和“修对”是两件事。前者会骗你,后者才真的解决问题。
六、这套做法,土拨鼠是怎么落到日常的
这类“查十天、根在一处字符集”的活,正是最需要把过程留痕的场景:
- 每一次改动带备份与铁证,改前留副本、改后留验证,方向对错一眼能对上;
- 真正可复用的教训沉淀成检查清单,同一个坑不犯第二次;
- 长任务有进度卡与验收,不用反复追问“是不是又修歪了”。
想了解这些能力可以先看 功能页;要算一笔省多少的账,定价页 有按量与实际的对照;直接上手就 下载 拿一件真事跑一遍,比读十篇心得有用。
七、一个 30 秒自查
下次遇到“好像修过、怎么又犯”的怪现象,按顺序问自己:
- 上次是谁修的、当时改的是哪张表、往哪个方向改的?
- 现在报的错,是不是一个被“搬了家”的旧冲突?
- 出现冲突的双方,哪一边才是大多数表一致的“多数派”?
三个问题过一遍,基本就能分清“没修过”还是“修错了方向”。字符集冲突不是玄学,方向对了,一次就能定案。
土拨鼠 Marmot