---
id: f5707f0f4f230a276a3428b1eb1e6469
author: Lemon Mint
title: 'Schottky: Zero-Allocation, Order-Preserving Byte-Key Encoding pro Go'
description: Knihovna pro jazyk Go bez alokací paměti, která kóduje složené n-tice do bajtových klíčů uspořádaných podle memcmp, s nezávislým řazením ASC/DESC, značkami pro umístění hodnot NULL a mezemi pro prefixové skenování.
language: cs
date: 2026-08-31T07:47:42.614875839Z
path: /schottky
go_package: gosuda.org/schottky
go_repourl: https://github.com/gosuda/schottky.git
category: database
---

Při implementaci úložišť typu klíč-hodnota založených na LSM-stromech nebo B-stromech a databázových indexů je často nutné kombinovat složená pole do jediného bajtového klíče.

Standardní serializační formáty, jako jsou JSON nebo Protocol Buffers, nejsou pro tento účel navrženy, protože jejich serializované bajtové výstupy nezachovávají přirozené řazení vyžadované neznaménkovým bajtovým porovnáním (`bytes.Compare` nebo `memcmp`).

Schottky je knihovna pro jazyk Go navržená pro kódování vícetypových složených n-tic do bajtových klíčů zachovávajících pořadí.

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

### Serializace versus klíče pro řazení

Zajištění správného bajtového řazení vyžaduje řešení několika nízkoúrovňových detailů reprezentace dat:

- **Celá čísla**: Standardní kódování dvojkového doplňku v big-endian narušuje přirozené řazení kvůli nejvýznamnějšímu znaménkovému bitu. Pro správná neznaménková bajtová porovnání je nezbytné invertovat znaménkový bit.
- **Čísla s plovoucí desetinnou čárkou**: Vyžadují úpravy znaménkového bitu, obrácené řazení záporných hodnot a konzistentní zpracování hodnot `NaN` a `-0`.
- **Řetězce a bajtové řezy proměnné délky**: Hranice polí musí být zachovány bez narušení prefixového řazení.
- **Požadavky na složené klíče**: Podpora nezávislého řazení ASC/DESC pro každé pole, oddělená pravidla NULLS FIRST/LAST, striktní lexikografická priorita (dřívější pole určují pořadí) a kompatibilita s prefixovým skenováním.

Schottky převádí každou hodnotu na kanonický datový blok před aplikací značek přítomnosti a směrové orientace. U polí DESC je každý bajt payloadu ASC bitově invertován (`^b`). Umístění NULL je řešeno pomocí vyhrazených značek přítomnosti a nezávisí na směru řazení.

### Základní použití

