
SQLite VFS с холодными JOIN-запросами из S3 менее чем за 100 мс + сжатие и шифрование на уровне страниц
turbolite — это SQLite VFS на Rust, который выполняет точечные запросы и объединения напрямую из S3 с холодной задержкой менее 250 мс.
Этот репозиторий представляет собой рабочую область Cargo с двумя крейтами:
turbolite — чисто Rust библиотека. SQLite VFS с сжатием на уровне страниц, шифрованием и распределением по уровням S3.turbolite-ffi — C FFI / загружаемое расширение + привязки к языкам (Python, Node.js, Go).Он также предлагает сжатие на уровне страниц (zstd) и шифрование (AES-256) для эффективности и безопасности в состоянии покоя, которые можно использовать отдельно от S3.
Экспериментально. turbolite находится в активной разработке и содержит ошибки. Будьте осторожны.
Объектное хранилище становится быстрым. S3 Express One Zone обеспечивает однозначные миллисекундные GET-запросы, а Tigris также чрезвычайно быстр. Разрыв между локальным диском и облачным хранилищем сокращается, и turbolite использует это.
Дизайн и название вдохновлены подходом turbopuffer к бескомпромиссной архитектуре с учётом ограничений облачного хранения. Первоначальной целью проекта было превзойти холодные старты Neon в 500 мс+. Цель достигнута.
Если у вас одна база данных на сервер, используйте том. turbolite исследует, как иметь сотни или тысячи баз данных (одна на арендатора, одна на рабочую область, одна на устройство), не использовать том для каждой из них и мириться с единственным источником записи.
turbolite поставляется как библиотека Rust, загружаемое расширение SQLite (.so/.dylib) и языковые пакеты для Python и Node.js, а также зависимости для Go через Github. Подходит любое S3-совместимое хранилище (AWS S3, Tigris, R2, MinIO и т. д.). Это стандартный VFS SQLite, работающий на уровне страниц, поэтому должно работать большинство функций SQLite: FTS, R-деревья, JSON, режим WAL и т. д.
turbolite является частью более широкой экосистемы hadb. Отдельный turbolite — это VFS для хранения с одним безопасным писателем; если вам нужна HA-выборка лидера с непрерывной репликацией WAL, используйте его через haqlite-turbolite, который поверх добавляет HaQLite и walrust. Этот HA-путь всё ещё очень экспериментален.
Если вы хотите внести вклад в turbolite или найти ошибки, создайте pull request или откройте issue.
| Запрос | Тип | Холодный (S3 Express) | Холодный (Tigris) |
|---|---|---|---|
| Post + user | point lookup + join | 86ms | 172ms |
| Profile | multi-table join (5 JOINs) | 251ms | 479ms |
| Who-liked | index search + join | 206ms | 302ms |
| Mutual friends | multi-search join | 19ms | 49ms |
| Indexed filter | covered index scan | 79ms | 88ms |
| Full scan + filter | full table scan | 476ms | 532ms |
1M постов / 100K пользователей (~1.5 ГБ хранится) без кэширования, каждый байт с S3. EC2 c5.2xlarge + S3 Express One Zone (та же зона доступности, ~4 мс задержки GET). Fly performance-8x + Tigris (~25 мс задержки GET). Оба: 8 выделенных vCPU, 16 ГБ RAM, 7 рабочих потоков предвыборки. См. Бенчмаркинг и Важность бэкенда хранения.
Бенчмарки организованы по уровню кэша (что уже есть на локальном диске при выполнении запроса):
| Уровень кэша | Что кэшировано | Что загружается с S3 | Когда это происходит |
|---|---|---|---|
| none | ничего | всё | Свежий старт, пустой кэш |
| interior | внутренние страницы B-дерева | страницы индекса + данных | Первый запрос после открытия соединения |
| index | внутренние + страницы индекса | только страницы данных | Нормальная работа turbolite |
| data | всё | ничего | Эквивалент локального SQLite |
interior — самый реалистичный холодный бенчмарк: внутренние страницы загружаются сразу при открытии соединения, поэтому к моменту выполнения первого запроса они уже кэшированы. Страницы индекса агрессивно предварительно загружаются в фоне при первом обращении и могут быть ещё не готовы.
100K строк, Fly.io performance-2x (выделенный vCPU, NVMe, IAD):
| Операция | SQLite | turbolite | Накладные расходы |
|---|---|---|---|
| Point lookup | 145K/s | 73K/s | 2.0x |
| Range scan | 8.8K/s | 8.3K/s | паритет |
| Full table scan | 56/s | 60/s | паритет |
| INSERT | 19K/s | 23K/s | паритет |
| UPDATE by PK | 40K/s | 27K/s | 1.5x |
| Batch INSERT (в транзакции) | 685K/s | 740K/s | паритет |
Точечные запросы имеют наибольшие накладные расходы на страницу (~2x). Всё остальное приближается к паритету или превосходит его. Архитектура кэша без блокировок означает, что параллельные чтения никогда не блокируют запись.
| После | Локально | S3 (RustFS в том же регионе) |
|---|---|---|
| 1K вставок | 19ms | 38ms |
| Пакет 10K | 17ms | 114ms |
| 1K обновлений | 9ms | 36ms |
Запись всегда на локальной скорости. Затраты на S3 возникают только при контрольной точке. Цифры с RustFS в том же регионе Fly (~2 мс RTT). S3 Express One Zone была бы сопоставима.
pip install turbolite
# Chunk 3
(No content provided in this chunk.)```python
import turbolite
conn = turbolite.connect("my.db", mode="s3",
bucket="my-bucket",
endpoint="https://t3.storage.dev")
conn.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)")
conn.execute("INSERT INTO users VALUES (1, 'alice', '[email protected]')")
conn.commit()
alice = conn.cursor().execute("SELECT * FROM users").fetchone()
print(alice[1])
>>> "alice"
Смотрите Установка для Node, Go, Rust, локального режима и использования загружаемого расширения .so напрямую
turbolite спроектирован с учётом ограничений S3, а не файловой системы. Каждое решение исходит из этой модели:
| Ограничение S3 | Последствие |
|---|---|
| Повторные запросы медленные | Сводите количество запросов к минимуму. Группируйте записи, агрессивно предварительно считывайте. |
| Пропускная способность — узкое место | Максимально используйте пропускную способность. |
| PUT и GET тарифицируются за операцию | GET на 64 КБ стоит столько же, сколько GET на 16 МБ. Оптимизируйте количество запросов, а не эффективность по байтам. |
| Объекты неизменяемы | Никогда не обновляйте на месте. Записывайте новые версии, меняйте указатель. Нет повреждения данных при частичной записи. |
| Хранилище дёшево | Не оптимизируйте под объём. Выделяйте с запасом, сохраняйте старые версии, пусть сборщик мусора уберёт их позже. |
turbolite добавляет слои интроспекции и косвенности между SQLite и S3, которые эффективно группируют, сжимают, отслеживают и извлекают страницы.
SQLite использует B-дерево и запрашивает по одной странице за раз. Он знает, что страница N находится по смещению N * page_size. И эти страницы распределены случайным образом по карте страниц для эффективного произвольного доступа. Но в S3 получение одной страницы за запрос означало бы тысячи потенциально случайных GET-запросов на каждый запрос.
Но страницы не одинаковы. SQLite имеет разные типы страниц. turbolite разделяет группы страниц по типу: внутренние страницы B-дерева, листовые страницы индекса и листовые страницы данных.