如何做一个靠谱的发号器

本文曾载于 有赞技术团队博客 https://tech.youzan.com/id_generator/ 为什么需要一个发号器 在使用数据库时,表的主键经常会使用数据库的自增(auto_increment)来产生。这当然很方便也很高效。但是使用自增也会带来一些麻烦。如果从一个数据库以外的地方,也就是发号器来产生全局唯一 ID,这些问题就可以得到解决,生活就可以更美好。 难以适应分片场景 在采用数据库分片时,如果使用数据库自增 ID,不同分片上会产生相同的 ID。单靠 ID 无法唯一标示一个对象,还需要额外加上分片字段才行。如果需要将 ID 用于其他对象的关联时,会麻烦很多。而采用发号器生成的是全局唯一的 ID,单靠 ID 就能实现关联。同时,这也使得采用 ID 作为分片字段成为可能。 主备切换时数据冲突 在 MySQL 集群发生主备切换时,异步复制无法确保主从完全同步。在备库开放写入后,备库上产生的自增 ID 会和尚未同步的主库上的数据冲突。这样一来,即使原来的主库恢复了,也无法重新加入集群。数据修复也变成了一件非常困难的事情。引入发号器以后,备库上插入的 ID 和原来主库上的 ID 是不会重复的。因此,未复制的新增数据和对这些新增数据的修改就不会在备库发生冲突。 网络异常时无法判断插入是否成功 当插入记录时,如果使用数据库自增 ID,在完成插入后,才能得到产生的 ID。如果在执行语句时发生网络中断,客户端无法知道事务是否成功,即使成功,也无法再获得产生的 ID。如果使用发号器,就可以在插入之前预先产生 ID。如果碰到网络中断,可以用已经获得的 ID 去尝试查询来判断之前的插入是否成功。 此外,一些业务 ID 会需要一个全局唯一的整数作为它的组成部分。其他的分布式系统可以用全局单调的唯一 ID 作为事务号。有一个现成的服务就不用各自实现了。 发号器的必要特性 既然叫发号器,首先就得保证 ID 的全局唯一。就是说保证无论什么情况下都不会发出重复的 ID。这看起来很简单,但是事实上,很多实现却上并没有做到这点。要真正做到全局唯一,发号器必须要实现 crash safe,并不受外部环境变化影响。 crash safe 首先是 crash safe。即得保证在服务崩溃重新恢复后,不会产生已经发过的 ID。在服务彻底完蛋时,也要能够在其他地方恢复出一个一定能用的。有的实现定期保存或者异步保存已经发过的 ID。如果发生崩溃,如果直接用保存过的 […]

文章来源:

Author:神仙
link:http://xiezhenye.com/2017/11/如何做一个靠谱的发号器.html