高并发系统设计 40 问
唐扬
美图公司技术专家
49013 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 49 讲
高并发系统设计 40 问
15
15
1.0x
00:00/00:00
登录|注册

10 | 发号器:如何保证分库分表后ID的全局唯一性?

独立的发号器服务
嵌入到业务代码中
应用场景
改造
优缺点
实现方式
原理
使用生成的唯一ID作为主键
使用业务字段作为主键
Snowflake算法
数据库的主键选择
一课一思
课程小结
主键全局唯一性问题
聚合查询性能
查询限制
数据分片
数据冗余
总结
分库分表
主从读写分离
分布式存储
发号器:如何保证分库分表后ID的全局唯一性?

该思维导图由 AI 生成,仅供参考

你好,我是唐扬。
在前面两节课程中,我带你了解了分布式存储两个核心问题:数据冗余和数据分片,以及在传统关系型数据库中是如何解决的。当我们面临高并发的查询数据请求时,可以使用主从读写分离的方式,部署多个从库分摊读压力;当存储的数据量达到瓶颈时,我们可以将数据分片存储在多个节点上,降低单个存储节点的存储压力,此时我们的架构变成了下面这个样子:
你可以看到,我们通过分库分表和主从读写分离的方式解决了数据库的扩展性问题,但是在 09 讲我也提到过,数据库在分库分表之后,我们在使用数据库时存在的许多限制,比方说查询的时候必须带着分区键;一些聚合类的查询(像是 count())性能较差,需要考虑使用计数器等其它的解决方案,其实分库分表还有一个问题我在09 讲中没有提到,就是主键的全局唯一性的问题。本节课,我将带你一起来了解,在分库分表后如何生成全局唯一的数据库主键。
不过,在探究这个问题之前,你需要对“使用什么字段作为主键”这个问题有所了解,这样才能为我们后续探究如何生成全局唯一的主键做好铺垫。

数据库的主键要如何选择?

数据库中的每一条记录都需要有一个唯一的标识,依据数据库的第二范式,数据库中每一个表中都需要有一个唯一的主键,其他数据元素和主键一一对应。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

