qwick

package module
v0.0.0-...-c93e72b Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 16, 2026 License: MIT Imports: 19 Imported by: 0

README

Qwick (Static On-Disk ART)

Qwick — это высокопроизводительное KV-хранилище на языке Go, оптимизированное для чтения. Оно использует адаптивное дерево остатков (ART) для индексации и memory-mapped файлы (mmap) для мгновенного доступа к данным с нулевыми аллокациями по горячему пути.

Основные возможности
  • Нулевые аллокации при чтении: Данные возвращаются в виде срезов []byte, указывающих прямо в отображенный в память файл.
  • Быстрая индексация: Использует ART (Adaptive Radix Tree) для эффективного поиска.
  • Встроенная компрессия: Поддержка zstd и s2 (snappy) для экономии места на диске.
  • Простота: Минималистичный API.
Установка
go get github.com/globalmac/qwick
Использование
1. Запись (сборка базы)

Для создания базы используется art.Tree для индексации в памяти, после чего дерево записывается в бинарный файл.

package main

import (

	"encoding/json"
	"fmt"
	
	"github.com/globalmac/qwick"
)

func main() {
	// Создаем новое дерево
	tree := qwick.New()

	// Вставляем данные
	tree.Insert([]byte("user:1"), []byte("Alice"))
	tree.Insert([]byte("user:2"), []byte("Bob"))
	tree.Insert([]byte("admin:1"), []byte("Charlie"))

	// Сохраняем в файл
	if err := qwick.Build(tree, "users.qwick"); err != nil {
		panic(err)
	}
	fmt.Println("База данных успешно создана!")
	
	// или 1 000 000 тестовых записей

    type User struct {
      ID   uint64   `json:"id"`
      Name string   `json:"name"`
      Tags []string `json:"tags"`
    }

    for i := 1; i <= 1_000_000; i++ {
		
      strID := fmt.Sprintf("%d", i)
  
      user := User{
        ID:   uint64(i),
        Name: "username_" + strID,
        Tags: []string{"go" + strID, "db" + strID, "json" + strID},
      }
  
      bin, _ := json.Marshal(user)
      tree.Insert([]byte(strID), bin)
  
      if i%100_000 == 0 {
        fmt.Printf("Inserted: %d\n", i)
      }
	  
    }

    fmt.Println("База данных 1 000 000 строк - успешно создана!")
	
}
2. Чтение данных

Чтение происходит мгновенно через mmap. Поддерживается два способа получения значения: GetRaw (без аллокаций, возвращает ссылку на данные в mmap) и Find (с распаковкой, если база сжата).

package main

import (
	
	"encoding/json"
	"fmt"
	
	"github.com/globalmac/qwick"
)

func main() {
	// Открываем существующую базу
	db, err := qwick.Open("users.qwick")
	if err != nil {
		panic(err)
	}
	defer db.Close()

	// Быстрое получение сырых данных (без аллокаций)
	if val, ok := db.GetRaw([]byte("user:1")); ok {
		fmt.Printf("GetRaw: %s\n", val)
	}

	// Получение данных с поддержкой декомпрессии (если использовалось сжатие)
	dst := make([]byte, 1024)
	if val, ok, err := db.Find([]byte("user:2"), dst); ok && err == nil {
		fmt.Printf("Find: %s\n", val)
	}
	
	// или с JSON Unmarshal
	
    type User struct {
      ID   uint64   `json:"id"`
      Name string   `json:"name"`
      Tags []string `json:"tags"`
    }
	
    // Получение данных с поддержкой декомпрессии (если использовалось сжатие)
    if val, ok, err := db.Find([]byte("123"), dst); ok && err == nil {
      var user User
      _ = json.Unmarshal(val, &user)
      fmt.Println("Find: ", user.ID, user.Name)
    }
      
}
3. Поиск по префиксу

Благодаря ART-индексу, поиск по префиксу выполняется крайне эффективно. Доступно две версии метода: PrefixRaw для получения сырых данных и Prefix для получения распакованных данных.

package main

import (
	  "fmt"
	
	  "github.com/globalmac/qwick"
)

func main() {
  db, _ := qwick.Open("users.qwick")
  defer db.Close()

  fmt.Println("Поиск всех пользователей (с распаковкой):")
  dst := make([]byte, 1024)
  db.Prefix([]byte("user:"), dst, func(key, val []byte) bool {
    fmt.Printf(" - %s: %s\n", key, val)
    return true // true для продолжения итерации, false для остановки
  })

  fmt.Println("Поиск всех пользователей (сырые данные, без аллокаций):")
  db.PrefixRaw([]byte("user:"), func(key, val []byte) bool {
    fmt.Printf(" - %s: %d bytes\n", key, len(val))
    return true
  })
}
4. Продвинутая сборка (Сжатие)

