三恒移俗在本文中提出一种全局唯一ID的增强规范,用于补齐UUID的短板,实现可排序的UUID,在兼容UUID的全局唯一性的同时,支持排序,检索,简化数据管理中的逻辑和运维。

1 前言

在数据库规划和管理中,有一个比较传统且简便的 主键(Primary Key)做法,即使用数字自增字段作为主键。 在早期,几乎是全部都这么用,现在,如果是简单的系统,基本上也大部分这么用。 直到一些分布式系统的出现,才对主键有了全局唯一性的要求,出现了一些用UUID作为主键的设计。

自增数字字段作为主键,确实非常的方便,无需维护,并且保留自然的顺序,可排序可检索,还非常直观。 但是他也存在一个让人非常难受的弊端,就是他是操作完成后才知道具体数值的。 也就是说,你要新增一条数据,你是不知道这条数据的id的,直到你把它插入到数据库,完成之后,才能获取到他的id。

在CRUD中,除了C之外其他的操作都是很自然的,现有数据,然后执行操作。 只有新增(C),是先准备数据,然后执行操作,然后获取操作结果,得到id。 这是一种非常反直觉思维的逻辑。 并且在这个过程中包含了很多的隐患。 虽然现代数据库基本都支持事物操作,绝大部分的封装库也都在底层做好了这部分的封装。但是在实际使用中,仍是很别扭,最主要还卡流程的存在。

这种危险的,后知后觉的操作设计,不仅仅是用起来不方便,很不舒服,实际上还会有很多其他的问题。比如说卡流程,你必须完成插入操作后,要获得有效的id,才能走流程的下一步。 简单的数据库内部操作还可以用事务来处理。但是很多的业务是跨系统的,比如通过API调用。这种逻辑就对整个流程造成了很大的困扰。

另外一个问题是无法批量处理(除非不在乎id和后续的跟近)。 一个典型的例子:比方说有一批的通讯录,或是材料清单,几百到几万条记录。 一次性倒入数据库之后,原始通讯录,材料清单做了些更新,可能是删除了过期的数据,也可能是修正一批纰漏。如果用数字自增id,那么这就是一种灾难性的后果。你无法将现有的数据和数据库里的记录对应起来,唯一能做的就是清空数据库,全部重新倒入。 而如果数据库已经开始运行的话,还需要连环检查相关的外部依赖记录,全部清空,完整的去除所有的历史业务记录,才能够再次导入,重新再来。

为了解决上面的问题,就必须引入额外的固定的,唯一的主键。虽然这种选择比较多,但是对于一个通用的方案来说,最简单的就是用UUID来做主键了。

用uuid做主键,有一个很大的好处,就在可以事先生产id,而不是事后在获取id。 这样子就可以完美解决前面提到的数字自增id的缺陷和不足了。对于批量管理来说也特别的简单,事先生产的uuid在全阶段都可以用作主键来建立关联业务关系。

uuid用作主键,CRUD的流程也变得一致,且简单了。也不会卡流程。

但是uuid也不是万金油,他解决了自增id带来的麻烦,同时引入了新的麻烦。 UUID长这样子:b9c83eaa-7819-11f1-af19-7cd30a92197a。 从他的样子我们就可以看出他几乎就是一些随机数字,除了唯一性之外没有任何有价值的信息。 相对于数字自增id来说,比如这些: 99, 3, 12345。 我们可以非常直白的知道他们的顺序,大概的位置,索引后还可以快速的找到,甚至凭借他的顺序位置,大致的估摸他的生成时间。 数字不仅可读,可理解,还可以顺口告诉其他人(用于沟通交流)。这些都是uuid的缺陷所在。

当然,由此也有一种简单的妥协办法,就是同时使用两种机制,数字自增字段作为主要的主键,uuid作为辅助的唯一键,两个互相搭配组合来用。这种方式简单的看是可以用上两种不同机制各自的优点,但在实践中,仍需要明确的规范和定义每个主键各自的应用场景,且需要做全局的一致性强约束。避免混淆是需要考虑的重中之重。

2 有序的UUID

既然数字自增主键和uuid主键各自都有很强的优点,又有很强的缺陷和不足。简单的同时都用又容易混淆概念。 那是不是有一种方法可以兼具 数字自增id和uuid的优点呢? 三恒移俗在实践中总结出一个新的规范,简单的结合uuid和数字自增id的优点,形成一个有序的全局唯一id. 机制如下:

  1. 生产一个唯一的全局ID,uuid=b9c83eaa-7819-11f1-af19-7cd30a92197a,有效信息32位。 特殊情况下可以扩展到64位,128位。但是处于效率考虑,一般的32位就足够了。
  2. 生产一个包含本地信息的还有顺序的信息:最长可以到16位长度(32为情况下),基本原则不应超过uuid的一半长度。 如最简单的情况下,直接用时间戳:201808042050
  3. 用包含顺序信息的部分,替换uuid的前半部分,形成sequuid. 如: 20180804205039ee1719b1662b6b70e7

好了,现在我们获得了一个SeqUUID, 他包含数字自增ID的优点:可排序,可索引,可读可理解,能大致估计位置和时间, 同时包含uuid的优点:在任何场合,任何环境下,全局唯一; 还能预先生成。

3 适用场景

SeqUUID 几乎可以替代任何使用数字自增id的场合和使用uuid的场合,并且立即获得对应的优点。

唯一的不一致在与数字自增id是数字类型的,而SeqUUID是字符串类型的。 这对于旧系统的直接改造可能存在一些兼容问题,需要升级代码。

但是对于小的系统,以及计划新建的系统,以及原来基于uuid的系统,都可以直接进行替换改进。