从零了解分布式主键
从零了解分布式主键分布式主键的需求解决方案数据库生成(主从)数据库生成(多主)UUIDSNOWFLAKE(雪花算法)分布式主键的需求全局唯一性: 不能出现重复的 ID 号,既然是唯一标识,这是最基本的要求。趋势递增: 在 MySQL InnoDB 引擎中使用的是聚集索引,由于多数 RDBMS 使用 B-tree 的数据结构来存储索引数据,在主键的选择上面我们应该尽量使用有序的主键保证写入性能。单调
分布式主键的需求
- 全局唯一性: 不能出现重复的 ID 号,既然是唯一标识,这是最基本的要求。
- 趋势递增: 在 MySQL InnoDB 引擎中使用的是聚集索引,由于多数 RDBMS 使用 B-tree 的数据结构来存储索引数据,在主键的选择上面我们应该尽量使用有序的主键保证写入性能。
- 单调递增: 保证下一个 ID 一定大于上一个 ID,例如事务版本号、IM 增量消息、排序等特殊需求。
- 信息安全: 如果 ID 是连续的,恶意用户的扒取工作就非常容易做了,直接按照顺序下载指定 URL 即可;如果是订单号就更危险了,竞对可以直接知道我们一天的单量。所以在一些应用场景下,会需要 ID 无规则、不规则。
解决方案
数据库生成(主从)
以 MySQL 举例,利用给字段设置 auto_increment_increment 和 auto_increment_offset 来保证 ID 自增,每次业务使用下列 SQL 读写 MySQL 得到 ID 号。
- 优点:
- 非常简单,利用现有数据库系统的功能实现,成本小,有 DBA 专业维护。
- ID 号单调自增,可以实现一些对 ID 有特殊要求的业务。
- 缺点:
- 强依赖 DB,当 DB 异常时整个系统不可用,属于致命问题。配置主从复制可以尽可能的增加可用性,但是数据一致性在特殊情况下难以保证。主从切换时的不一致可能会导致重复发号。
- ID 发号性能瓶颈限制在单台 MySQL 的读写性能。
数据库生成(多主)
对于 MySQL 性能问题,可用如下方案解决:在分布式系统中我们可以多部署几台机器,每台机器设置不同的初始值,且步长和机器数相等。比如有两台机器。设置步长 step 为 2,TicketServer1 的初始值为 1(1,3,5,7,9,11…)、TicketServer2 的初始值为 2(2,4,6,8,10…)
缺点: 系统水平扩展比较困难,比如定义好了步长和机器台数之后,如果要添加机器该怎么做?使用发号器分发id而不是由mysql自增来解决
UUID
UUID (Universally Unique Identifier) 的标准型式包含 32 个 16 进制数字,以连字号分为五段,形式为 8-4-4-4-12 的 36 个字符,示例:550e8400-e29b-41d4-a716-446655440000,到目前为止业界一共有 5 种方式生成 UUID
- 优点:
- 性能非常高:本地生成,没有网络消耗。
- 缺点:
- 不易于存储:UUID 太长,16 字节 128 位,通常以 36 长度的字符串表示,很多场景不适用。
- 信息不安全:基于 MAC 地址生成 UUID 的算法可能会造成 MAC 地址泄露,这个漏洞曾被用于寻找梅丽莎病毒的制作者位置。
- ID 作为主键时在特定的环境会存在一些问题,比如做 DB 主键的场景下,UUID 就非常不适用:
注意:
MySQL 官方有明确的建议主键要尽量越短越好,36 个字符长度的 UUID 不符合要求
对 MySQL 索引不利:如果作为数据库主键,在 InnoDB 引擎下,UUID 的无序性可能会引起数据位置频繁变动,严重影响性能
SNOWFLAKE(雪花算法)
雪花算法是由 Twitter 公布的分布式主键生成算法,它能够保证不同进程主键的不重复性,以及相同进程主键的有序性。在同一个进程中,它首先是通过时间位保证不重复,如果时间相同则是通过序列位保证。 同时由于 时间位是单调递增的,且各个服务器如果大体做了时间同步,那么生成的主键在分布式环境可以认为是总体有序的,这就保证了对索引字段的插入的高效性。例如 MySQL 的 Innodb 存储引擎的主键。
使用雪花算法生成的主键,二进制表示形式 包含 4 部分,从高位到低位分表为:1bit 符号位、41bit 时间戳位、10bit 工作进程位以及 12bit 序列号位。
- 预留的符号位,恒为零。
- 时间戳位 (41bit),可用69.73 年满足绝大部分系统的要求。
- 工作进程位 (10bit),该标志在 Java 进程内是唯一的,如果是分布式应用部署应保证每个工作进程的 id 是不同的。该值默认为 0,可通过属性设置。
- 序列号位 (12bit),该序列是用来在同一个毫秒内生成不同的 ID。如果在这个毫秒内生成的数量超过 4096 (2 的 12 次幂),那么生成器会等待到下个毫秒继续生成。
注意: 该算法存在 时钟回拨 问题,服务器时钟回拨会导致产生重复序列,因此默认分布式主键生成器提供了一个最大容忍的时钟回拨毫秒数。 如果时钟回拨的时间超过最大容忍的毫秒数阈值,则程序报错;如果在可容忍的范围内,默认分布式主键生成器会等待时钟同步到最后一次主键生成的时间后再继续工作。 最大容忍的时钟回拨毫秒数的默认值为 0,可通过属性设置。
更多推荐
所有评论(0)