Вы можете настроить алгоритм сжатия и другие параметры при сборке базы.

package main

import (
	"github.com/globalmac/qwick"
)

func main() {
	tree := qwick.New()
	// ... вставка данных ...

	opts := qwick.BuildOptions{
		Compression: 1,    // 1: Zstd, 2: S2, 3: None
		ZstdLevel:   3,    // Уровень сжатия для Zstd
		SizeCutover: 128,  // Не сжимать значения меньше 128 байт
	}

	qwick.BuildWithOptions(tree, "compressed.qwick", opts)
}
5. Дополнительное сжатие + шифрование (S2 + AES-256-CTR + Poly1305)

Для чувствительных и больших БД, Вы можете использовать сжатие S2 + AES-256-CTR + Poly1305

package main

import (
  "crypto/rand"
  "encoding/base64"
  "fmt"
  "log"

  "github.com/globalmac/qwick"
)

func main() {

  key := make([]byte, 32)
  rand.Read(key)
  keyRaw := base64.RawStdEncoding.EncodeToString(key)

  fmt.Println("Ключ:", keyRaw)

  // 1. Шифруем (сжатие s2 + шифрование AES-CTR + аутентификация Poly1305)
  err := qwick.ZipEncrypt("file.qwick.enc", "file.qwick", key)
  if err != nil {
    log.Fatalf("ZipEncrypt ошибка: %v", err)
  }

  keyPlain, _ := base64.RawStdEncoding.DecodeString(keyRaw)

  // 2. Расшифровываем (проверка целостности + дешифрование + декомпрессия)
  err = qwick.UnzipDecrypt("file.qwick", "file.qwick.enc", keyPlain)
  if err != nil {
    log.Fatalf("UnzipDecrypt ошибка: %v", err)
  }

}

Documentation

Index

Constants

View Source
const (
	FileMagic   = "QWICK\xAB\xCD\xEF"
	FileVersion = 1
)

Константы формата файла QWICK

Variables

This section is empty.

Functions

func Build

func Build(tree art.Tree, path string) error

Build — обёртка над BuildWithOptions с параметрами по умолчанию.

func BuildWithOptions

func BuildWithOptions(tree art.Tree, path string, opts BuildOptions) error

BuildWithOptions сериализует ART дерево в файл с заданными опциями.

func New

func New() art.Tree

New создаёт новое адаптивное радикс-дерево (ART) в памяти.

func UnzipDecrypt

func UnzipDecrypt(dstPath, srcPath string, masterKey []byte) error

UnzipDecrypt расшифровывает и распаковывает файл srcPath, записывая результат в dstPath с использованием masterKey.

func ZipEncrypt

func ZipEncrypt(dstPath, srcPath string, masterKey []byte) error

ZipEncrypt сжимает и шифрует файл srcPath, записывая результат в dstPath с использованием masterKey. Входной файл читается через mmap для максимальной производительности.

Types

type BuildOptions

type BuildOptions struct {
	Compression uint32 // 0=auto, 1=zstd, 2=s2
	ZstdLevel   int    // 1..3 уровни скорости
	SizeCutover int    // порог выбора между s2 и zstd для режима auto
}

BuildOptions управляет настройками компрессии при сборке базы.

type MMAPDB

type MMAPDB struct {
	// contains filtered or unexported fields
}

MMAPDB представляет собой базу данных с доступом через memory-mapped file (только для чтения).

func Open

func Open(path string) (*MMAPDB, error)

Open открывает базу данных из указанного пути.

func (*MMAPDB) Close

func (db *MMAPDB) Close() error

Close закрывает базу данных и освобождает mmap.

func (*MMAPDB) Find

func (db *MMAPDB) Find(key []byte, dst []byte) ([]byte, bool, error)

Find возвращает распакованное значение в dst.

func (*MMAPDB) GetRaw

func (db *MMAPDB) GetRaw(key []byte) ([]byte, bool)

Get выполняет поиск ключа и возвращает сырые данные (указывает прямо в mmap).

func (*MMAPDB) Prefix

func (db *MMAPDB) Prefix(prefix []byte, dst []byte, cb func(key, val []byte) bool) error

Prefix похож на PrefixRaw, но распаковывает значения.

func (*MMAPDB) PrefixRaw

func (db *MMAPDB) PrefixRaw(prefix []byte, cb func(key, val []byte) bool)

PrefixRaw перебирает все ключи, начинающиеся с prefix.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL