oklog/ulid

GitHub: oklog/ulid

该项目是 ULID(通用唯一词典可排序标识符)的 Go 实现,解决了 UUID 无法按时间排序且编码冗长的问题。

Stars: 5043 | Forks: 187

# 通用唯一词典可排序标识符 [![项目状态](https://img.shields.io/github/release/oklog/ulid.svg?style=flat-square)](https://github.com/oklog/ulid/releases/latest) ![构建状态](https://static.pigsec.cn/wp-content/uploads/repos/cas/ce/ce733292a922c08274cf5a2096f8fa4cf01023bfa51a36ef6beecaaef371a9d9.svg) [![Go Report Card](https://goreportcard.com/badge/oklog/ulid?cache=0)](https://goreportcard.com/report/oklog/ulid) [![覆盖率状态](https://coveralls.io/repos/github/oklog/ulid/badge.svg?branch=master&cache=0)](https://coveralls.io/github/oklog/ulid?branch=master) [![go.dev 参考](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/oklog/ulid/v2) [![Apache 2 授权](https://img.shields.io/badge/license-Apache2-blue.svg)](https://raw.githubusercontent.com/oklog/ulid/master/LICENSE) 这是 [ulid/javascript](https://github.com/ulid/javascript) 的 Go 移植版本,并实现了二进制格式。 ## 背景 GUID/UUID 在许多用例中可能不是最优的,因为: - 它不是编码 128 位的最节省字符的方式 - UUID v1/v2 在许多环境中不实用,因为它需要访问唯一的、稳定的 MAC 地址 - UUID v3/v5 需要唯一的种子,并生成随机分布的 ID,这可能会导致许多数据结构出现碎片化 - UUID v4 除了随机性外不提供任何其他信息,这可能会导致许多数据结构出现碎片化 然而,ULID: - 与 UUID/GUID 兼容 - 每毫秒可生成 1.21e+24 个唯一的 ULID(准确地说是 1,208,925,819,614,629,174,706,176 个) - 支持按词典顺序排序 - 规范编码为 26 个字符的字符串,而 UUID 为 36 个字符 - 使用 Crockford 的 base32 以获得更好的效率和可读性(每个字符 5 位) - 不区分大小写 - 无特殊字符(URL 安全) - 单调的排序顺序(正确检测并处理同一毫秒) ## 安装 此包需要使用 Go modules。 ``` go get github.com/oklog/ulid/v2 ``` ## 用法 ULID 由两部分构成:一个是毫秒级精度的时间戳, 另一部分是一些随机数据。 时间戳被建模为表示 Unix 时间(以毫秒为单位)的 uint64 值。 可以通过将 [time.Time](https://pkg.go.dev/time#Time) 传递给 [ulid.Timestamp](https://pkg.go.dev/github.com/oklog/ulid/v2#Timestamp) 来生成它们, 或者通过调用 [time.Time.UnixMilli](https://pkg.go.dev/time#Time.UnixMilli) 并将返回的值转换为 `uint64` 来生成。 随机数据取自提供的 [io.Reader](https://pkg.go.dev/io#Reader)。 这种设计在选择权衡时提供了更大的灵活性,但对新手来说 可能会有些困惑。 如果你只是想生成一个 ULID,并且(暂时)不关心 性能、加密安全性等细节,请使用 [ulid.Make](https://pkg.go.dev/github.com/oklog/ulid/v2#Make) 辅助函数。 此函数会调用 [time.Now](https://pkg.go.dev/time#Now) 来获取时间戳, 并使用一个进程全局的、 [伪随机](https://pkg.go.dev/math/rand)且[单调的](https://pkg.go.dev/github.com/oklog/ulid/v2#LockedMonotonicReader)熵源。 ``` fmt.Println(ulid.Make()) // 01G65Z755AFWAKHE12NY0CQ9FH ``` 更高级的用例应该使用 [ulid.New](https://pkg.go.dev/github.com/oklog/ulid/v2#New)。 ``` entropy := rand.New(rand.NewSource(time.Now().UnixNano())) ms := ulid.Timestamp(time.Now()) fmt.Println(ulid.New(ms, entropy)) // 01G65Z755AFWAKHE12NY0CQ9FH ``` 在提供熵源时应当小心。 上面的示例使用了 [math/rand.Rand](https://pkg.go.dev/math/rand#Rand), 它不支持多个 goroutine 的并发使用。请考虑诸如 [x/exp/rand](https://pkg.go.dev/golang.org/x/exp/rand#LockedSource) 之类的替代方案。 对安全性敏感的用例应始终使用由 [crypto/rand](https://pkg.go.dev/crypto/rand) 提供的加密安全的熵。 对性能敏感的用例在生成 ID 时应避免同步操作。 一种选择是为每个并发的 goroutine 使用独立的熵源, 这不会导致锁竞争,但无法对随机数据提供强有力的保证, 并且不能在给定的毫秒内提供单调性。 一种常见的性能优化是使用 [sync.Pool](https://pkg.go.dev/sync#Pool) 来池化熵源。 单调性是指每个 ULID 都“大于”前一个 ULID 的属性。 ULID 自动具有单调性,但仅限于毫秒精度。 在同一毫秒内生成的 ULID 会根据其随机分量进行排序, 这意味着默认情况下它们是无序的。 你可以使用 [ulid.MonotonicEntropy](https://pkg.go.dev/github.com/oklog/ulid/v2#MonotonicEntropy) 或 [ulid.LockedMonotonicEntropy](https://pkg.go.dev/github.com/oklog/ulid/v2#LockedMonotonicEntropy) 来创建在给定毫秒内保持单调的 ULID,但需注意一些限制。 详情请参阅文档。 如果你不关心生成的 ID 基于时间的排序,那么就 没有理由使用 ULID! 还有许多其他类型的 ID,它们更简单、更快速、更小等等。 请考虑使用 UUID。 ## 命令行工具 此仓库还提供了一个用于在命令行生成和解析 ULID 的工具。 ``` go install github.com/oklog/ulid/v2/cmd/ulid@latest ``` 用法: ``` Usage: ulid [-hlqz] [-f ] [parameters ...] -f, --format= when parsing, show times in this format: default, rfc3339, unix, ms -h, --help print this help text -l, --local when parsing, show local time instead of UTC -q, --quick when generating, use non-crypto-grade entropy -z, --zero when generating, fix entropy to all-zeroes ``` 示例: ``` $ ulid 01D78XYFJ1PRM1WPBCBT3VHMNV $ ulid -z 01D78XZ44G0000000000000000 $ ulid 01D78XZ44G0000000000000000 Sun Mar 31 03:51:23.536 UTC 2019 $ ulid --format=rfc3339 --local 01D78XZ44G0000000000000000 2019-03-31T05:51:23.536+02:00 ``` ## 规范 以下是本仓库中实现的 ULID 的当前规范。 ### 组件 **时间戳** - 48 位 - UNIX 时间(以毫秒为单位) - 直到公元 10889 年才会耗尽空间 **熵** - 80 位 - 用户定义的熵源。 - 使用 [`ulid.Monotonic`](https://godoc.org/github.com/oklog/ulid#Monotonic) 实现同一毫秒内的单调性 ### 编码 使用 [Crockford 的 Base32](http://www.crockford.com/wrmg/base32.html),如下所示。 此字母表排除了字母 I、L、O 和 U,以避免混淆和滥用。 ``` 0123456789ABCDEFGHJKMNPQRSTVWXYZ ``` ### 二进制布局和字节顺序 各组件被编码为 16 个字节。每个组件均以最高有效字在前(网络字节顺序)进行编码。 ``` 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 32_bit_uint_time_high | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 16_bit_uint_time_low | 16_bit_uint_random | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 32_bit_uint_random | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 32_bit_uint_random | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ``` ### 字符串表示 ``` 01AN4Z07BY 79KA1307SR9X4MV3 |----------| |----------------| Timestamp Entropy 10 chars 16 chars 48bits 80bits base32 base32 ``` ## 测试 ``` go test ./... ``` ## 基准测试 在 Intel Core i7 Ivy Bridge 2.7 GHz、MacOS 10.12.1 和 Go 1.8.0beta1 环境下 ``` BenchmarkNew/WithCryptoEntropy-8 2000000 771 ns/op 20.73 MB/s 16 B/op 1 allocs/op BenchmarkNew/WithEntropy-8 20000000 65.8 ns/op 243.01 MB/s 16 B/op 1 allocs/op BenchmarkNew/WithoutEntropy-8 50000000 30.0 ns/op 534.06 MB/s 16 B/op 1 allocs/op BenchmarkMustNew/WithCryptoEntropy-8 2000000 781 ns/op 20.48 MB/s 16 B/op 1 allocs/op BenchmarkMustNew/WithEntropy-8 20000000 70.0 ns/op 228.51 MB/s 16 B/op 1 allocs/op BenchmarkMustNew/WithoutEntropy-8 50000000 34.6 ns/op 462.98 MB/s 16 B/op 1 allocs/op BenchmarkParse-8 50000000 30.0 ns/op 866.16 MB/s 0 B/op 0 allocs/op BenchmarkMustParse-8 50000000 35.2 ns/op 738.94 MB/s 0 B/op 0 allocs/op BenchmarkString-8 20000000 64.9 ns/op 246.40 MB/s 32 B/op 1 allocs/op BenchmarkMarshal/Text-8 20000000 55.8 ns/op 286.84 MB/s 32 B/op 1 allocs/op BenchmarkMarshal/TextTo-8 100000000 22.4 ns/op 714.91 MB/s 0 B/op 0 allocs/op BenchmarkMarshal/Binary-8 300000000 4.02 ns/op 3981.77 MB/s 0 B/op 0 allocs/op BenchmarkMarshal/BinaryTo-8 2000000000 1.18 ns/op 13551.75 MB/s 0 B/op 0 allocs/op BenchmarkUnmarshal/Text-8 100000000 20.5 ns/op 1265.27 MB/s 0 B/op 0 allocs/op BenchmarkUnmarshal/Binary-8 300000000 4.94 ns/op 3240.01 MB/s 0 B/op 0 allocs/op BenchmarkNow-8 100000000 15.1 ns/op 528.09 MB/s 0 B/op 0 allocs/op BenchmarkTimestamp-8 2000000000 0.29 ns/op 27271.59 MB/s 0 B/op 0 allocs/op BenchmarkTime-8 2000000000 0.58 ns/op 13717.80 MB/s 0 B/op 0 allocs/op BenchmarkSetTime-8 2000000000 0.89 ns/op 9023.95 MB/s 0 B/op 0 allocs/op BenchmarkEntropy-8 200000000 7.62 ns/op 1311.66 MB/s 0 B/op 0 allocs/op BenchmarkSetEntropy-8 2000000000 0.88 ns/op 11376.54 MB/s 0 B/op 0 allocs/op BenchmarkCompare-8 200000000 7.34 ns/op 4359.23 MB/s 0 B/op 0 allocs/op ``` ## 先前作品 - [ulid/javascript](https://github.com/ulid/javascript) - [RobThree/NUlid](https://github.com/RobThree/NUlid) - [imdario/go-ulid](https://github.com/imdario/go-ulid)
标签:EVTX分析, 日志审计