---
id: f5707f0f4f230a276a3428b1eb1e6469
author: Lemon Mint
title: 'Schottky: Zero-Allocation, Order-Preserving Byte-Key Encoding for Go'
description: Go-bibliotek uden allokeringer, der koder sammensatte tupler til byte-nøgler sorteret med memcmp, med uafhængig ASC/DESC-sortering, NULL-placeringsmarkører og præfiksscanningsgrænser.
language: da
date: 2026-08-31T07:47:42.614875839Z
path: /schottky
go_package: gosuda.org/schottky
go_repourl: https://github.com/gosuda/schottky.git
category: database
---

Når der implementeres LSM-træ- eller B-træbaserede nøgle-værdi-lagre og databaseindekser, skal sammensatte felter ofte kombineres til en enkelt byte-nøgle.

Standardiserede serialiseringsformater såsom JSON eller Protocol Buffers er ikke designet til dette formål, fordi deres serialiserede byte-output ikke bevarer den naturlige sorteringsrækkefølge, som kræves af usignerede bytevise sammenligninger (`bytes.Compare` eller `memcmp`).

Schottky er et Go-bibliotek designet til at kode sammensatte tupler af flere typer til ordensbevarende byte-nøgler.

```bash
go get gosuda.org/schottky@latest
```

### Serialisering vs. sorteringsnøgler

Sikring af korrekt bytevis sortering kræver håndtering af flere lavniveau-detaljer i datarepræsentationen:

- **Heltal**: Standard big-endian-kodning i totalkomplement bryder den naturlige orden på grund af det mest betydende fortegnsbit. Invertering af fortegnsbittet er nødvendig for korrekte usignerede bytesammenligninger.
- **Flydende kommatal**: Kræver justeringer af fortegnsbittet, invsome rækkefølge for negative værdier og konsistent håndtering af `NaN` og `-0`.
- **Strenge og byte-slices med variabel længde**: Feltgrænser skal bevares uden at bryde præfiks-sorteringsrækkefølger.
- **Krav til sammensatte nøgler**: Understøttelse af uafhængig ASC/DESC-sortering pr. felt, afkoblede NULLS FIRST/LAST-regler, streng leksikografisk prioritet (tidligere felter bestemmer rækkefølgen) og kompatibilitet med præfiksscanninger.

Schottky konverterer hver værdi til en kanonisk nyttelast, før præsenstags og retningsorientering anvendes. For DESC-felter inverteres hver byte i ASC-nyttelasten bitvist (`^b`). NULL-placering håndteres via dedikerede præsenstags og fungerer uafhængigt af sorteringsretningen.

### Grundlæggende brug

Følgende eksempel bygger en sammensat nøgle bestående af et konto-ID (`ASC, NULLS LAST`) og et navn (`DESC, NULLS FIRST`):

```go
package main

import (
        "fmt"

        "gosuda.org/schottky"
)

func main() {
        storage := make([]byte, 0, 128)
        builder := schottky.NewBuilder(storage)

        builder.Int64(42, schottky.AscNullsLast)
        accountPrefixLen := builder.Len()

        builder.String("Ada", schottky.DescNullsFirst)
        key, err := builder.Key()

        if err != nil {
                panic(err)
        }
        accountPrefix := key[:accountPrefixLen]
        fmt.Printf("key=%x\nprefix=%x\n", key, accountPrefix)
}
```

Schottky tilbyder fire eksplicitte sorteringskonfigurationer:

- `AscNullsFirst`
- `AscNullsLast`
- `DescNullsFirst`
- `DescNullsLast`

NULL-positionering udledes aldrig implicit. Hvis en ugyldig `Order`-værdi sendes, registrerer builderen `ErrInvalidOrder`, som returneres ved kald af `Key()` eller `Err()`.

### Præfiksscanning og intervalgrænser

Schottky-sammensatte nøgler indeholder ingen globale headere, feltantalsmetadata, typemarkører eller afsluttere. Forudsat at skemaet er kendt på forhånd, sammenkædes feltkodninger blot.

På grund af denne layout danner de kodede bytes fra førende felter et gyldigt præfiks for intervalscanninger. I eksemplet ovenfor kan `accountPrefix` direkte anvendes som præfiksfilter til at scanne alle poster, hvor `Account ID == 42`.

For at beregne den eksklusive øvre grænse for halvåbne `[prefix, upper)`-intervalscanninger anvendes `PrefixUpperBound`:

