使用 Rust 实现 SnowflakeId
在最近的业务中更改设计的时候最终决定使用 雪花 ID (下文称之为 SID)作为数据库的主键,这样可以避免使用发号器等中间件。
但是广为使用的 snowflake 的实现实际上是线程级别的唯一,而不是分布式意义上的唯一,因此在生产上如果和分布式搭配会产生极大的问题。
怎么办?只能自己写了。
原理
SID 实际上是 Rust 的 i64
,他有 64 位。但是有一位是符号位,所以实际上可以使用的只有 63 位但也绰绰有余。
所以我们的结构看起来像是这样:
1 |
|
接下来我们将会介绍标准的 SID 实现。为什么是标准的?因为存在很多变种,比如 Mastodon 就是变种 SID,我们不讨论他们。
标准的 SID 包含如下信息
- 时间戳: 41bit
- 标识符: 10bit
- 序列号: 12bit
所以我们的 SID 看起来应该如下
1 |
|
✨ 深入黑暗
很好,现在我们理解了最基本的 SID 组成,让我们更深一步。
时间戳
标准的 SID 使用的是毫秒精度,恰好是 41 位。实际上根据 SID 的设计不同,时间戳可以是任意精度,也可以是任意起始位置。
标识符
在设计分布式系统时,我们会有多台机器(或实例)同时运行。
因此,我们必须区分它们。基于标识符为 10 位,我们可以同时拥有 1024 个实例,这简直太酷了!
序列号
你注意到 Sequence Number
了吗?它有 12 位,这意味着它在一毫秒内最多可以处理 4096 条信息(或其他东西,随便你)。
综上所述:整个系统在一毫秒内最多可以产生 1024 * 4096 = 4194304
条信息,这完全足够了!
分配完毕(不对,你是怎么做到的?!)
但我们总有可能遇到这样的情况:这一毫秒内的所有 SID 都已分配完毕!
此时,实例必须等待下一毫秒。在下一毫秒,我们将有新的 4096 个 SID 可以分配。
在这种情况下,可能需要拓展实例了 XD
使用 Rust 实现 SnowflakeId
https://blog.krysztal.dev/2024/10/23/使用-Rust-实现-SnowflakeId/