3. 主键玄学
Introduction
关于主键, 常见的问题包含
什么是主键, 什么是唯一键
什么是自增键, 自增键是怎么做到的
为什么自增主键可以是不连续的
主键跟唯一键之间什么关系
可以用符合主键吗? 为什么不推荐使用复合主键
为什么建议使用无意义的字段作为主键
snowflake的存在意义是什么
我们常用auto_increment
来表示一个自增主键, 每次使用就加一, 按理说没什么问题, 用多就会发现有坑, 这东西可能会遭遇写入失败的问题, 原因竟然能是主键重复
auto_increment
的实现原理是在内存里维护一个值, 每次使用就加一下, 而常规的insert操作是不会对这个值加锁的, 并发写入就有可能导致主键重复.
好, 那我们不搞常规, 用事务来写入, 这种情况下的确会上锁, 假设现在两个事务, 按照自增主键一个id=10, 一个id=11, 现在第一个事务回滚了, 而第二个id=11却得以保留, 这直接会导致自增主键不连续. 除此之外, 删除也会搞的主键不连续.
内存? 那我重启
这个值既然是维护在内存里面, 那重启的时候怎么恢复这个值? 在很久以前, 我们通过select max(id)
的方式来恢复这个值, 但是这种方法有点危险.
假设现在插入一条id=10, 然后在另一个表里插入一个外键where id=10
, 然后我们只删主表里的id=10, 另一个表里的不管, 然后重启. 下次重启的时候我们通过select max(id)
得出下一条插入的id应该等于10. 想想这种情况, 那么这条记录一插入就能立刻得到另一个表里有where id=10
关联着. 这是非常严重的错误.
说到主键...
与唯一键的区别
唯一键本身并不是键哈, 正确叫法是"唯一索引", 索引跟键差别就挺大的了
主键是key, 唯一索引是index, 键的本质是一种约束, 而索引只是一个方便你查询的冗余数据结构
主键不能为空, 而唯一索引可以为空
每个主键都会生成出一个唯一索引
为什么不使用复杂/复合主键
理论上主键是可以复合的, 但不建议这样做, 我们上面说了主键会生成(聚簇)索引, 按照主键值会进行叶节点排序, 你搞复杂的排序不方便
主键为什么是无意义的
无意义的唯一目的就是为了保证唯一, 换句话说主键不应该跟任何业务含义有挂钩, 因为任何有意义的字段都有潜在的冲突风险
我们举个例子, 18位的身份证号, 除去地区/生日/checksum以外只有3位表示性别+编号. 这意味着同一天, 同一个地区里, 男性新生儿只能有500个, 女性新生儿500个, 你想想海淀有多大, 500个应该还是挺容易突破的吧. 这就是id不应该跟业务产生挂钩的原因
另一个例子是snowflake, snowflake会产生64位的数字id, 其中包含有 毫秒时间戳 + 机房编号 + 机器编号, 最后只有12位表示序号, 这意味着同一个机房的同一个机器, 一毫秒内最多生产2^12次方个id
Last updated
Was this helpful?