专栏首页Lambda浅谈数据库主键策略

浅谈数据库主键策略

浅谈数据库主键策略

数据库表的主键很多童鞋都非常熟悉了,主键就是Primary Key,简称PK。

数据库主键的作用是唯一标识一条记录,所以在同一张表中,任意一条记录的主键都是唯一的,不然,数据库系统就无法根据主键直接定位记录。

虽然数据库系统本身对主键没有特别的要求,但是,写程序的时候,要考虑清楚使用什么类型的主键。正确地使用主键是存储数据成功的一半,错误地使用主键会让一个应用逐渐走向崩溃。

主键不可修改

对于数据库来说,主键其实是可以修改的,只要不和其他主键冲突就可以。但是,对于应用来说,如果一条记录要修改主键,那就会出大问题。

因为主键的第二个作用是让其他表的外键引用自己,从而实现关系结构。一旦某个表的主键发生了变化,就会导致所有引用了该表的数据必须全部修改外键。很多Web应用的数据库并不是强约束(仅仅引用主键但并没有设置外键约束),修改主键会导致数据完整性直接被破坏。

业务字段不可用于主键

所有涉及到业务的字段,无论它看上去是否唯一,都决不能用作主键。例如,用户表的Email字段是唯一的,但是,如果用它作主键,就会导致其他表到处引用Email字段,从而泄露用户信息。

此外,修改Email实际上是一个业务操作,这个操作就直接违反了上一条原则。

那么,主键应该使用哪个字段呢?

主键必须使用单独的,完全没有业务含义的字段,也就是主键本身除了唯一标识和不可修改这两个责任外,主键没有任何业务含义。

类似的,看上去唯一的用户名、身份证号等,也不能用作主键。对这些唯一字段,应该加上unique索引约束。

主键应该用什么类型

主键应该使用整数还是字符串?(用浮点数的请自觉充值智商)

我强烈建议使用字符串。

为什么?

我们先看使用整数的问题。

使用整数有两个选择:数据库自增和自己生成。

自己生成其实也是自增,无非就是把上次使用的值保存到某个地方,下次使用的时候继续自增。常见的做法是用一个单独的表存储上次用的最大值。这种方式实现复杂,可靠性低,还不如数据库自增。

数据库自增最大的问题还不在于数据库单点造成无法水平切分,因为绝大部分公司还撑不到业务需要分库的情况就倒闭了。

自增主键最大的问题是把公司业务的关键运营数据完全暴露给了竞争对手和VC。举个例子,用户表采用自增主键,只需要每周一早上去注册一个用户,把上周注册的ID和本周注册的ID一比,立刻就知道了该公司一周的新增用户数量。如果网站声称新增了10万用户,但ID却只增加了1千,就只能呵呵了。

因为主键的本质是保证唯一记录,并不要求主键是连续的。实际上不连续的更好,这样既避免了运营数据泄露,也给黑客预测ID制造了障碍,具有更高的安全性。

用字符串主键就不存在这个问题。如果我们用一个UUID作为主键,即varchar(32),除了占用的存储空间较多外,字符串主键具有不可预测性。

有人觉得UUID完全随机,主键本身没有按时间递增,不利于直接主键排序。其实解决这个问题很简单。

方法一,直接用时间戳+UUID构造一个主键,时间戳注意补0,这样生成的主键就是按时间排序的。这个方法简单粗暴,缺点是主键更长了。

方法二,自定义一个算法,时间戳放高位,序列号放低位,还可以保留机器位,然后用base32编码,可以把长度控制在20个字符内。

有人会问,根据方法二,构造包含时间戳和序列号的64位整数作为主键是否可行?

理论上来说是可行的,因为时间戳0xffffffff可以表示到2100年。但是剩下的位不是ffffffff而是只有fffff,如果给机器分配ff作为标识,那么每秒只能最多生成0xfff+1=4096个主键,对一些大型应用不太够用。

为啥64位整数除掉时间戳只能用后面的fffff位呢?这是因为JavaScript的Number类型是56位精度,它能表示的最大整数是0x1fffffffffffff,而我们迟早会用REST跟JavaScript打交道,所以要把64位整数的范围限制在0x1fffffffffffff内,否则与JavaScript交互就会出错。

虽然理论上64位整数做时间戳+序列号的主键是没问题的,但是实践中是没法绕开与JavaScript交互的,综合考虑,字符串主键最可靠。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • springboot整合mongodb实现CRUD以及分页条件查询

    5.确认数据库对应集合的_class列(没有则创建改列)包路径是否与上面实体类路径一致,没有则更新命令如下

    用户1212940
  • 常用Lambda表达式实例

    集合操作 从集合中过滤出某一个字段存入到新集合 // 从商品集合中过滤出商品类目id为一个新 List<Integer>集合 List<Integer> c...

    用户1212940
  • 一篇文章理解AB测试和灰度发布

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明...

    用户1212940
  • MySQL 之主键

    小编在查询相关的学习资料的同时,偶尔会遇到关于主键的信息,也是一直没有很清晰的认知,所以,这篇学习笔记,主要是和大家一起分享有关主键的知识。

    DataScience
  • 数据结构(ER数据库)设计规范 原

    表命名的规则分为3个层级,层级之间通过_分割,例如b_r_identity、d_l_identity。规约为:

    随风溜达的向日葵
  • mysql 中select for update 锁表的范围备注

      实例:指定了锁定id=1的行且数据存在①,在更新1时lock wait超时②,但是更新id不为1的项目时可以直接更新③,释放锁后④,可以任意更新⑤

    肖哥哥
  • 数据库MySQL-实体之间的关系

    答:在字段数量很多情况下,数据量也就很大,每次查询都需要检索大量数据,这样效率低下。我们可以将所有字段分成两个部分,“常用字段”和“不常用字段”,这样对大部分查...

    cwl_java
  • SQL反模式学习笔记22 伪键洁癖,整理数据

    在插入新行时,通过遍历表,找到的第一个未分配的主键编号分配给新行,来代替原来自动分配的伪主键机制。

    张传宁老师
  • 向mysql数据库中插入数据时显示“Duplicate entry '1′ for key ‘PRIMARY' ”错误

    错误情况如题,出现这个错误的原因十分简单: 很明显,这是主键的问题。 在一张数据表中是不能同时出现多个相同主键的数据的 这就是错误的原因,解决的方法...

    roobtyan
  • 面试官:MySQL表设计要注意什么?

    其实上面这些问题,我最早想法是,每个问题都可以啰嗦出一篇文章。后来由于良心发现,烟哥就决定用一篇文章将这些问题都讲明白。 当然,我给的回答可能并非标准答案,毕竟...

    Java3y

扫码关注云+社区

领取腾讯云代金券