Mysql 数据库需不需要主键 Id?

2020-03-19 10:20:04 +08:00
 wangbenjun5

最近接触了一个项目,里面的数据库设计让我“大开眼界”,很多表没有主键 id,取而代之的是复合主键,也就是几个字段同时作为主键,比如说: A 表:

name
age
addr

主键是 name+age

B 表:

name
age
money
something
xxx

主键也是 name+age

实际上 B 表是 A 表的附属表,也就说是补充 A 表的内容,或者是关联到其它表

恕我愚昧,不知道这种设计有什么好处,我觉得用 id 当主键更简介方便,便于修改数据,比如上面的例子有时候还有修改 age 的需求,这时候就非常麻烦了,难道这是反范式化设计,故意做的数据冗余,我不敢苟同,咱也不敢问,咱也不敢说。

各位大佬,求打醒!!!

13592 次点击
所在节点    MySQL
99 条回复
fx
2020-03-19 10:42:40 +08:00
大学生水平的人写的
Vegetable
2020-03-19 10:43:13 +08:00
非自增的主键插入时一定要检查是否唯一,这个损失就洗不了。
wangbenjun5
2020-03-19 10:43:55 +08:00
@ytmsdy 唉,反正现在数据库里面大部分都是这种设计,也有极少部分是有 id 的,估计不是一个人写的,木已成舟
FragmentLs
2020-03-19 10:44:22 +08:00
不说 id,看到 age 就被吓哭了...这一定是被外在和内在因素影响深思熟虑挣扎出来的结果!
wangbenjun5
2020-03-19 10:44:55 +08:00
@loading
@littlewing 只是举个例子,实际不是这张表
hbolive
2020-03-19 10:45:13 +08:00
一般情况下,表是需要一个 ID 字段标识唯一一条数据记录,这个 ID 不一定跟业务发生关联。
fancy111
2020-03-19 10:47:47 +08:00
@littlewing 自增主键对于 MySQL InnoDB 来说,插入性能和空间利用率更高? 你想当然是吗?谁告诉你自增插入性能高?
更新的话我已经说了,这个表肯定不会这样设计。
fancy111
2020-03-19 10:49:51 +08:00
@littleylv 看清楚我的回答,我并没说这个设计就好。数据库的设计本身就是跟项目结合的,哪个性能好选哪个。搞编程的思维都这么死板的话没啥发展前途。
heyjei
2020-03-19 10:51:05 +08:00
你举得例子叫业务主键(只不过你举得例子不好),自增的 ID 是逻辑主键。

做数仓的一般比较倾向于业务主键,应用系统的喜欢逻辑主键。

设计良好的使用业务主键的系统,看上去真舒服!!!
tabris17
2020-03-19 10:53:31 +08:00
@fancy111 自增主键保证 insert 数据能够顺序写入,这样说没什么问题
wangbenjun5
2020-03-19 10:54:50 +08:00
@heyjei 额,怎么说呢,这个业务确实不是什么 Web 应用,偏内部系统,但是有一部分内容需要白屏化,所以涉及到一些增删改查,没有 id 主键确实头疼
lsls931011
2020-03-19 10:54:53 +08:00
不用主键 ID, 会让 mysql 插入的时候进行随机写入排序,毕竟 B-tree 索引是按顺序存储的, 而且以后也不利于运用到聚簇索引和覆盖索引啊。 除非你用的不是 innodb 引擎,而是其他的😓
heyjei
2020-03-19 11:00:44 +08:00
@wangbenjun5 你可以试着去接受业务主键,特别是这种业务复杂的内部系统,用着用着就会发现真香。。

自增的 ID,CRUD 方便了,但是要想理解业务,就蒙圈了,而且还特容易产生脏数据。
ZSeptember
2020-03-19 11:03:26 +08:00
主键还是要的。
smallpython
2020-03-19 11:06:48 +08:00
看起来像是把 mysql 当成 mongo 在用了, 这样做的好处就是查询快吧, 比如我查 B 表的时候就直接可以知道对应的 name 而不是要和 A 表关联一次才知道 name
tant
2020-03-19 11:11:43 +08:00
mysql 的 innodb 是必须的,其他的数据库可以建议,我个人只对 MySQL 比较了解,供你参考:
InnoDB 引擎表是基于 B+树的索引组织表;
InnoDB 会选择主键作为聚集索引、如果没有显式定义主键,则 InnoDB 会选择第一个不包含有 NULL 值的唯一索引作为主键索引;
数据记录本身被存于主索引(一颗 B+Tree )的叶子节点上。这就要求同一个叶子节点内(大小为一个内存页或磁盘页)的各条数据记录按主键顺序存放,因此每当有一条新的记录插入时,MySQL 会根据其主键将其插入适当的节点和位置,如果页面达到装载因子( InnoDB 默认为 15/16 ),则开辟一个新的页(节点)

如果表使用自增主键,那么每次插入新的记录,记录就会顺序添加到当前索引节点的后续位置,当一页写满,就会自动开辟一个新的页;

如果使用非自增主键(如果身份证号或学号等),由于每次插入主键的值近似于随机,因此每次新纪录都要被插到现有索引页得中间某个位置,此时 MySQL 不得不为了将新记录插到合适位置而移动数据,甚至目标页面可能已经被回写到磁盘上而从缓存中清掉,此时又要从磁盘上读回来,这增加了很多开销,同时频繁的移动、分页操作造成了大量的碎片,得到了不够紧凑的索引结构,后续不得不通过 OPTIMIZE TABLE 来重建表并优化填充页面;

以上是《高性能 MySQL 》中的原话

大概意思就是:InnoDB 表的数据写入顺序能和 B+树索引的叶子节点顺序一致的话,这时候存取效率是最高的,而主键可能不是递增关系时(例如字符串、UUID、多字段联合唯一索引的情况),该表的存取效率就会比较差。

所以一般 mysql 又用 innodb 的情况下一律加上自增 id,如果你说的两个表,B 表是 A 表的附属表,B 表的 ID 可以不设置自增,直接存 A 表的 ID 作为主键即可
l3n641
2020-03-19 11:12:02 +08:00
大部分的情况下是需要的 id 作为 primary key 的.如果表是 inodb 引擎的话,他是通过主键聚集数据的, 通过联合索引做主键,会增加 mysql 插入数据的时间,而且索引更大.而且会导致页分裂和碎片话.
Jooooooooo
2020-03-19 11:13:29 +08:00
@fancy111 瞎搞, 扩展性什么的完全不考虑. 之后要往外扔数据也用 name+age 当实体标记别人会用奇怪的眼神看你的
arjen
2020-03-19 11:14:43 +08:00
大学生都不这么写,别黑我们大学生...
zunceng
2020-03-19 11:14:47 +08:00
遵循数据库范式到哪个程度
业务简单的话都行 如果业务比较复杂设计的人出来谢罪

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://tanronggui.xyz/t/654133

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX