聊一个实际问题,淘宝的数据库的主键是如何设计的呢?

某些错误的离谱的答案还在网上流传着。其中一个明显的错误就是关于mysql的主键设计。

大部分人的回答如此自信就是:用8个自己的bigint做主键,而不是用int。

这样的回答只是站在数据库这一层而没有从业务的角度思考。主键就是一个自增的ID吗?

目录

自增ID的问题

1.可靠性不高

2.安全性不高

3.性能差

4.交互多

5.局部唯一性

业务字段做主键

选择卡号

选择会员电话或身份证号

某宝的主键设计

推荐的主键设计

UUID的特点

认识UUID

改造UUID

有序uuid性能测试


自增ID的问题

自增id作为主键,简单易懂,几乎所有的数据库都支持自增类型,只是实现上各自有所不同。自增id除了简单,其他都是缺点,总体来看存在以下几方面的问题

1.可靠性不高

存在自增id的回溯问题,这个问题直到mysql8.0才修复。

2.安全性不高

对外暴露的接口可以非常容易猜到对应的信息。比如/user/1/这样的接口,可以非常容易猜测用户id的值为多少,总用户数量有多少,也可以非常容易的通过接口进行数据的爬取。

3.性能差

自增id的性能较差,需要在数据库服务器端生成

4.交互多

业务还需要额外执行一次类似last_insert_id()的函数才能直到刚才插入的自增值,这需要多一次的网络交互。在海量并发的系统中,多1条sql就多一次性能上的开销。

5.局部唯一性

最重要的一点自增id是局部唯一性,只在当前数据库中唯一,而不是全局唯一,在任意服务器间都是唯一的。对于目前分布式系统来说,这简直是噩梦。

业务字段做主键

为了能够唯一地标识一个会员的信息,需要为会员信息表设置一个主键。那么怎么为这个表设置主键才能达到我们的目的呢?这里我们考虑业务字段做主键。

比如会员表中的字段有 cardno卡号 、membername名称、phone电话、memberpid身份证号、address地址、sex性别、birthday生日。在表中哪个字段比较合适呢?

选择卡号

会员卡号cardno看起来比较合适,因为会员卡号不能为空且唯一标识一条会员记录。但实际情况是,会员卡可能存在重复使用的情况。比如张三因为工作变动办理了原来的地址,不再到商家消费了退了会员卡,于是张三就不再是会员了。但是,商家不想让这个会员卡空着,就把这个卡发给了王五。

从系统角度看这个变化只是修该了会员信息表中的这个卡号的这个会员信息,并不影响到数据的一致性。也就是说,修改会员信息,系统的各个模块都会获取到修改后的会员信息。因此,从信息系统层面上看是没有问题的。

但是从使用系统的业务层面来看就有很大问题了,会对商家造成影响。

比如,我们有一个销售流水表,记录了所有销售流水明细。张三在商家买了一本书,那么系统中就有了张三的流水记录。比如

如果会员卡又发给了王五,我们会更改会员信息表。导致查询时候会显示王五买了书。这显然是错误的,结论是不能将会员卡作为主键。

选择会员电话或身份证号

会员电话可以做主键吗?不可以。在实际操作中,手机号也存在被运营商收回,重新发给别人使用的情况。

那么身份证号呢?好像可以,因为其不会重复且与人一一对应。可问题是身份证属于个人隐私,顾客不一定愿意提供给你。要是强制要求登记身份证号会把顾客赶跑。其实电话也一样。这就是我们在设计会员信息表的时候,允许身份证号和电话都为空的原因。

所以,建议尽量不要用跟业务有关的字段做主键。毕竟作为项目设计的人员我们谁也无法预测在项目的整个生命周期中哪个业务字段会因为项目的业务需求而重复或者重用之类的情况出现。

经验:刚开始使用mysql时,很多人都容易犯的错误就是喜欢用业务字段做主键,想当然的认为了解业务需求,但实际情况往往出乎意料,而更改主键设置的成本非常高。

某宝的主键设计

在某宝的电商业务中订单服务是一个核心业务。那么它的主键是怎么设计的呢?

从上图可以看出,订单号不是自增id。订单号是19位长度且后6位都是一样的,都是08113.前14位部分是单调递增的。大胆猜测其订单号应该是

订单ID=时间+去重字段+用户ID后6位

这样的设计能做到全局唯一且分布式系统查询极其友好。

推荐的主键设计

非核心业务:对应表的主键自增ID,如告警,日志,监控等信息。

核心业务:主键设计至少应该是全局唯一且单调递增。全局唯一保证在各个系统之间都是唯一的,单调递增是希望插入时不影响数据库性能。

这里推荐最简单的一种设计UUID

UUID的特点

全部唯一,占用36字节,数据无序,插入性能差

认识UUID

①为什么uuid是全局唯一

②为什么uuid占用36个字节

③为什么uuid是无序的

mysql数据库的uuid组成如下图所示

为什么uuid是全局唯一的?

在uuid中间部分占用60位,存储的类似timestamp的时间戳。但实际上其时间精度比时间戳要高。

时间序列是为了避免时钟被回拨导致产生时间重复的可能性。Mac地址用于全局唯一。

为什么占用36个字节?

uuid根据字符串进行存储,设计时还有无用的“-”字符串,因此总共需要36个字节。

为什么uuid是随机无序的?

因为uuid的设计中,将时间地位放在最前面,而这部分的数据是一直变化的,且是无序的。

改造UUID

若将时间高低位互换,则时间就是单调递增的了,也就变得单调递增了。mysql8.0可以更换时间地位和高位的存储方式,这样uuid就是有序的uuid了。

