写点什么

为什么总是需要无意义的 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:33778

评论

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

draw.io-取代visio的流程图绘制工具

Rice嵌入式开发技术分享

chrome vscode 写文章神器 draw.io

ARTS打卡Week 02

teoking

objective-c LeetCode WebRTC

如何用CSS选择符(数字开头) 杀死队友

德育处主任

Java html css3 大前端 Web

John 易筋 ARTS打卡Week 02

John(易筋)

ARTS 打卡计划 ARTS活动 arts

ARTS Week1

姜海天

B端产品经理养成记(1):业务场景

涛哥 数字产品和业务架构

产品经理 需求 产品开发

【openlayers】在vue中使用ol

德育处主任

Java html Vue 地图 openlayers

ARTS week 2

刘昱

你会写测试用例吗

Apache DolphinScheduler新特性与Roadmap路线

代立冬

大数据 数据中台 工作流调度 海豚调度 数据湖调度

转行程序员浅谈进程间的socket通信

WB

Linux 程序员 socket

钢铁侠马斯克之仰望星空

池建强

创业 马斯克 Space X

Element-UI实战系列:Table+Pagination组件实现已选和全选功能

AR7

Vue 大前端 Element

愚蠢写作术(1):怎么让你的标题被读者忽视

史方远

个人成长 写作

【5月】本月读书学到了什么

Neco.W

创业 读书感悟 阅读量

【ARTS打卡】Week01

Rex

学习

ARTS week2

紫枫

ARTS 打卡计划

使用Kotlin语言初始化数组

mengxn

数组 kotlin 初始化

RocketMQ - 高可用设计

Java收录阁

RocketMQ

不吹不黑!GitHub 上帮助人们学习编码的 12 个资源,错过血亏...

JackTian

GitHub 学习 开源 程序员 编码

游戏夜读 | 关于构图的困难

game1night

MAC OS 下 HomeBrew 使用

耳东@Erdong

macos brew homebrew

做PO难,难于上青天

刘华Kenneth

敏捷 产品经理 决策 PO

Kafka系列9:面试题是否有必要深入了解其背后的原理?我觉得应该刨根究底(上)

z小赵

大数据 kafka 实时计算

时代在变,产品运营能力很重要

punkboy

程序员 程序人生 产品经理 产品推荐 程序媛

RocketMQ - 如何实现事务消息

Java收录阁

RocketMQ

工厂模式(四)泛型工厂之MyBatis Mapper代理

LSJ

Java 设计模式 泛型 工厂注册中心

写博客的那些事

shengjk1

B端产品经理养成记(2):用户故事

涛哥 数字产品和业务架构

产品经理 需求 产品开发

ARTS打卡第一周5.25-5.31

我笔盒呢

工作 vs 生活

shengjk1

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