幂等与全局ID

什么是幂等?我们这里所说的幂等是指非主观因素造成了多次相同的请求,而做幂等设计要求这些相同的请求无论是做一次还是多次不产生副作用。即 f...f(f(x))=f(x)。产生多次请求原因有很多比如前端没有做防抖的多次点击提交,MQ消息投递失败重试等。

HTTP的幂等

HTTP GET 用于查询,不应有副作用,所以是幂等的。

HTTP HEAD 方法本质上与 GET 一样,知识不反悔body部分,所以HEAD也是幂等的。

HTTP OPTION 通常用于获取改URL支持的请求方法,没有副作用也是幂等的。

HTTP DELETE 方法用于删除对应资源,有副作用,但一次删除与多次删除效果是一样的,所以也满足幂等特性。

HTTP PUT 用于创建或更新URL对应ID的某个资源,有副作用。但是一次更新与多次更新后的结果是一样的,所以也满足幂等特征。

HTTP POST 用于创建资源,但是并没有选中某个具体资源,多次操作可能造成资源重复创建等问题,所以不满足幂等性质。因此我们可以将POST请求添加一个全局ID参数来将POST转化为PUT操作来获取幂等性质。

全局ID

说到分布式全局ID我们首先想到的是类似MongoDB Sharding使用UUID,但是UUID首先是太长了,性能不好,另外要想做到自增排序基本是不可能的。

使用数据库

如果每次生成的ID都从统一数据库中读取显然是能够保证全局唯一且自增的,但这显然带来了单点性能问题与风险。在分布式系统中我们可以用Mysql的auto_increment_incrementauto_increment_offset或者redis的INCRINCRBY为同一组数据库设置不同的初始值和步长来获取ID。比如三个Mysql实力的补偿全部设置为三,各自的初始值全部设置为1,2,3即可维护全局唯一的ID。

如果每次查数据库获取ID仍然会对数据库产生较大压力,因此我们可以在获取ID的Service中每次取出ID事默认做一个生成ID的缓存,如一次取出1000个,那么1000次ID获取操作才会产生一次数据库请求,大大降低了数据库压力。

Snowflake

Snowflake,雪花算法是由Twitter开源的分布式ID生成算法,以划分命名空间的方式将 64-bit位分割成多个部分,每个部分代表不同的含义。而 Java中64bit的整数是Long类型,所以在 Java 中 SnowFlake 算法生成的 ID 就是 long 来存储的。

  • 第1位占用1bit,其值始终是0,可看做是符号位不使用。
  • 第2位开始的41位是时间戳,41-bit位可表示2^41个数,每个数代表毫秒,那么雪花算法可用的时间年限是(1L<<41)/(1000L360024*365)=69 年的时间。
  • 中间的10-bit位可表示机器数,即2^10 = 1024台机器,但是一般情况下我们不会部署这么台机器。如果我们对IDC(互联网数据中心)有需求,还可以将 10-bit 分 5-bit 给 IDC,分5-bit给工作机器。这样就可以表示32个IDC,每个IDC下可以有32台机器,具体的划分可以根据自身需求定义。
  • 最后12-bit位是自增序列,可表示2^12 = 4096个数。

这样的划分之后相当于在一毫秒一个数据中心的一台机器上可产生4096个有序的不重复的ID。但是我们 IDC 和机器数肯定不止一个,所以毫秒内能生成的有序ID数是翻倍的。

成熟的分布式ID方案

百度UidGenerator

美团Leaf