mysql8.0还解决了uuid存在的占用空间的问题,去除了uuid字符串中无用的“-”字符串,并且将字符串用二进制类型保存,这样存储空间降低为了16字节。

可以通过mysql8.0提供的uuid_to_bin函数实现上述功能,同样的,mysql也提供了bin_to_uuid函数进行转化:

通过函数uuid_to_bin(@uuid,true)将uuid转化为有序uuid了。全局唯一+单调递增。

有序uuid性能测试

下面截图是我在别人测试的地方截取的

在当今的互联网环境中,非常不推荐自增id作为数据库主键。更推荐类似有序uuid全局唯一的实现。

另外在真实的业务系统中,主键还可以进入业务和系统属性,如用户的尾号,机房的信息等。这样的主键设计就更为考研架构师的水平了。

mysql数据库主键如何设计相关推荐

  1. MySql数据库主键外键与数据库设计

    MySql数据库主键外键与数据库设计 首先要指出的: 列.字段.属性是一个概念 行.记录.元组是一个概念 MySQL数据库CONSTRAINT约束:非空约束,唯一约束,主键约束,外键约束 show c ...

  2. 数据库主键的设计和思考

    1. 何谓数据库主键 数据库主键是指表中一个列或列的组合,其值能唯一地标识表中的每一行.这样的一列或多列称为表的主键,通过它可强制表的实体完整性.当创建或更改表时可通过定义 PRIMARY KEY约束 ...

  3. 更改mysql数据库主键自增时报错ALTER TABLE causes auto_increment resequencing, resulting in duplicate entry '1

    当我用SQLyog尝试修改已有记录的mysql数据表的主键为自动增长时,报出以下错误 ALTER TABLE causes auto_increment resequencing, resulting ...

  4. mysql数据库主键的思考

    自增主键优点 数据库AUTO_INCREMENT,innodb的索引特性导致了自增id做主键是效率最好的,而且是增量增长,按顺序存放,对于检索非常有利: 用户表user百万记录,主键为UUID和主键为 ...

  5. 数据库主键如何设计?

    文章目录 1. 自增ID的问题 2. 淘宝的主键设计 3. 推荐的主键设计 3.1 分类 3.2 UUID(最简单的一种主键设计) 3.1.1 UUID的特点 3.1.2 UUID的组成 3.1.3 ...

  6. mysql数据库主键PK_数据库主键代码pk

    表格存储数据模型和查询操作 摘要 本篇文章主要会详细聊一下表格存储的查询操作,以及如何根据业务的需求来设计表结构以支持特定条件的查询. 在理解查询操作之前,会简单描述一下表格存储的数据模型,以加深对查 ...

  7. mysql数据库主键自增6_mysql数据库,主键自增主键不连续

    解决方法:就是 删除数据之后,执行一下语句: 每次删除记录后,执行: ALTER table tableName AUTO_INCREMENT=1; 后面插入数据就是连续的了. MySQL复制表结构 ...

  8. mysql数据库主键重复

    2019独角兽企业重金招聘Python工程师标准>>> 修改表中字段data_name,提示报错,原因:该字段为主键,不能重复 解决方法:将第一个更换名字,然后第二个换成第一个之前的 ...

  9. mysql主键标识_实体标识与数据库主键

    今天,我们将讨论DDD意义上的标识与数据库主键之间的区别. 我们经常将两者混合在一起,但它们真的是一回事吗? 实体标识 在DDD的背景下,标识是实体固有的东西. 只有实体拥有它; 它是用于区别于所有其 ...

最新文章

  1. Arm十年最大更新:V9架构正式发布
  2. turbolinux mysql 5.0 cluste,Debian -- 新聞 -- Debian GNU/Linux 5.0 發布
  3. Hadoop-2.2.0学习之一Hadoop-2.2.0变化简介
  4. nacos如何做配置中心?自带自动刷新配置功能?这一篇文章让你明明白白!
  5. C语言两班平均成绩,用C语言编程平均分数
  6. as3是js和java_AS3与JS进行交互(一)
  7. 我的网页设计(网页页面制作二)
  8. mysql having ct_mysql中where和having子句的区别和具体用法
  9. 安卓开发文档_鸿蒙2.0,HarmonyOS开发体验!
  10. 【nginx】关于Nginx的一些优化(突破十万并发)
  11. 发现VB6中SAX的乐趣[转]
  12. GitLab5.3修改项目仓库名称后wiki不能访问
  13. python 拼音输入法_ubuntu上安装 ibus Google拼音输入法(修改一点错误)
  14. SSM流程及核心原理
  15. 硅谷课堂 13_公众号点播课程和直播管理
  16. wegt安装tomcat镜像use --no-check-certificate
  17. FPGA芯片内两种存储器资源
  18. IDEA 中 project窗口,不显示项目工程目录,解决方法
  19. ISCC 2018 PWN WriteUp
  20. 世界上最好用的纯前端模板【没有之一】,AceTemplate高调亮相。

热门文章

  1. UVM学习笔记—前门访问/后门访问
  2. 卸载Windows中的集成组件(转)
  3. 普通电脑如何实现网吧锁屏?让你的电脑实现网吧挂机锁功能的方法
  4. 有规律的坚持写文章有多难?
  5. DARPA互联网协议的设计哲学
  6. GNU Radio基础操作与简易MATLAB分析
  7. 计算机管理mmc无法,WinXP系统提示MMC无法创建管理单元如何解决?
  8. PNG图像文件格式结构分析
  9. auto.exe F0D78D11.DLL F77B20D5.EXE专杀
  10. 让人秒懂的Redis的事件处理机制