```go
upperStorage := make([]byte, 0, len(accountPrefix))
upper, finite, err := schottky.PrefixUpperBound(upperStorage, accountPrefix)

if err != nil {
        panic(err)
}

if finite {
        // Halvåbent [accountPrefix, upper) intervalscanning
} else {
        // Ubegrænset åbent intervalscanning
}
```

_Bemærk: `Builder.Len()` skal måles ved rene feltgrænser. Udskæring inde i et felts interne bytestrøm producerer et ugyldigt præfiks._

### Nul-allokering og bufferhåndtering

Nøglegenerering kører ofte på kritiske databasestier. For at eliminere heap-allokeringer og overhead ved buffergenstørrelse arbejder `Builder` strengt inden for kapaciteten af den slice, som kaldes leverer, og omallokerer ikke internt.

Hvis bufferen løber tør for kapacitet, registreres `ErrShortBuffer` uden at skrive delvise bytes. Feltskrivninger er atomare, og den første fejl, der opstår, bevares indtil den kontrolleres via `Key()` eller `Err()`. At sikre tilstrækkelig kapacitet på forhånd garanterer nul-allokeringskodning.

Bufferstørrelser kan beregnes på forhånd ved hjælp af hjælpefunktioner såsom `EncodedBytesSize`, `EncodedStringSize` og `EncodedDecimalSize`, eller via faste konstanter. Den returnerede nøgle refererer direkte til den angivne buffer, og hukommelsens livscyklus overlades til kalderen.

`Decoder` fungerer symmetrisk: den låner direkte fra inputnøglen, kræver kalderleverede destinationsbuffere for felter med variabel længde og tilbyder `Remaining() == 0` til at registrere efterfølgende bytes eller skemamismatch.

### Understøttede datatyper

- **Heltal**: Signerede og usignerede (8-bit til 64-bit), `Int128`
- **Flydende kommatal og numeriske værdier**: `Float32`, `Float64`, decimal tekst
- **Grundlæggende typer**: Binær streng, byte-slice, boolean, enum-rang
- **Dato og tid**: Dato, tid, zonet tid, tidsstempel, varighed, kalenderinterval
- **Netværk og identifikatorer**: UUID, MAC, IP, IP-præfiks, kanonisk netværkspræfiks, LSN
- **Sammensatte strukturer**: Indlejrede tupler, intervaller og rå strukturelle kodninger
- **Kollation**: Unicode-kollationsnøgler og eksterne kanoniske tokens

SQL-typemapping er tilpasset PostgreSQL 18 B-træ-sorteringsregler. Typer, der afhænger af databasekataloger eller interne motortilstande, håndteres ved at sende eksterne kanoniske tokens.

### Strengkollation

`Builder.String` anvender som standard rå UTF-8-binær orden. Til lokalesensitiv sortering tilbyder Schottky en samtidighedssikker, uforanderlig `Collator`:

- **Deterministisk kollation**: Kodes kollationsnøglen sammen med rå UTF-8-bytes for at fungere som udligningsregel, når kollationsvægte er identiske.
- **Ikke-deterministisk kollation**: Behandler kollationsligestillede strenge som identiske og udelader den rå byte-udligningsregel.

Unicode- og profilversioner bør registreres i metadataskemaet. Hvis kollationsudbydere eller profilindstillinger ændres, skal eksisterende nøgler genopbygges.

### Skemahåndtering

Fordi Schottky-nøgler er rå byte-sekvenser uden headere, skal skemalaget spore:

1. Feltsekvens og datatyper.
2. Sorteringsretninger (`ASC`/`DESC`) og NULL-rækkefølge (`NULLS FIRST`/`LAST`).
3. Strengkollation og normaliseringsregler.
4. Schottky- og kollationsprofilversioner.

Sammenligning af nøgler genereret med forskellige skemaer, eller afkodning mod et uoverensstemmende skema, bryder rækkefølgegarantierne.

### Ydeevne og links

På Go 1.27+ kan eksperimentel bærbar SIMD-acceleration aktiveres ved hjælp af `GOEXPERIMENT=simd`. Skalare og SIMD-stier producerer byte-identiske nøgler.

- **GitHub-repository**: https://github.com/gosuda/schottky
- **Specifikation af nøglelayout**: https://github.com/gosuda/schottky/blob/main/docs/03-key-layout.md
- **Guide til SQL-typemapping**: https://github.com/gosuda/schottky/blob/main/docs/17-sql-type-map.md
- **Go API-reference**: https://github.com/gosuda/schottky/blob/main/docs/18-api.md
