写点什么

为什么总是需要无意义的 ID ?(二)

  • 2019-12-27
  • 本文字数:2139 字

    阅读完需:约 7 分钟

为什么总是需要无意义的 ID ?(二)

唯一性

消息队列往往需要对外保证服务质量,可能需要提供包括最多一次、最少一次和正好一次在内的服务质量,由于网络可能存在超时等不确定性,当我们想要实现正好一次时,就一定需要一种机制能够在接收方识别发送方发出的重复消息,在这时就需要使用唯一的标识符来解决这个问题:



我们在之前的系列文章 为什么 TCP 建立连接需要三次握手 提到的 TCP 连接中的序列号也是一个唯一的标识符,它能够帮助我们判断对数据进行去重,保证应用层的协议不会收到异常的数据包,这些场景都需要用到标识符的唯一性,唯一性为我们带来的就是精确识别对象的能力。


在与网络相关的场景下,使用唯一 ID 的例子非常普遍,假如我们想通过支付宝或者微信的 API 向其他人发起一笔转账,如果这次请求发生了超时,那么我的这笔转账请求到底有没有被处理呢?



当前的节点对于这笔转账请求的结果是不知道的,如果这时重新请求可能会发生二次转账这类严重的问题,但是如果不重新请求,转账可能没有生效,这时如果我们引入一个无意义的 ID 来帮助接收方识别请求的唯一性就能很好地解决这个问题:


  1. 如果接收方已经成功处理 ID 对应的请求,那么就直接返回;

  2. 如果接收方没有处理 ID 对应的请求,就正常进行处理;


为了保证请求的唯一性,根据业务对于唯一性要求的强弱,我们需要在接收方对 ID 进行存储,可以在内存中,也可以在数据库中,最重要的是唯一的 ID 为接收方提供了判断重复的重要依据。


除了在不稳定的网络中,数据库也包含 ID 标识符这一概念,我们在数据库中往往叫做主键,它在一般情况下都是一个递增的唯一整数,绝大多数的表都会使用 ID 作为表的主键来保证数据的唯一性,当我们想要对数据进行增删改查等操作时,使用主键 ID 查询数据也是性能最优并且最不容易出现问题的做法。

无意义

无意义的意思其实就是 ID 中不应该包含任何与具体场景或者业务相关的内容,包含这些内容并不是不可以,只是一旦出现这些内容,要么 ID 重复的可能性会增加,这很可能对我们的业务逻辑造成比较严重的影响,以我们的身份证号为例,它的 18 位数字(或符号)大多都是有意义的。



这 18 位数字中的前 6 位表示的是地区,也就是省份、城市和区县,随后的 8 位表示的是出生年月日,接下来的 3 位才同时表示 ID 和性别,最后 1 位用于做校验码防止出现身份证号输错的情况。用上述图中的黄色部分中有一半的数字是用来表示出生的男性,另一半表示出生的女性,所以如果同一个地区的同一天,同时出生了 501 位男性或者女性就会导致潜在的重复问题。


上面谈到的问题其实也是我们在各种业务场景中经常能够遇到的问题,18 位的数字中真正用于表示序列的 ID 其实只有 1000 的一半,如果 18 位数都是无意义的,那它们可以表示 10 亿亿个人,但是一旦在 ID 中引入了业务上的具体信息,就增加了冲突的可能性。


业务记录上主键的长度往往都是固定的,大多数业务的主键都会使用整数,它的上限一般就是 2^64,如果这些位数都用来表示记录的 ID,那么在有生之年基本上是不可能被使用完的,但是一旦我们将业务信息加入 ID,就会让原本无意义的 ID 变得有意义从而影响它的唯一性。


另一个比较类似的例子其实是分布式的 ID 生成器,Snowflake 算法会为 64 个比特的整数赋予不同的信息:


范围长度作用
0-01不使用
1-4141毫秒级时间戳
42-465数据中心标识符
47-515机器标识符
52-6312序列号


从这个设计来看,我们的假设其实是一台机器上一毫秒最多只能生成 4096 个 ID,一旦超过了这个这个数量就有可能导致 ID 冲突或者乱序,从而失去其唯一性;这个算法中涉及的时间戳、数据中心标识符、机器标识符都没有办法解决唯一性的问题,哪怕这三者完全相等,最终还是有冲突的可能,我们仍然需要使用无其他意义的序列号来保证 ID 的唯一。

总结