Následující příklad vytváří složený klíč sestávající z ID účtu (`ASC, NULLS LAST`) a jména (`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 poskytuje čtyři explicitní konfigurace směru řazení:

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

Pozice NULL se nikdy neodvozuje implicitně. Pokud je předána neplatná hodnota `Order`, builder zaznamená `ErrInvalidOrder`, která je vrácena při volání `Key()` nebo `Err()`.

### Prefixové skenování a hranice rozsahu

Složené klíče Schottky neobsahují žádné globální hlavičky, metadata o počtu polí, typové značky ani zápatí. Za předpokladu, že schéma je známo předem, se kódování polí jednoduše zřetězí.

Vzhledem k tomuto uspořádání tvoří zakódované bajty vedoucích polí platný prefix pro rozsahové skeny. Ve výše uvedeném příkladu lze `accountPrefix` přímo použít jako prefixový filtr pro skenování všech záznamů, kde `ID účtu == 42`.

Pro výpočet exkluzivní horní hranice pro polouzavřené rozsahové skeny `[prefix, upper)` použijte `PrefixUpperBound`:

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

if err != nil {
        panic(err)
}

if finite {
        // Polouzavřený rozsah skenování [accountPrefix, upper)
} else {
        // Neohraničený otevřený rozsah skenování
}
```

_Poznámka: `Builder.Len()` musí být měřeno na čistých hranicích polí. Řezání uvnitř vnitřního bajtového toku pole produkuje neplatný prefix._

### Nulová alokace a správa bufferů

Generování klíčů často probíhá na kritických databázových cestách. Aby se eliminovaly alokace na haldě a režie spojená se změnou velikosti bufferu, `Builder` pracuje striktně v rámci kapacity řezu poskytnutého volajícím a interně nealokuje znovu.

Pokud buffer vyčerpá kapacitu, zaznamená se `ErrShortBuffer` bez zápisu částečných bajtů. Zápisy polí jsou atomické a první zjištěná chyba je uchována až do kontroly přes `Key()` nebo `Err()`. Poskytnutí dostatečné kapacity předem zajišťuje kódování bez alokací.

Velikosti bufferů lze předem vypočítat pomocí pomocných funkcí, jako jsou `EncodedBytesSize`, `EncodedStringSize` a `EncodedDecimalSize`, nebo pomocí pevných konstant. Vrácený klíč odkazuje přímo na poskytnutý buffer a správa životního cyklu paměti zůstává na volajícím.

`Decoder` funguje symetricky: vypůjčuje si data přímo ze vstupního klíče, pro pole proměnné délky vyžaduje cílové buffery poskytnuté volajícím a poskytuje `Remaining() == 0` pro detekci přebytečných bajtů nebo neshod schématu.

### Podporované datové typy

- **Celá čísla**: Se znaménkem i bez znaménka (8 až 64 bitů), `Int128`
- **Čísla s plovoucí desetinnou čárkou a numerické typy**: `Float32`, `Float64`, desetinný text
- **Základní typy**: Binární řetězec, bajtový řez, Boolean, Enum Rank
- **Datum a čas**: Date, Time, Zoned Time, Timestamp, Duration, Calendar Interval
- **Síť a identifikátory**: UUID, MAC, IP, IP Prefix, Canonical Network Prefix, LSN
- **Složené struktury**: Vnořené n-tice, Ranges a surová strukturální kódování
- **Kolace**: Unicode Collation Keys a externí kanonické tokeny

Mapování typů SQL je sladěno s pravidly řazení B-stromů PostgreSQL 18. Typy závislé na databázových katalozích nebo na interním stavu enginu se zpracovávají předáním externích kanonických tokenů.

### Řazení řetězců

`Builder.String` ve výchozím nastavení používá řazení surového UTF-8 binárně. Pro řazení respektující locale poskytuje Schottky souběžně bezpečný a neměnný `Collator`:

- **Deterministická kolace**: Kóduje klíč kolace spolu se surovými bajty UTF-8, aby poskytla rozhodovací kritérium v případě shodných vah kolace.
- **Nedeterministická kolace**: Považuje řetězce shodné podle kolace za identické a vynechává rozhodovací kritérium na úrovni surových bajtů.

Verze Unicode a profilu by měly být evidovány v metadatech schématu. Pokud se změní poskytovatelé kolace nebo nastavení profilu, je nutné existující klíče znovu sestavit.

### Správa schématu

Protože klíče Schottky jsou surové bajtové sekvence bez hlaviček, musí vrstva schématu evidovat:

1. Sekvenci polí a datové typy.
2. Směry řazení (`ASC`/`DESC`) a řazení NULL (`NULLS FIRST`/`LAST`).
3. Pravidla kolace a normalizace řetězců.
4. Verze profilů Schottky a kolace.

Porovnávání klíčů vygenerovaných s různými schématy nebo dekódování proti neodpovídajícímu schématu narušuje záruky řazení.

### Výkon a odkazy

Na Go 1.27+ lze experimentální přenosnou akceleraci SIMD povolit pomocí `GOEXPERIMENT=simd`. Skalární a SIMD cesty produkují bajtově identické klíče.

- **Repozitář na GitHubu**: https://github.com/gosuda/schottky
- **Specifikace rozložení klíčů**: https://github.com/gosuda/schottky/blob/main/docs/03-key-layout.md
- **Průvodce mapováním typů SQL**: https://github.com/gosuda/schottky/blob/main/docs/17-sql-type-map.md
- **Reference Go API**: https://github.com/gosuda/schottky/blob/main/docs/18-api.md
