Geração de ID
1) Por que prestar atenção aos identificadores
ID é a chave fundamental da entidade: linhas de banco de dados, mensagens, arquivo, encomenda. Suas propriedades dependem:- Exclusividade e escala (colisões, crescimento horizontal).
- Ordem e triagem (correlação temporária, replicação, dedução).
- Desempenho de armazenamento (índices, páginas quentes, tamanho da chave).
- Segurança (imprevisível, fuga, adivinhação).
- Yusabiliti/integração (curto, URL-safe, não sensível a maiúsculas).
A escolha de ID é um compromisso entre entropia, ordenabilidade, comprimento, velocidade de geração e operação.
2) Requisitos e termos essenciais
Única, a probabilidade de conflito deve ser inferior ao risco aceitável.
Entropia: «Quantos acidentes» contém ID (bit).
Ordenabilidade (time-sortable/k-sortable): Ordenação vocabulária ≈ horária.
Monotonia: seqüência não decrescente dentro do nó/fluxo.
Local da gravação: quanto a nova inserção se concentra na «cauda» do índice (perigo de páginas quentes).
Previsividade: você pode adivinhar o ID próximo (importante para a segurança/API).
Apresentação: binário/linha, Base16/32/ 36/58/64, defeitos, maiúsculas.
3) Famílias principais de identificadores
3. 1 UUID
v4 (random): 122 bits de entropia. Desordenado, bom para segurança e simplicidade. Menos: os índices são «caídos» devido à distribuição aleatória - o que, no entanto, dissipa as cargas e remove as «páginas quentes» de forma uniforme.
v1 (time + MAC): organizamos, mas leva MAS/tempo (privacidade); Muitas vezes evitam.
v7 (time-ordered): tempo milissegundo + random. Design sob ordem lexicográfica por tempo e boa compressão em BD. Compromisso: A cauda quente do índice aparece; É tratado com charding/prefixo/encarnação.
Dicas
Para APIs externas e exigências de ordem não altas - v4.
Para BB de evento/laje e chaves «ordenadas» - v7.
3. 2 ULID (Crockford Base32)
128 bits: 48 bits de tempo (ms) + 80 bits de casualidade. Lexicografado por tempo, homem amigável (sem 'I, L, O, U'), URL safe. Há uma variação monótona (com a mesma marca de tempo, a parte aleatória aumenta).
Os benefícios são leitura, ordenabilidade, portabilidade.
Contras: A uma taxa de inserção muito alta em um momento é «cauda quente».
3. 3 KSUID
160 bits: 32 bits de tempo (segundos) em relação à era + 128 bits de casualidade. Maior intervalo de tempo e estabilidade de triagem, linhas ULID mais curta? (não - mais longo, mas com sua codificação), é bom para logs e objetos distribuídos.
3. 4 Snowflake-similares (k-sortable flake IDs)
Padrão clássico (personalizável):
[ timestamp bits ][ region/datacenter bits ][ worker bits ][ sequence bits ]
Propriedades: crescimento monótono no nó, exclusividade quase global, representação binária curta (64 bits).
Riscos: dependência de relógios (deriva/regressão do tempo), esgotamento sequence em uma única tecla, coordenação de bits region/worker.
Tratamento: proteção contra «clock back», reserva sequence, detector de tempo, PTP/NTP disciplina.
3. 5 Sequências de BD (SEQUENCE/IDENTITY)
Geração monótona mais simples em um DBD/deque.
Benefícios: curto, rápido, conveniente para tabelas locais.
Contras: difícil globalmente em um cluster distribuído; previsivelmente (não seguro como chave pública), cria uma cauda quente de índice.
3. 6 ID de endereço de conteúdo (hash conteúdo)
SHA-256/Blake3 do conteúdo → ID estável, dedução, verificação de integridade, armazenamento em dinheiro.
Mais, determinismo, protecção contra trocas.
Contras: geração cara (CPU), colisões de zeros práticos, sem triagem temporal, comprimento.
4) Conflitos e «aniversário paradoxal» (intuitivo)
A probabilidade de conflito para uma ID aleatória de tamanho 'b' bit 'nas gerações' n 'é próxima:
p ≈ 1 - exp (-n (n-1 )/2/2 ^ b) ≈ n ^ 2/2 ^ (b + 1) (for small p)
Exemplos:
- UUIDv4 (122 bits) a n = 10 ^ 12 (trilhão) → p £1e-14 (desrespeitado).
- O rand de 64 bits → a n = 10 ^ 9 já p £0. 027 (risco notável).
- Conclusão: 64 bits aleatórios muitas vezes são escassos para sistemas enormes; use 96/128 bits.
5) Índices, páginas quentes e armazenamento
As chaves aleatórias (v4) distribuem as inserções de forma uniforme na árvore do índice → não há «cauda», mas são piores do que a localidade em dinheiro.
O tempo de arrumação (v7/ULID/Snowflake) é inserido «na cauda» → melhor localidade e compressão, mas o risco de páginas quentes sob alto registro paralelo.
- prefixo/charding por tenant/region (adicionar 1-2 bytes antes do tempo);
- interleaving: parte do acidente em bits mais velhos;
- inserção de batch, felfator na árvore B, saída automática em BRIN/clusterização para grandes logs.
- 'UUID (16B)' vs 'BIGINT (8B) '/' INT8' economiza memória/dinheiro; as linhas Base32/58/64 aumentam o tamanho entre 20% e 60%. Para um BD, guarde binário, serigrafe em uma linha na borda.
6) Segurança e privacidade
Não use SEQUENCE/INT como ID público no URL/API: adivinhado → alocação de recursos.
Adicione ID random e imprevisível (v4/v7/ULID/KSUID) para links externos.
Não codifique PII no ID. Se você desejar incluir um atributo, criptografe/assine (por exemplo, JWE/JWS) ou use tocadores opacos.
Codificações seguras URL: Base32 Crockford, Base58 (sem '0OIl'), Base64url.
7) Multi-tenência, prefixados e rotação
Formato: '[TENANT _ PREFERIX] - [ID]' ou binário: 'tenant _ id | | id'.
Vantagens: filtros rápidos/partituras por locador, proteção contra N + 1 raias.
Contras: Pode piorar a densidade da entropia nos bits mais velhos → pensar na distribuição (hesh prefixo).
O sufixo hash (2-3 bytes) reduz os conflitos e ajuda o routing shard = hash (id)% N '.
8) Recomendações práticas de escolha
API, links públicos, serviços distribuídos sem ordem máxima: UUIDv4, ULID/KSUID.
Logs/eventos/pedidos, onde muitas vezes ordenamos por tempo, UUIDv7 ou ULID (monótono).
Banda de banda ultrarrápida com monotonia local e chave curta: Snowflake-semelhante a 64 bits (precisa de disciplina de tempo).
Armazéns de artefatos/bildos/blobs: conteúdo-endereçado (SHA-256) e acima de homem-amigável «vitrine» (Hasids/link).
Tabelas locais de um BD: SEQUENCE/IDENTITY + «embrulho» externo para links públicos (masking).
9) Implementações e exemplos
9. 1 PostgreSQL
Guarde o UUID binário, os índices são 'btree' ou 'hash' por necessidade.
sql
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE TABLE orders (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- или uuid_generate_v4()
created_at timestamptz NOT NULL DEFAULT now(),
tenant smallint NOT NULL
);
-- For time-sortable (UUIDv7) store binary (uuid), generation in the application.
-- If you want a cluster by time:
CREATE INDEX ON orders (created_at DESC);
Sequential hot fix: para time-sorted ID, adicione «sal» em batidas seniores ou particione por tenant:
sql
CREATE TABLE orders_t1 PARTITION OF orders FOR VALUES IN (1);
CREATE TABLE orders_t2 PARTITION OF orders FOR VALUES IN (2);
9. 2 Redis (contadores atômicos/monutonia)
bash
INCR "seq: orders" # local sequence combine: epoch_ms<<20 (worker_id<<10) (seq & 1023)
9. 3 Snowflake-gerador semelhante (pseudocode)
pseudo const EPOCH = 1704067200000 # custom epoch (ms)
state: last_ms=0, seq=0, worker=7, region=3
next():
now = epoch_ms()
if now < last_ms: wait_until(last_ms) # защита от clock back if now == last_ms:
seq = (seq + 1) & ((1<<12)-1) # 12 бит if seq == 0: wait_next_ms()
else:
seq = 0 last_ms = now return (now-EPOCH)<<22 region<<17 worker<<12 seq
9. 4 ULID/UUID em aplicativos
Go
go
// ULID t:= time. Now(). UTC()
entropy:= ulid. Monotonic(rand. New(rand. NewSource(t. UnixNano())), 0)
id:= ulid. MustNew(ulid. Timestamp(t), entropy)
//UUID v7 (if there is a library)
id:= uuid. Must(uuid. NewV7())
Node. js
js import { ulid } from 'ulid';
import { v4 as uuidv4 } from 'uuid';
const id1 = ulid();
const id2 = uuidv4(); // v4
Python
python import uuid, time id_v4 = uuid. uuid4()
For v7, use a library (for example, uuid6/7 third-party packages)
10) Codificações e apresentações
Binário em BD ('BYTEA', 'UUID') → compacto e rápido. Na borda, converta para:- Base32 Crockford (ULID): insensível a maiúsculas, sem caracteres visualmente semelhantes.
- Base58: mais curto que o Base32/64 para tokens humanos, URL-safe.
- Base64url: curto, mas 'e' _ 'no URL.
Estabilize a maiúscula e o formato (defeitos/ausências) para evitar duplicações na comparação de linhas.
11) Testes playbooks e observação
Conflitos: métrica 'id _ collision _ total' (deve ser 0), alert a> 0.
Distribuição de prefixo: histograma de bytes seniores - Procurando compras.
Taxa de geração: 'ids _ per _ sec', p99 latência do gerador.
Clock skew (para Snowflake): off-set, eventos «clock went back».
Caudas de índice p95/p99 'INSERT' latency; porcentagem de bloqueios/páginas quentes.
- A injecção «clock draft/back» → convencemo-nos de que o gerador está esperando/mudando.
- Congestionamento de 'sequence' em milissegundos → verificação de espera next _ ms.
- Paralelismo em massa → se não há tempestades de bloqueio no índice.
12) Anti-pattern
AUTO _ INCREMENT/SEQUENCE como um ID público: adivinhado, vazamento. Use um ID opaco público acima do interno.
UUIDv1 (MAS/tempo) para fora: privacidade.
64 bits de ID aleatório por trilhões de registros, risco real de conflito.
Gerador central global sem HA, SPOF e estreito.
Time-sorted IDs sem proteção contra clock back: duplicados/regredidos de ordem.
Misturar diferentes formatos de ID sem uma versão explícita/prefixo → o caos no debag/migração.
Salvar ID como linhas com diferentes maiúsculas/formas → duplicados ocultos.
13) Folha de cheque de implementação
- O formato selecionado (v4/v7/ULID/KSUID/Snowflake/SEQ/hash) para os requisitos de domínio.
- Os requisitos de ordem foram definidos (se a triagem é necessária).
- A probabilidade de conflito (b bits, n gerações) foi avaliada e um limite de risco foi definido.
- Codificação projetada (binário em BD + vitrine humana).
- Para time-sorted - proteção contra clock back, sequence-limite e NTP/PTP disciplina.
- Para ID público - imprevisibilidade (rand/ULID/KSUID), falta de PII.
- Traçado shard routing (hash (id)% N), prefixados multi-tenentes.
- Observabilidade: métricas de conflito, distribuição, atrasos, clock skew.
- Mala de teste de congestionamento sequence/alta concorrência/comprimento da janela.
- Documentação de formato, versão, época, marcação de bits e plano de migração.
14) FAQ
Q: O que escolher «padrão» para microsséries?
A: UUIDv7 ou ULID: ordenabilidade em tempo, muita entropia, simples geração na borda. Para API externo - ULID/UUIDv4 também.
Q: Você precisa de um ID curto e humano.
A: ULID/KSUID ou Base58 codificação de 128 bits de ID aleatória/temporária. Lembre-se do comprimento e dos conflitos.
Q: É possível fazer um ID «curto», mas seguro?
A: Sim: guarde o SEQ interno, e dê para fora o token opaque (rand 96-128 bits) ou o Hasids com sal + assinatura.
Q: Como migrar da SEQ para UUIDv7?
A: Digite a nova coluna «id _ new» (UUID), anote-se, publica links para o novo ID, então altere o RC/chaves externas e remova o antigo.
Por que as minhas inserções com a ULID se tornaram quentes?
A: Coloque chaves estritamente crescentes no mesmo índice. Divida em partituras/tenant, misture batidas mais velhas, use inserções batch.
15) Resultado
Uma boa ID é um conjunto correto de propriedades sob a tarefa: entropia suficiente, triagem previsível (se necessário), publicidade segura e exploração saudável de índices. Escolha UUIDv4/ULID/UUIDv7/KSUID para simplicidade e distribuição, Snowflake para monotonia apertada e chaves curtas (com tempo livre), seqüência para tabelas locais, hash conteúdo para artefatos. Coloque a observação e os testes, e os identificadores deixarão de ser uma fonte de surpresas.