IM 融合专题:后端架构师的核心修炼
大家好这里是「敖行客研发实战日记」GGB。这次的活儿是把一套外购的即时通讯系统下称 IM整体融进我们自研的微服务平台IM 自己的登录注册整个砍掉用户体系和平台合成一套历史聊天数据一条不丢。听起来是“搬家加接线”真动手才发现最难的不是代码是两套用户身份攒了几年的历史包袱。下面按踩坑顺序复盘。一、两套主键459 处身份引用悬在一根线上IM 全站的用户身份是它自己用户表的自增 id——好友、群成员、私聊、群消息、黑名单、收藏所有业务表的外键都指向这个自增号而平台侧的用户主键是另一套长整型业务 id。要把“两边的同一个人”真正合成一个人等于要给 IM 整个社交图谱的主键换血。把代码整个扒了一遍先得到一个吓人的数字全项目 459 处身份引用散在 21 个文件里。但继续扒下去发现天大的好消息——459 处全部经由“会话上下文取当前用户 id”这一个出口流进各张表没有一处绕道。也就是说根本不用碰那 459 处业务代码只要改“进入会话的那个值”再把存量数据做一次 id 重映射下游原封不动地跟着新值走。方案的核心就此定型平台用户表是唯一身份IM 用户表降级成“扩展资料表”主键直接等于平台用户 id。**经验**融合两套系统之前先找“身份的唯一出口”。出口收得越窄融合的代价越小身份取值散落得到处都是那才是真正的灾难。图 1IM 所有身份引用经由会话上下文单一出口流入各业务表二、注释会说谎号称“桥接列”的字段只填了 4 行IM 用户表里有一列注释明明白白写着“平台用户 id”看起来天生就是为融合准备的桥。差点直接拿它当迁移依据——好在动手前先跑了一轮只读诊断35 个 IM 用户里这一列只填了 4 个。再回头翻代码更狠IM 自己的代码从头到尾没有任何一处写这个列它是当年不知道谁手工补的数据。**经验**注释和字段名描述的是“愿望”代码和数据才是“事实”。任何要拿来当迁移依据的字段先数一遍填充率再确认到底有没有人在写它。三、数字虚高二十倍JOIN 扇出与逻辑删除的幽灵正式迁移前做匹配预演明明只有 35 个用户预演统计却跑出 780 条匹配。数字对不上立刻停手。根因有两层。第一层是统计 SQL 里四个 LEFT JOIN 发生了笛卡尔扇出一个用户的手机号能 JOIN 出几十行第二层才是真凶——平台用户表里躺着大量“逻辑删除”的历史账号它们还揣着和活人一模一样的手机号和用户名是这批幽灵把 JOIN 撑爆的。把逻辑删除的账号滤掉再跑数字严丝合缝35 16 个能匹配 19 个纯 IM 历史孤儿且无一冲突。这一步在迁移脚本里被做成了硬性“STOP 闸门”只读诊断的数字对不上总量后面的破坏性阶段一律不准执行。事后看这道闸门至少避免了一次“把两个人的聊天记录并到一个人头上”的事故。**经验**数据迁移脚本必须分段执行且第一段永远是只读诊断预演数字和总量对不上说明有你还没看见的数据质量问题在前面等着。图 2LEFT JOIN 扇出示意——逻辑删除账号携带重复手机号撑爆匹配统计四、匹配规则从四级优先级砍到一条最初的匹配设计很“完备”桥接列、手机号、用户名、邮箱四级优先级层层兜底尽可能多地把人对上。和项目经理对完最后砍成一条未删除账号且手机号一致才映射其余一律不映射宁可留孤儿。孤儿并不可怕老 id 和新体系的 id 永不冲突历史消息原样可查无非是这批账号没有平台身份、登不进来。错误映射才可怕——把 A 的聊天记录挂到 B 头上在公司里这叫事故。为了保险映射表上还压了一条唯一约束任何情况下都不允许两个 IM 账号并到同一个平台账号撞上就报错停下绝不静默吞掉。**经验**迁移匹配规则“简单且可解释”压倒“覆盖率高”。每多一级模糊兜底就多一分把人对错的概率。五、差点白干三周动手之前先盘家底用户合并方案定了下一步是把 IM 从新框架降级改造成平台的老框架微服务343 个 Java 文件包名、依赖体系、模块拆分全要动怎么估都是两三周起步的体力活。动手前习惯性翻了翻历史工程结果发现一个月前有个实验版本已经把这件事干完了降级、五模块拆分、连“共享会话登录”都写好了。把两边的类集合做了个 diff341 对 344业务类几乎完全一致差异清一色是启动类和配置类。于是策略从“从头降级”变成“整目录搬移加三处构建配置”第一次编译就通过预估三周的活变成了一天。**经验**接到“体力活”先别埋头干花半小时盘一遍家底。团队的历史分支和旧工程里往往埋着已经趟平的路。图 3两条路线对比——从头降级改造 vs 复用历史版本整体搬移六、最后一个急转弯把“同步”整个扔掉资料一致性的初版方案是“改哪同步哪”平台改了昵称头像就调接口把 IM 侧的数据刷一遍登录时兜底同步一次搜索时再落一次库——三条链路维护两份数据。写到一半越写越不对劲这分明是在用三个补丁去掩盖“数据存了两份”这个根问题。推翻重来。IM 侧所有“这个人叫什么、头像是什么”的展示读取收拢到一个资料门面读的时候实时批量取平台数据平台查不到的历史孤儿账号回落本地兜底。IM 自己的用户表彻底降级成“扩展资料表”只存 IM 专属的设置项平台侧一行代码都不用改。“同步”这个概念直接消失了——不存在同步不同步因为数据只有一份。顺带着“登录”的认知也被扭了过来融合之后不存在“平台登录之后 IM 再自动登录一次”这回事。两边共享同一套会话登录了平台访问 IM 天然就是登录态真正要接的只剩前端拿一张临时凭证去建立长连接一个接口的事。**经验**当你发现自己在写第三条同步链路的时候停下来想想能不能改成“只有一份数据”。同步是补丁单一数据源才是解。图 4资料读取链路——展示数据实时取自平台孤儿账号本地回落七、小结图 5本次融合问题总览现象 → 根因 → 解法融合两套系统先找“身份的唯一出口”出口越窄换血的代价越小。迁移必须设只读诊断闸门数字对不上就停别让破坏性操作跑在疑问上面。注释、字段名、文档都会说谎代码和数据不会迁移依据只认查证过的事实。匹配规则宁简勿全留下孤儿是遗憾把人对错是事故。动手之前盘家底历史工程里往往埋着已经趟平的路半小时能省三周。能实时读就不要同步单一数据源永远优于多份拷贝加一堆补丁。至此用户体系合一、历史数据分毫未丢、一次登录全端通行IM 从一座孤岛变成了平台里的一个普通微服务。融合这种活没有银弹能做的就是让每一步都踩在“验证过的事实”上而不是“看起来对”的假设上。