使用基于 Snowflake 算法搭建发号器

为什么使用Snowflake 而不使用UUID?

  • 雪花算法生成的是有序的,便于排序,而UUID不行
  • 另一个原因在于 ID 有序也会提升数据的写入性能
  • UUID 不能作为 ID 的另一个原因是它不具备业务含义

如果请求发号器的 QPS 不高,比如说发号器每毫秒只发一个 ID,就会造成生成 ID 的末位永远是1,那么在分库分表时如果使用 ID 作为分区键就会造成库表分配的不均匀。解决办法主要有两个:

  1. 时间戳不记录毫秒而是记录秒,这样在一个时间区间里可以多发出几个号,避免出现分库分表时数据分配不均。
  2. 生成的序列号的起始号可以做一下随机,这一秒是 21,下一秒是 30,这样就会尽量的均衡了。

个人觉得“微信序列号生成器”的方法更简单,因为:

Snowflake

  1. Snowflake算法是基于二进制的,对于像我这样基础不扎实的理解起来还是比较困难。
  2. Snowflake集群环境下需要保证时钟同步,对运维能力有一定要求;一旦时钟错乱,又刚好是高并发时,会导致大量异常序号。
  3. 如果公司运维能力有限,不适合用Snowflake。
  4. 百度开源的UidGenerator(仅支持单机部署)使用Snowflake算法,单机QPS可达600万。项目说明:https://github.com/baidu/uid-generator/blob/master/README.zh_cn.md
  5. 美团Leaf(分布式ID生成系统),QPS近5万。项目地址:https://tech.meituan.com/2017/04/21/mt-leaf.html

微信序列号生成器

文档地址:https://www.infoq.cn/article/wechat-serial-number-generator-architecture

  1. 递增但不连续的数字序列解决方案。
  2. 设计目标QPS1000万以上。
  3. 通过在递增过程中使用“步长”将每秒磁盘写入由1000万级降至1万。
  4. 设计原理相对于Snowflake更通俗易懂。
  5. 可以使用hash的负载均衡策略组建集群。
  6. 缺点:需要自己实现集群中机器增减后更新负载均衡策略的逻辑。
  7. Java版最简单Demo():使用spring boot搭建一个web工程,使用Controller调用Service实现数字递增

Service类

import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.concurrent.atomic.AtomicLong;

@Service
public class GeneratorService {

    private AtomicLong id;

    @PostConstruct
    private void init(){
        id = new AtomicLong(0);
    }

    public long getId(){
        return id.incrementAndGet();
    }
}

单机测试QPS 3万(测试工程、测试脚本在同一机器运行。) 硬件信息:CPU 2.7 GHz Intel Core i7 | 内存 16 GB 2133 MHz LPDDR3 测试工具:JMeter