其实不难看出,使用无意义 ID 的主要目的就是利用它的唯一性保证对象的标识符不会发生冲突,无意义 ID 的唯一作用就是保证唯一性,这能帮助我们避免业务字段可能存在潜在冲突的可能,这也提示我们想要使用联合字段构成主键时一定要深思熟虑。


如果我们想要在具有唯一性的标识符中加入业务信息,一定要注意这可能会减少用于保证唯一性的『空间』,当然对于一个足够大的空间来说,这其实并没有什么问题;但是类型为 int64 的 ID 中加入业务数据还是需要仔细思考可扩展性以及预留的信息是否足够业务的发展。


到最后,我们还是来看一些比较开放的相关问题,有兴趣的读者可以仔细思考一下下面的问题:


  • 软件工程还有哪些场景利用了 ID 的唯一性?

  • 在日常生活中除了身份证号之外,还有哪些 ID 也有比较高的冲突可能性?


如果对文章中的内容有疑问或者想要了解更多软件工程上一些设计决策背后的原因,可以在博客下面留言,作者会及时回复本文相关的疑问并选择其中合适的主题作为后续的内容。

相关文章


本文转载自 Draveness 技术博客。


原文链接:https://draveness.me/whys-the-design-meaningless-identifier


2019-12-27 11:331172

评论

发布
暂无评论
发现更多内容

Raft探索历程--Part2

老胡爱分享

分布式系统 raft

Git 的远端操作及解析(含思维导图)

多选参数

git GitHub gitlab

小白也有大厂梦,如何从零开始掌握高薪Java工程师必备技能?

无予且行

Java 架构 面试 后端 大厂

有了多线程,为什么还要有协程?

八两

线程 进程 协程 GMP 进程线程区别

GeekPwn 2020少年黑客马拉松大赛即将开启 谁将CARRY全场?

Geek_116789

Java程序员的必修课之Spring理解透彻了吗?不会还咋去面试?

犬来八荒

Java spring 面试 后端 框架

三十张图助你看清红黑树的前世今生

淡蓝色

Java 程序员 数据结构 算法

公司一直用Mybatis的原因原来在这!不得不竖起我的大拇指

小闫

Java mybatis mybatis-config.xml mybatis缓存

区块链系列教程之:比特币的问题

程序那些事

比特币 区块链 智能合约 以太坊

面试官:为什么需要happens-before规则和什么是指令重排序

无予且行

Java 编程 程序员 面试 happens-before

如果张东升是个程序员

程序员生活志

程序员 张东升

依赖倒置原则

John

极客大学架构师训练营

Hexo blog 创建指导手册

想飞的鱼

GitHub Hexo GitHub Pages Blog

【Python】 any() 和 or 区别你真的知道吗?

Leetao

Python 数据结构 Python基础知识

现在面试这么难,背下题就能过的时代一去不复返了

小谈

Java 面试 JVM springboot SpringCloud

碎片化学习行不行

封不羁

道路千万条,安全只三条

石君

安全评估 安全设计

JVM中的双亲委派机制你还没懂吗?

阿文

Java JVMTI JVM 深入理解JVM JVM原理

工业4.0|振动分析能做到预防性维护吗?

清水河路人甲

架构师训练营学习总结

John

极客大学架构师训练营

Java线程池最细的解释,看完后彻底征服面试官

小新

Java 架构 面试 线程 线程池

针对GPU单指令多数据流的编译优化算法

GPU

gpu 编译器 程序语言 if-conversion

从Servlet到Spring Boot

废材姑娘

Java Spring Boot

视读——沟通的艺术,看入人里,看出人外(开篇)

废材姑娘

读书笔记 视觉笔记

架构训练营第五周 - 作业

无心水

极客大学架构师训练营

架构训练营第五周 - 总结

无心水

极客大学架构师训练营

它们为什么这么快:从多进程到多线程再到I/O复用

Ya

多线程 进程 并发

[1.3万字] 玩转前端二进制

阿宝哥

Java 大前端 base64 Blob

重学 Java 设计模式:实战策略模式「模拟多种营销类型优惠券,折扣金额计算策略场景」

小傅哥

Java 设计模式 小傅哥 重构 代码优化

刚去面试现场聊了一个多小时的Redis ,悄悄分享给大家!

Java小咖秀

nosql redis 面试

一致性哈希 -- java 实现

lei Shi

为什么总是需要无意义的 ID ?(二)_文化 & 方法_Draveness_InfoQ精选文章