读Sharding & IDs at Instagram

原文地址 https://instagram-engineering.com/sharding-ids-at-instagram-1cf5a71e5a5c

文章说提到的一个环境是, 在ins中, 每秒有25的照片和90个赞, 那么怎么把这个量级的数据存储在Django和PostgreSQL中, 在数据库的集群中, 如果用单一的递增id查找数据(严格递增), 会让查找非常慢. 在分析过系统需求后, ins的工程师做出以下几点具体需求:

  • 生成的ID要是天然地sorted by time. 这里的天然地指的是不需要其他id上的数据支持.
  • id最好能64位.
  • 最好能有一个简单移动的解决方案. 并且让数据的迁移变小.

在现有的解决方案下, ins的工程师有两个选择, 一个是在app端生成id (减少服务器端生产id所需资源消耗), 另一个是用现成的UUIDs的算法几何.

使用这些现有的工具的优点有:

  • 在客户端生产id, 可以最大程度减少failure和id重复.
  • 如果用timestamp作为id的一个part, 那么自然就是sorted.

但是缺点很明显:

  • 这些解决方案会让整体的系统结构变得复杂. (这里我不知道为什么作者要写这个, 因为snowflake本来就是twitter开发用来做这个功能的, 当时在退休前已经是很完善的分布式id生成器了)

另一种思路是用数据库自带的id生成器. Flickr用两个sql做ticket db. 然后前边用一个load balancer 挡着, 两个sql一个奇数, 一个偶数.

用这种解决方案的优点是:

  • 因为id生成器靠近服务器端, 所以有更好的控制能力和整体性.

缺点是:

  • 当数据不断扩大, 最后write操作还是会成为bottleneck.
  • tickets servers需要额外的维护.
  • sorted by time 只能粗略的保证.

现在来看看ins的最终解决方案:

在ins中, 使用了上千个分片机, 为了确保服务器的架构简单和可迁移性, 他们使用了靠近服务器端的id生成器. 具体来说, 他们把id分给每个逻辑分片机上的数据库的每个table上. 所以他们使用了一个id的不同字段表示不同的逻辑分片机, 一个id具体包括:

  • 41个bits作为timestamp, 确保id可以sorted
  • 13个bits作为标识符, 标示不同的逻辑分片机.
  • 10个bits作为 id序列, 通过mod 1024得到, 这意味着每个timestamp下(每毫秒), 每个分片机最多可以承受1024个id的数据量.

在插入数据时, 先确定timestamp, 然后通过user id % total shards 得到插入的区间, 然后再通过系统auto increment生产的id % 1024得到具体的id.