Sqids: 简短唯一ID生成器

Sqids(发音为squids, 鱿鱼)是一个开源库,允许您从数字中生成简短的唯一且看起来随机的标识符, 而且这些ID是URL安全的,你可以同时编码几个数字, 也可以剔除常见的脏话或者政治敏感的单词。你可以把它想象成一个十进制到十六进制的转换器,但它还具备一些额外的功能。
比如:https://example.com/Lqj8a0

先前它叫做Hashids, 但是由于商标问题,我不得不更改名称。Sqids是Short Quick Unique Identifiers的缩写。

为什么需要简短的唯一ID

适合场景

  • 短链接缩
  • 为日志生成唯一的事件 ID
  • 为网站上的产品/对象生成 ID(如 YouTube 为视频所做的那样)
  • 为文本消息生成短 ID
  • 电子邮件中的确认码
  • 商品优惠码
  • 起止时间戳合并缩写
  • 地理位置编码
  • 临时的token

不适合场景

  • 任何敏感数据
  • 生成的 ID 不是哈希值,可以解码回数字。例如,它们可能不是用户 ID 的好选择,因为一旦解码,它们可能会泄露你的应用程序的用户数量

Sqids的特性

🆔 从非负数字生成短 ID
✅ 易于编码和解码
🫣 自动生成的 ID 不包含常见的禁用词
🎲 支持通过打乱字母顺序来自定义 ID
🥳 44个语言库, 比如Go、Rust、C、C++、Bash、Java、Python、JavaScript等
📌 每个版本都生成相同的 ID
🍻 带有宽松许可的小型库 (MIT许可)

Sqids 你可以可以自定义字母表。更长的字母表产生的ID更短,更短的字母表产生的ID更长。默认字母表包含大写和小写字母,因此默认ID是区分大小写的。

使用同样的输入和相同的字母表,生成的ID是唯一的。

Sqids可以将一个或多个非负数编码为单个ID。您可以编码的数字数量没有限制,但可以编码的数字大小有限(取决于实现语言)。
比如您可以编码开始时间和结束时间的UNIX时间戳为一个ID,或者您可以将数据库分片号与主键一起编码,并节省额外的数据库查询。

Sqids 有什么限制

  • Sqids 不能编码负数。
  • 最小字母表长度是 3 个字符。
  • 字母表中不能包含任何多字节字符。
  • Sqids 不能生成固定长度的 ID,只能生成至少达到某个长度的 ID。最小长度参数范围在 0 到 255 之间。
  • Sqids 可以尝试重新生成长度最多为字母表长度减一的 ID。

屏蔽列表

屏蔽列表可以防止某些单词出现在您的 ID 中。这是有益的,因为生成的 ID 可能会出现在公共场所,如 URL 中。

Sqids 附带了一个默认屏蔽列表,其中包含了几种语言中最基本的不雅和不当词汇。当然,您也可以用自己的词汇来扩展这个屏蔽列表。

屏蔽列表中的单词匹配是不区分大小写的。

少于 3 个字符的短单词不会被屏蔽。长度为 3 个字符的单词必须与 ID 完全匹配。如果 4 个或更多字符的单词是 ID 的子字符串,则会触发匹配。

如果屏蔽列表中的单词包含数字(leet 术语),则只有当它们位于 ID 的开头或结尾时才会触发匹配。

ID 是否合法

解码 ID 通常会产生某种数值输出,但这并不一定意味着该 ID 是规范的。为了检查 ID 是否有效,您可以重新编码解码后的数字,并检查 ID 是否匹配。

不自动执行此操作的原因是,如果将来默认屏蔽列表发生更改,我们不希望自动使过去生成的、现在可能与新屏蔽列表单词匹配的 ID 失效。

Go示例

官方提供了一个Go语言的库:sqids/sqids-go

以下是一个简单的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main
import (
"fmt"
"time"
"github.com/sqids/sqids-go"
)
func main() {
s, _ := sqids.New()
id, _ := s.Encode([]uint64{1234567890}) // "PcHfYmv"
fmt.Println(id)
start := time.Now().Unix()
end := time.Now().Add(24 * time.Hour).Unix()
id, _ = s.Encode([]uint64{uint64(start), uint64(end)}) // "s6eUn008oGU27p"
fmt.Println(id)
numbers := s.Decode(id) // [1714879533 1714965933]
fmt.Println(numbers)
}

New函数还提供额外的参数,用来定制字母表、最小长度和黑名单:

1
2
3
4
5
type Options struct {
Alphabet string
MinLength uint8
Blocklist []string
}

比如:

1
2
3
4
5
s, _ := sqids.New(sqids.Options{
Alphabet: "FxnXM1kBN6cuhsAvjW3Co7l2RePyY8DwaU04Tzt9fHQrqSVKdpimLGIJOgb5ZE",
})
id, _ := s.Encode([]uint64{1, 2, 3}) // "B4aajs"
numbers := s.Decode(id) // [1, 2, 3]