Package kid (K-sortable ID) provides a goroutine-safe generator of short (10 byte binary, 16 bytes when base32 encoded), url-safe, k-sortable unique IDs.
The 10-byte binary representation of an ID is composed of:
- 6-byte value representing Unix time in milliseconds
- 2-byte sequence, and,
- 2-byte random value.
IDs encode (base32) as 16-byte url-friendly strings that look like:
06bqj05bhh2lcbdb
- Size: 10 bytes as binary, 16 bytes if stored/transported as an encoded string.
- Timestamp + sequence is guaranteed to be unique for each call to New().
- 2 bytes of trailing randomness to avoid counter-based attacks.
- K-orderable in both binary and base32 encoded representations.
- URL-friendly custom encoding without the vowels a, i, o, and u.
- Automatic (un)/marshalling for SQL and JSON.
- cmd/kid tool for ID generation and introspection.
func main() {
id := kid.New()
fmt.Printf("%s %s %03v\n", id, id.String(), id[:])
// Example output: 06bq7xhnr03mlz6r 06bq7xhnr03mlz6r [001 149 115 246 021 192 007 073 252 216]
id, err := kid.FromString("06bq7xhnr03mlz6r")
if err != nil {
// do something
}
fmt.Printf("%s %s %03v\n", id, id.String(), id[:])
// Output: 06bq7xhnr03mlz6r 06bq7xhnr03mlz6r [001 149 115 246 021 192 007 073 252 216]
}
-
While the ID payload differs greatly, the API and much of this package borrows heavily from github.com/rs/xid, a zero-configuration globally-unique ID generator.
-
Unique timestamp+sequence pairs are generated by the github.com/google/uuid getV7Time() algorithm.
Each call to kid.New()
is guaranteed to return a unique ID with a
timestamp+sequence greater than any previous call.
To satisfy whether kid.IDs are unique, run eval/uniqcheck/main.go:
$ go run eval/uniqcheck/main.go -count 2000000 -goroutines 20
# example output:
Generating 2,000,000 IDs per 20 goroutines:
Total keys: 40,000,000. Keys in last time tick: 1,380. Number of dupes: 0
Or, at the command line, produce IDs and use OS utilities to check (single-threaded):
$ go install github.com/mwyvr/kid/cmd/kid@latest
$ kid -c 2000000 | sort | uniq -d
// None output
Package kid
also provides a tool for id generation and inspection:
$ kid
06bpwm8x107evvh9
$ kid -c 2
06bpwm3hkm371gz4
06bpwm3hkm3d5ezr
# produce 4 and inspect
kid $(kid -c 4)
06bpwlvhb86bypp7 ts:1741312454738 seq:3247 rnd:23239 2025-03-07 01:54:14.738 +0000 UTC ID{ 0x1, 0x95, 0x6e, 0x4f, 0x70, 0x52, 0xc, 0xaf, 0x5a, 0xc7 }
06bpwlvhb86gcdw6 ts:1741312454738 seq:3317 rnd:45958 2025-03-07 01:54:14.738 +0000 UTC ID{ 0x1, 0x95, 0x6e, 0x4f, 0x70, 0x52, 0xc, 0xf5, 0xb3, 0x86 }
06bpwlvhb86gkmks ts:1741312454738 seq:3320 rnd:53817 2025-03-07 01:54:14.738 +0000 UTC ID{ 0x1, 0x95, 0x6e, 0x4f, 0x70, 0x52, 0xc, 0xf8, 0xd2, 0x39 }
06bpwlvhb86gmb73 ts:1741312454738 seq:3322 rnd:10467 2025-03-07 01:54:14.738 +0000 UTC ID{ 0x1, 0x95, 0x6e, 0x4f, 0x70, 0x52, 0xc, 0xfa, 0x28, 0xe3 }
- 2025-03-24 Drop minimum supported Go version to 1.23, thanks to heads up from @sergeevabc.
- 2025-03-08 v1.2.0 released.
- 2025-03-06 Forked rid in favour of kid for true k-sortability, requiring a new ID payload, now expected to remain static. Improved code coverage and documentation.
Contributions are welcome.
kid
was born out of a desire for a short, k-sortable unique ID where global
uniqueness or inter-process ID generation coordination is not required.
A comparison of various Go ID generators:
Package | BLen | ELen | K-Sort | Encoded ID and Next | Unique | Components |
---|---|---|---|---|---|---|
mwyvr/kid | 10 | 16 | true | 06bwz2qyzm14d070 06bwz2qyzm14fnte 06bwz2qyzm14hxmf 06bwz2qyzm14kdl1 |
unique (ts(ms) + sequence) + crypto/rand | 6 byte ts(millisecond) : 2 byte sequence : 2 byte random |
rs/xid | 12 | 20 | true | cvhjc0tq9fa75iaa3d00 cvhjc0tq9fa75iaa3d0g cvhjc0tq9fa75iaa3d10 cvhjc0tq9fa75iaa3d1g |
ts(sec) + machineID + pid + counter | 4 byte ts(sec) : 2 byte mach ID : 2 byte pid : 3 byte monotonic counter |
segmentio/ksuid | 20 | 27 | true | 2upRtyliBRn6UnfS2RsdkEIhqbg 2upRu1TTpojt5KQDykjTjreGXGE 2upRu0IZ0RbjFMSS1lb0Io3aQ8A 2upRu2AjlZoy3rnU6MJdqSuDs1H |
ts + crypto/rand | 4 byte ts(sec) : 16 byte random |
google/uuid V4 | 16 | 36 | false | f03fed10-c632-4d06-95b5-6783796e6aaa 1c6c044b-66db-44e8-ac45-0e4e358dcc1f 9b26d7cd-d85d-4696-beec-0483d6446e7f f2cf8c26-4e3c-4612-865c-2270883456fd |
crypt/rand | v4: 122 bits random; 6 bits embedding version & variant |
google/uuid V7 | 16 | 36 | true | 0195cf8a-fefd-725a-8655-0910a814aa54 0195cf8a-fefd-725b-b7ee-b178436e1aa0 0195cf8a-fefd-725e-b663-bc0ae6ee45f9 0195cf8a-fefd-725f-8936-57d2937b3ecc |
ts(ms) + crypt/rand | v7: 16 bytes : 48 bits time, 12 bits sequence, 6 bits version/variant, 62 bits random |
chilts/sid | 16 | 23 | true | 1WlBXfeKc31-1L10__76tcP 1WlBXfeKcCQ-4_us_ZM7ZtY 1WlBXfeKcMr-0HIZhTn1J_0 1WlBXfeKcUN-4AYyBPKLA5P |
ts + math/rand | 8 byte ts(nanosecond) 8 byte random |
matoous/go-nanoid/v2 | 21 | 21 | false | iaGKMTIcslXAPNkbIp4ho hM2E2H2y56NtaljmWCfNs SpTDFWVxLW_Rrk_S93A87 kPxkTozTenuSfwAO0s9rb |
ts + crypto/rand | 21 byte rand (adjustable) |
sony/sonyflake | 16 | 29 | true | GU2TSMZXGYYTCNZVGYYDANBZHEZTA GU2TSMZXGYYTCNZVGYYDCMJVGQ3DM GU2TSMZXGYYTCNZVGYYDCOBRGAYDE GU2TSMZXGYYTCNZVGYYDENBWGUZTQ |
ts + counter | 39 bit ts(10msec) 8 bit seq, 16 bit mach id |
oklog/ulid | 16 | 26 | true | 01JQ7RNZQXCZ605958V4E9XMF0 01JQ7RNZQXK35FDQBEQM86VMEF 01JQ7RNZQXJV8JBXKFE1VVPJJT 01JQ7RNZQX1GJ4EQSG1KC3RY8N |
ts + user-definable rand src | 6 byte ts(ms) : 10 byte monotonic counter random init per ts(ms) |
kjk/betterguid | 17 | 20 | true | -OMEXjvxqWZMbCF4xNP6 -OMEXjvxqWZMbCF4xNP7 -OMEXjvxqWZMbCF4xNP8 -OMEXjvxqWZMbCF4xNP9 |
ts + rand-init counter | 8 byte ts(ms) : 9 byte counter random init per ts(ms) |
A benchmark suite comparing some of the above-noted packages can be found in
eval/bench/bench_test.go. All runs were done with
scaling_governor set to performance
:
echo "performance" | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
$ go test -cpu 1,2,4,8,16,32 -test.benchmem -bench .
goos: linux
goarch: amd64
pkg: github.com/mwyvr/kid/eval/bench
cpu: Intel(R) Core(TM) i9-14900K
BenchmarkKid 25668646 44.42 ns/op 0 B/op 0 allocs/op
BenchmarkKid-2 24071296 50.45 ns/op 0 B/op 0 allocs/op
BenchmarkKid-4 15894621 75.01 ns/op 0 B/op 0 allocs/op
BenchmarkKid-8 12931524 95.28 ns/op 0 B/op 0 allocs/op
BenchmarkKid-16 10273124 114.5 ns/op 0 B/op 0 allocs/op
BenchmarkKid-32 8641492 138.6 ns/op 0 B/op 0 allocs/op
BenchmarkXid 39413270 28.53 ns/op 0 B/op 0 allocs/op
BenchmarkXid-2 42305062 27.39 ns/op 0 B/op 0 allocs/op
BenchmarkXid-4 42286348 27.95 ns/op 0 B/op 0 allocs/op
BenchmarkXid-8 40082140 29.77 ns/op 0 B/op 0 allocs/op
BenchmarkXid-16 36853810 32.59 ns/op 0 B/op 0 allocs/op
BenchmarkXid-32 58261640 21.51 ns/op 0 B/op 0 allocs/op
BenchmarkKsuid 15683168 75.23 ns/op 0 B/op 0 allocs/op
BenchmarkKsuid-2 14172432 84.61 ns/op 0 B/op 0 allocs/op
BenchmarkKsuid-4 11290425 101.4 ns/op 0 B/op 0 allocs/op
BenchmarkKsuid-8 9389043 119.3 ns/op 0 B/op 0 allocs/op
BenchmarkKsuid-16 7758454 153.2 ns/op 0 B/op 0 allocs/op
BenchmarkKsuid-32 6843870 181.9 ns/op 0 B/op 0 allocs/op
BenchmarkGoogleUuid 24592844 47.72 ns/op 16 B/op 1 allocs/op
BenchmarkGoogleUuid-2 29907156 37.78 ns/op 16 B/op 1 allocs/op
BenchmarkGoogleUuid-4 36956997 31.78 ns/op 16 B/op 1 allocs/op
BenchmarkGoogleUuid-8 44705392 29.42 ns/op 16 B/op 1 allocs/op
BenchmarkGoogleUuid-16 38979994 33.91 ns/op 16 B/op 1 allocs/op
BenchmarkGoogleUuid-32 43177587 28.15 ns/op 16 B/op 1 allocs/op
BenchmarkGoogleUuidV7 14226376 83.90 ns/op 16 B/op 1 allocs/op
BenchmarkGoogleUuidV7-2 12127534 89.25 ns/op 16 B/op 1 allocs/op
BenchmarkGoogleUuidV7-4 12975457 92.48 ns/op 16 B/op 1 allocs/op
BenchmarkGoogleUuidV7-8 12842493 95.62 ns/op 16 B/op 1 allocs/op
BenchmarkGoogleUuidV7-16 10053843 119.1 ns/op 16 B/op 1 allocs/op
BenchmarkGoogleUuidV7-32 7877816 152.3 ns/op 16 B/op 1 allocs/op
BenchmarkUlid 19653237 60.15 ns/op 16 B/op 1 allocs/op
BenchmarkUlid-2 29194627 39.72 ns/op 16 B/op 1 allocs/op
BenchmarkUlid-4 42736906 29.85 ns/op 16 B/op 1 allocs/op
BenchmarkUlid-8 38114436 30.23 ns/op 16 B/op 1 allocs/op
BenchmarkUlid-16 39977670 34.00 ns/op 16 B/op 1 allocs/op
BenchmarkUlid-32 41136363 28.09 ns/op 16 B/op 1 allocs/op
BenchmarkBetterguid 25478740 46.51 ns/op 24 B/op 1 allocs/op
BenchmarkBetterguid-2 24268438 47.96 ns/op 24 B/op 1 allocs/op
BenchmarkBetterguid-4 18773090 64.66 ns/op 24 B/op 1 allocs/op
BenchmarkBetterguid-8 14096698 81.05 ns/op 24 B/op 1 allocs/op
BenchmarkBetterguid-16 10897140 110.5 ns/op 24 B/op 1 allocs/op
BenchmarkBetterguid-32 9171368 132.2 ns/op 24 B/op 1 allocs/op
PASS
ok github.com/mwyvr/kid/eval/bench 53.240s