美文网首页
发号器设计

发号器设计

作者: NeXt4 | 来源:发表于2020-12-13 18:02 被阅读0次

    需求背景

    高效生成趋势有序的全局唯一ID,兼顾有序性、高性能、可扩展等因素。
    主要应用于一下场景:

    • 信息安全,如果id连续,那么就容易被恶意用户猜测
    • 分库分表需要有一个唯一ID来标识一条数据

    常见方案

    数据库

    使用数据库的自增特性来生成全局唯一递增ID。
    可用性难以保证,数据库常见架构是一主多从,主库挂了就会有很大影响。
    扩展性差,性能也有瓶颈,因为是单点写入,难以扩展。

    单点批量ID生成服务

    发号器采用单点服务以保证唯一性。
    数据库采用双主模式,保证可用性。

    数据库中只存储当前ID的最大值,例如0,ID生成服务假设每次批量拉取6个ID,服务访问数据库,将当前ID的最大值修改为5,这样应用访问ID生成服务索要ID,ID生成服务不需要每次访问数据库,就能依次派发0,1,2,3,4,5这些ID了,当ID发完后,再将ID的最大值修改为11,就能再次派发6,7,8,9,10,11这些ID了,于是数据库的压力就降低到原来的1/6了。

    这个方案的缺点是服务仍然是单点,那解决单点服务的常用可高用方案是增加备份,也可以叫“影子服务”。对外服务的是主服务,有一个影子服务时刻处于备用状态,当主服务挂了的时候影子服务顶上,这个切换过程对调用方透明,常用技术是vip+keepalived。

    UUID

    直接说缺点,无法保证趋势递增,uuid过长,作为主键建立索引查询效率低。
    保持趋势递增,主要是为了写数据库时保证顺序写,避免随机写,提高性能。另外一个就是业务需要。

    如果作为数据库主键,在InnoDB引擎下,UUID的无序性可能会引起数据位置频繁变动,严重影响性能。

    SnowFlake

    Twitter-Snowflake算法产生的背景相当简单,为了满足Twitter每秒上万条消息的请求,每条消息都必须分配一条唯一的id,这些id还需要一些大致的顺序(方便客户端排序),并且在分布式系统中不同机器产生的id必须不同。

    snowflake

    (1)1位:标识部分,在java中由于long的最高位是符号位,正数是0,负数是1,一般生成的ID为正数,所以为0;
    (2)41位:时间戳部分,这个是毫秒级的时间,一般实现上不会存储当前的时间戳,而是时间戳的差值(当前时间-固定的开始时间),这样可以使产生的ID从更小值开始;41位的时间戳可以使用69年,(1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69年;
    (3)10位:节点部分,Twitter实现中使用前5位作为数据中心标识,后5位作为机器标识,这样就可以表示32个IDC,每个IDC下可以有32台机器,可以部署1024个节点;
    (4)12位:序列号部分,支持同一毫秒内同一个节点可以生成4096个ID;

    理论上snowflake方案的QPS约为409.6w/s,这种分配方式可以保证在任何一个IDC的任何一台机器在任意毫秒内生成的ID都是不同的。

    该方案优点

    • 毫秒数在高位,自增序列在低位,整个ID都是趋势递增的。
    • 不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成ID的性能也是非常高的。
    • 可以根据自身业务特性分配bit位,非常灵活。

    snowflake算法将面临两个挑战
    1)机器id的指定
    这个问题在分布式的环境下会比较突出,通常的解决方案是利用Redis或者Zookeeper进行机器注册,确保注册上去的机器id是唯一的。
    为了解决强依赖Redis或者Zookeeper的问题,可以将机器id写入本地文件系统。

    2)机器id的生成规则
    这个问题会有一些纠结,因为机器id的生成大致要满足三个条件:
    a. int类型(10bit)纯数字,b. 相对稳定,c. 与其他机器要有所区别。
    至于优雅美观,都是其次了。对于机器id的存储,可以使用HASH结构,KEY的规则是“application-name.port.ip”,其中ip是通过算法转换成了一段长整型的纯数字,VALUE则是机器id,服务id,机房id,其中,可以通过服务id和机房id反推出机器id。
    如果用Redis存储,其表现形式如下:

    HASH:sequence-client.8112.3232123398
    
    | row | key       | value |
    | --- | --------- | ----- |
    | 1   | machineId | 257   |
    | 2   | workId    | 1     |
    | 3   | rackId    | 1     |
    

    3)时钟回拨
    因为snowflake对系统时间是很依赖的,所以对于时钟的波动是很敏感的,尤其是时钟回拨,很有可能就会出现重复发号的情况。时钟回拨问题解决策略通常是直接拒绝发号,直到时钟正常,必要时进行告警。

    由于强依赖时钟,对时间的要求比较敏感,在机器工作时NTP同步也会造成秒级别的回退,建议可以直接关闭NTP同步。要么在时钟回拨的时候直接不提供服务直接返回ERROR_CODE,等时钟追上即可。或者做一层重试,然后上报报警系统,更或者是发现有时钟回拨之后自动摘除本身节点并报警

    具体可以参考美团的snowflake方案。

    参考

    相关文章

      网友评论

          本文标题:发号器设计

          本文链接:https://www.haomeiwen.com/subject/wlqpgktx.html