需求:
登陆管理表,将历史密码表(哈希后),历史邮箱表,登录记录表(v4 and v6),token历史表连接到user表。
表结构和约束条件都很清晰,分别创立以下列:
编号 | 列明 | 类型 | 限制 |
1 | user_id | INT | primary key |
2 | user_pwd | VARCHAR | |
3 | user_pwd_logid | VARCHAR | unique key |
4 | user_email | VARCHAR | |
5 | user_email_logid | VARCHAR | unique key |
6 | user_login | VARCHAR | |
7 | user_login_logid | VARCHAR | unique key |
8 | user_auth | VARCHAR | |
9 | user_auth_logid | VARCHAR | unique key |
进一步需求:考虑到logid的不可重复性,在生成此类ID时需要考虑自创UUID的长度,UUID规则目前没有设计好。
OK,出现问题了。如果UUID的规则没有设计好,意味着各种logid列的长度是当前表设计的时候不可知的。于是开始打VARCHAR长度的主意。这里会涉及到一个坑,MYSQL使用innoDB引擎时候的ROW SIZE限制在对于设置了unique key的列时候不是共享的,而是每个unique的列自己有一个最大值的。
以下是掉进坑里挣扎和最终爬出来的过程:
看到这种验证逻辑,很明显就是需要事务出来干活了,因此选用了innodb引擎,根据MYSQL8.0手册,所有列共享了ROW SIZE限制。但是手册上在varchar这一部分没说的是这时候作为 unique key做了索引的列来说,限制是3072字节。结果我就掉到坑里去了,以使用UTF-16对接java 11 程序考虑,一个字符是2-4字节,数据库建表时候取了4字节作为默认值,也就是说如果限制成unique,在使用UTF16编码前提下只能放进去最多768个字符。因为UUID生成规则目前不明,我随手就用了个VARCHAR(1000)结果就报错了。后期的修改过程中,又因为其他列长度限制的影响完全忽略了那些logid是带着unique的索引列。这下子可好玩了,字符数量的限制一会儿是65535/4约为16000个字符,一会儿是3072/4 = 768字符,脑袋彻底乱掉了。最后还是社区里一位大佬帮忙,一眼看到了报错信息中的specified key这个关键词,指出设计时候这里出错了,这才从坑里面爬出来了。
总结经验教训:
1 下次尽可能要求UUID长度定下来以后再设计表,不要自信心爆棚以为随便给个大值就能兼容后面一切捣腾,虽然想到了使用unique限制UUID这种连进来的外键来避免UUID算法可能产生的碰撞,但是unique限制后会触及数据库底层效率架构,在设计表,迁移数据等场景下都可能产生问题。
2 掉进坑里后自己扑腾的时候要回去看设计图,看文档要比盯着SQL错误信息和deadline发慌有效太多了
3 必要时候最好使用远程数据库或者内网独立服务器验证避免陷入开发环境遭到暗中破坏这种想法,有个对比试验的环境最好了。