SELECT phone_hmac, count(*) FROM users
GROUP BY phone_hmac HAVING count(*) > 1;
ALTER TABLE users ADD UNIQUE KEY uk_phone_hmac (phone_hmac);

手机号改成加密存储后,唯一索引建在密文列上,同一个号码用随机 IV 加密两次得到不同密文,重复注册两次都成功,库里出现两条同一手机号的账号。先别急着动参数,把报错原文和时间点记下来,顺序反了容易白折腾。

原因不复杂,随机 IV 让相同明文每次密文都不同,密文列上的唯一约束失去意义。业务需要的是明文唯一,不是密文唯一。去重逻辑还在按密文查,永远查不到已有记录。

「密文唯一索引」排查到最后往往是很小的一处差异,前面那些复杂猜测可以先放一放。备份不是备了就行,恢复一次才算数,很多备份文件打开才发现是空的。

处理上按这个顺序:另存一列手机号的 HMAC 值 用固定密钥计算 建唯一索引;注册前按 HMAC 查询是否已存在;密文列只负责存储 不参与唯一约束和查询;用同一号码注册两次确认第二次被拒。

改完只确认一件事:第二次注册返回 409 唯一索引冲突 分组查询查不到重复的 phone_hmac。

作者 9823189