分库分表是解决数据库扩展性问题的有效方式,但在此过程中,全局唯一性的主键生成成为一个挑战。文章介绍了在分库分表后如何保证ID的全局唯一性,提出了使用生成的唯一ID作为主键的建议。对于单库单表的场景,可以使用数据库的自增字段作为ID,但在分库分表后,这种方式无法保证ID的全局唯一性。因此,建议搭建发号器服务来生成全局唯一的ID。这样可以确保在分布到多个库中的同一个逻辑表的数据中,ID仍然是全局唯一的。文章还介绍了选择主键的两种方式:使用业务字段或者生成的唯一ID,并分析了它们的适用场景和局限性。文章内容深入浅出,为读者提供了解决分库分表后ID全局唯一性问题的有效方法。 文章还介绍了Snowflake算法的原理和设计思想,以及如何将其工程化应用来生成全局唯一的ID。Snowflake算法通过将64位的二进制数字分成若干部分,存储时间戳、机器ID、序列号等数据,最终生成全局唯一的有序ID。该算法简单高效,能够生成具有全局唯一性、单调递增性和有业务含义的ID。然而,它也存在一些缺点,如依赖系统时间戳可能导致生成重复ID,以及在低QPS情况下可能造成数据分配不均匀的问题。作者还分享了对Snowflake算法的改造和优化方法,以及其他公司基于数据库生成ID的方案。这些方法都能解决分布式环境下ID全局唯一性的问题,读者可以从中寻找适合自己业务场景的解决方案。 总的来说,本文通过介绍分库分表后ID全局唯一性的问题和Snowflake算法的应用,为读者提供了解决数据库扩展性和ID生成问题的有效方法,同时也启发读者多角度了解不同的方法,找到最适合自己业务场景的解决方案。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《高并发系统设计 40 问》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(57)

  • 最新
  • 精选
  • 阿杜
    不仅仅是分库分表后的数据库,很多业务场景都需要分布式发号器,使用snowflake是个很好的选择,不过一般都是用的snowflake雪花算法,实现上会有所差异,比如机器位数和序号位数的选取就不同,1+41位时间戳+10机器区间位+12号递增或随机的数字,类似这种。uuid长度过长,也不递增,使用受限。不过snowflake算法有个问题就是服务器时间回拨的问题,就是时间可能不准,这个时候不能停止发号,我觉得可以采取的方式是:每个服务器存储最新的一个maxNewId,起个线程监控服务器时间是否正确,不正确就从maxNewId递增1获取,同事调准服务器时间,直到服务器时间正确。

    作者回复: 应该可行

    2019-12-20
    20
  • 程序水果宝
    老师说如果我们发现系统时钟不准,就可以让发号器暂时拒绝发号,直到时钟准确为止。我们的程序本身就是运行在系统中的,如何来判断系统中的时间是否准确呢?

    作者回复: 可以暂时记录上次发好的时间,然后和这次的时间比较

    2019-10-09
    5
    18
  • 张珂
    老师好,想了解部署一套snowflake,性能怎么样?还有一个问题是,发号器虽然可以保证递增发号,但写入数据库时(假设有两个事务要写同一个表),那对于底层B+树也不一定顺序写入,无法利用磁盘顺序写的性能优化吧?

    作者回复: 性能在单实例单核可以达到2亿万次每秒 发号器一般是改的redis

    2020-01-21
    2
    8
  • 小喵喵
    但是当数据库分库分表后,使用自增字段就无法保证 ID 的全局唯一性了? 1.使用数据库的自增,设置起始值和步长不一样,不是一样可以实现吗? 2.预估每天的数据量,预先生成ID存入缓存(比如Redis)里面,然后去取,这种方法也简单?

    作者回复: 其实很难预估数据量,某一天有活动咋办?不同的起始值也可,只是增加人工成本,增加了库表咋办?忘了设置咋办?

    2019-10-09
    3
    7
  • Lane
    老师我有疑问:中间的机器ID,同一毫秒内,3号机器先注册了一个用户,1号机器再注册一个用户。这样的话也不是顺序的了。

    作者回复: 是的,如果是独立部署的话就可以保证了

    2020-01-31
    3
    5
  • 长期规划
    老师,序列号占12位,对应序列号最大值4096,如果一毫秒内请求生成唯一键的次数大于此值怎么办呢?我能想到的办法是当生成的序列号达到4096时,延时1毫秒,再生成。实际中,是这样处理吗?

    作者回复: 会发这么多号吗……

    2019-10-22
    5
  • 王肖武
    snowflake不能保证单调递增吧?首先,服务器的时钟可能有快有慢;其次,同一时刻,机器号大的机器生成的ID总是大于机器号小的机器,但他的请求可能是先到达了数据库。个人观点:主键还是要用数据库的自增id,另外再加个全局唯一的code作为业务主键。

    作者回复: 首先,服务器的时钟一般是对时的 其次,如果是单独部署的发号器,没有机器ID是可以保证单调递增的

    2019-10-12
    5
    5
  • stg609
    假设通过容器化来部署发号器,且同时会有多个发号器容器运行,那这个 worker Id 如何生成。容器自身的 id 是一串很长的16进制,无法转换为 worker id 吧?难道也需要引入 zookeeper 吗?有没有其他简单可行的方案?

    作者回复: 容器ID太长了。。。 其实引入zk也还好,对于zk是弱依赖,只是启动的时候拉一下机器ID

    2019-10-09
    2
    5
  • jimmy
    snowflake方案中 现在一般公司都有容器虚拟化,所以每个实例都有自己的实例ID,以此作为唯一ID即可,另外保险起见在服务启动的时候可以向其他启动的服务发送check请求,确保ID全局唯一,这样可以不引入zk,让系统更简单些~

    作者回复: 容器ID太长了吧,比较占发号器的位数

    2019-10-09
    5
  • helloworld
    老师,有相关的示例代码不?我的理解是每一个毫秒将下41时间戳加1,10位的机器不变,12的序列号先随机生成一个数字,然后再在这个基础上生成这一毫秒所需要的全局id的数量。不知道我理解的对不对。打卡09.

    作者回复: 是的,没错

    2019-10-21
    2
    4
收起评论
显示
设置
留言
57
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部