GoSuda

gosuda.org/sqlite3: Driver database/sql Drop-In yang Menjalankan Native C SQLite Melalui Shared Memory

By Lemon Mint
views ...

gosuda.org/sqlite3 adalah driver database/sql dengan CGO_ENABLED=0 untuk Go yang menjalankan amalgamasi C SQLite hulu 3.54.1 di dalam proses pekerja terisolasi melalui dering memori bersama HQQ. Driver ini terdaftar di bawah nama driver standar "sqlite3", mendukung parameter kueri DSN github.com/mattn/go-sqlite3, menyematkan biner server prafroses yang diatestasi SLSA untuk enam target OS dan arsitektur, serta menggunakan request pipelining dengan prefetching batch 64 KiB untuk mengungguli cgo dalam proses pada kueri banyak baris.

1go get gosuda.org/sqlite3@v0.3.3540100

Mengapa Driver Go SQLite Lainnya?

SQLite adalah salah satu perangkat lunak yang paling banyak diterapkan di dunia, namun menggunakannya dari Go secara historis memaksa para insinyur dihadapkan pada pilihan sulit antara kesederhanaan pembangunan dan fidelitas mesin.

Di satu sisi terdapat github.com/mattn/go-sqlite3, standar komunitas yang telah lama ada. Driver ini membungkus amalgamasi C SQLite resmi secara langsung melalui cgo. Anda mendapatkan perencana kueri C asli, semantik konkurensi WAL penuh, dan manajemen memori C yang dapat diprediksi. Biayanya adalah CGO_ENABLED=1: pembangunan lokal dan alur kerja CI melambat, citra kontainer minimal tanpa scratch atau distroless memerlukan pustaka waktu proses C yang cocok, cross-compiling ke OS atau arsitektur CPU lain memerlukan rantai perkakas silang C yang lengkap, dan korupsi memori apa pun dalam C membagikan ruang alamat proses Go.

Di sisi lain terdapat port SQLite murni Go yang dihasilkan dengan menerjemahkan secara mekanis amalgamasi C ke dalam kode sumber Go di atas waktu proses C yang ditiru (libc). Mereka memulihkan CGO_ENABLED=0, yang membuat pembangunan menjadi sepele. Namun, di bawah beban kerja yang berkelanjutan, timbunan C yang diterjemahkan dan alokasi Go per baris meningkatkan tekanan pengumpulan sampah, serta kasus-kasus khusus yang halus dalam penguncian, batas waktu sibuk, atau interupsi pernyataan dapat berbeda dari pembangunan C asli.

Kami membangun gosuda.org/sqlite3 agar aplikasi Go tidak harus memilih antara CGO_ENABLED=0 dan mesin C SQLite yang asli. Dengan mengompilasi amalgamasi C SQLite resmi 3.54.1 ke dalam server memori bersama khusus (hqq-sqlite-server) dan berkomunikasi melalui gosuda.org/sqlite3, biner Go Anda dikompilasi dengan CGO_ENABLED=0 seraya mengeksekusi C SQLite yang tidak dimodifikasi dan dioptimalkan dengan -O3 melintasi batas proses.

Eksekusi Konfigurasi Nol: Dari go get ke sql.Open

Mengganti github.com/mattn/go-sqlite3 dalam basis kode Go yang ada hanya memerlukan perubahan jalur impor kosong. Driver terdaftar sebagai "sqlite3" (dan "hqq-sqlite" untuk pemilihan eksplisit) dan menerima string kueri DSN yang sama:

 1package main
 2
 3import (
 4	"context"
 5	"database/sql"
 6	"fmt"
 7	"log"
 8
 9	_ "gosuda.org/sqlite3"
10)
11
12func main() {
13	dsn := "file:app.db?_journal_mode=WAL&_busy_timeout=5000&_txlock=immediate&_foreign_keys=on"
14	db, err := sql.Open("sqlite3", dsn)
15	if err != nil {
16		log.Fatal(err)
17	}
18	defer db.Close()
19
20	ctx := context.Background()
21	if _, err := db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)`); err != nil {
22		log.Fatal(err)
23	}
24	if _, err := db.ExecContext(ctx, `INSERT INTO users (name) VALUES (?)`, "Ada Lovelace"); err != nil {
25		log.Fatal(err)
26	}
27
28	var id int64
29	var name string
30	if err := db.QueryRowContext(ctx, `SELECT id, name FROM users WHERE name = ?`, "Ada Lovelace").Scan(&id, &name); err != nil {
31		log.Fatal(err)
32	}
33	fmt.Printf("user #%d: %s\n", id, name)
34}

Biner Pekerja Teratestasi SLSA yang Disematkan

Pertanyaan wajar ketika mendengar bahwa sebuah pustaka menggunakan proses pekerja C adalah: "Apakah saya harus menginstal atau mengirimkan biner C terpisah bersama program Go saya?"

Tidak. gosuda.org/sqlite3 menyematkan biner hqq-sqlite-server prafroses untuk keenam target utama secara langsung di dalam berkas Go yang diberi tag platform (internal/embedded/embed_<goos>_<goarch>.go). Karena batasan pembangunan Go hanya mengompilasi berkas yang cocok dengan GOOS dan GOARCH target Anda, cross-compiling GOOS=linux GOARCH=arm64 go build secara otomatis hanya menyematkan biner server linux/arm64 berukuran sekitar ~1.4 MiB.

OS TargetArsitektur Target (GOARCH)Biner yang DisematkanRantai Perkakas Pembangunan & Provenans
Linuxamd64, arm64hqq-sqlite-server_linux_<arch>GCC -O3, Provenans SLSA Build L3
macOSarm64, amd64hqq-sqlite-server_darwin_<arch>AppleClang -O3, Provenans SLSA Build L3
Windowsamd64hqq-sqlite-server_windows_amd64.exeMSVC -O2, Provenans SLSA Build L3
Windowsarm64hqq-sqlite-server_windows_arm64.exezig cc -target aarch64-windows-gnu -O3, SLSA L3

Ketika sql.Open menetapkan koneksi pertamanya, autolaunch.go mewujudkan biner yang disematkan ke dalam direktori tembolok pengguna ($XDG_CACHE_HOME/hqq/bin/<sha256>/), memverifikasi ringkasan SHA-256 kriptografisnya terhadap peta ReleaseHashes yang disematkan dalam hashes.go melalui gosuda.org/hqq/secureexec, dan meluncurkan pekerja dengan --parent-stdin sehingga masa pakainya terikat secara ketat pada proses Go Anda.

Kompatibilitas DSN mattn/go-sqlite3 Ruang Bersih

Untuk berfungsi sebagai pengganti langsung yang sebenarnya, pengurai DSN (dsn.go) mengimplementasikan replika ruang bersih dari kenop koneksi dan pragma yang digunakan di seluruh basis kode mattn/go-sqlite3:

Parameter Kueri DSNNilai yang DiterimaEfek SQLite
_busy_timeout / _timeoutMilidetik bilangan bulat (mis. 5000)Memanggil sqlite3_busy_timeout(db, ms) saat sesi dibuka.
_journal_mode / _journalDELETE, TRUNCATE, PERSIST, MEMORY, WAL, OFFMengeksekusi PRAGMA journal_mode = <mode>.
_txlockimmediate, exclusive, deferredMemilih BEGIN IMMEDIATE, BEGIN EXCLUSIVE, atau BEGIN DEFERRED untuk db.BeginTx.
_foreign_keys / _fkon, true, 1, off, false, 0Mengeksekusi PRAGMA foreign_keys = ON/OFF.
_synchronous / _sync0/OFF, 1/NORMAL, 2/FULL, 3/EXTRAMengeksekusi PRAGMA synchronous = <level>.
_cache_sizeHalaman bilangan bulat atau -KiB (mis. -20000)Mengeksekusi PRAGMA cache_size = <val>.
_locauto atau zona waktu IANA (mis. UTC, Asia/Seoul)Mengurai kolom teks DATETIME/TIMESTAMP ke dalam *time.Time di lokasi target.
modero, rw, rwc, memoryMenetapkan SQLITE_OPEN_READONLY, SQLITE_OPEN_READWRITE, SQLITE_OPEN_CREATE, atau SQLITE_OPEN_MEMORY.
_case_sensitive_like, _query_only, _recursive_triggers, _secure_delete, _AutoVacuumBoolean atau bilangan bulat modeMenerapkan PRAGMA sesi yang sesuai pada inisialisasi koneksi.

Mengubah Isolasi Proses Menjadi Keuntungan Throughput

Sekilas, menjalankan SQLite dalam proses terpisah terdengar lebih lambat daripada memanggil sqlite3_step dalam proses melalui cgo. Dalam praktiknya, hal sebaliknya terjadi segera setelah kueri mengembalikan lebih dari satu baris.

Biaya Tersembunyi dari Panggilan cgo Baris demi Baris

Dalam driver SQLite cgo konvensional, database/sql mendorong iterasi satu baris dan satu kolom pada satu waktu. Membaca 1.000 baris dengan 4 kolom memicu:

  • 1.000 panggilan cgo ke sqlite3_step
  • 4.000 panggilan cgo ke sqlite3_column_type dan sqlite3_column_int64 / sqlite3_column_text
  • Ribuan persimpangan batas individual antara penjadwal Go dan tumpukan C

Request Pipelining dan Prefetching Batch 64 KiB

gosuda.org/sqlite3 menghilangkan persimpangan batas per baris dengan merancang ulang protokol jaringan di sekitar dua mekanisme dalam stmt.go, rows.go, dan sqlite3/server/sqlite3_hooks.c:

 1Go Client (stmt.go / rows.go)                       C11 Worker (sqlite3_hooks.c)
 2-----------------------------                       ----------------------------
 31. Pipeline in 1 flight:
 4   ResetAsync + BindValuesAsync
 5   + StmtInfoAsync + FetchRowsAsync(128)  =======>  reset -> bind -> stmt_info ->
 6                                                    loop sqlite3_step() up to 128 rows
 7                                          <=======  Batch 1 (rows 1..128 packed in 64 KiB slot)
 82. Immediately fire prefetch:
 9   FetchRowsAsync(128) (Batch 2)          =======>  loop sqlite3_step() for rows 129..256
10   While C steps Batch 2, Go caller
11   scans rows 1..128 from Batch 1!        <=======  Batch 2 ready in shared memory
  1. Pipelining Kueri Penerbangan-Tunggal (stmt.go): Ketika QueryContext atau Stmt.QueryContext menjalankan pernyataan baca-saja, s.query() mengirimkan reset, bind_values, stmt_info (pada eksekusi pertama), dan fetch_rows secara asinkron dalam satu penerbangan memori bersama. Keempat operasi tersebut dieksekusi secara berturut-turut pada pekerja C dan kembali dalam satu perjalanan pulang pergi.
  2. Perulangan Batch C yang Ketat dan Prefetch Asinkron (rows.go): Di dalam fetch_rows (sqlite3_hooks.c), pekerja C menjalankan langkah sqlite3_step(stmt) dalam perulangan C asli, mengenkode hingga baris FetchSize (bawaan 128) secara langsung ke dalam slot respons memori bersama 64 KiB hingga batas batch tercapai, slot penuh, atau SQLITE_DONE terjadi. Segera setelah rows.ingest() mendekode Batch 1, fungsi tersebut memicu FetchRowsAsync untuk Batch 2 sebelum mengembalikan baris pertama ke pemanggil. Saat kode Go Anda memindai Batch 1, pekerja C sudah melangkah ke Batch 2 pada inti CPU lain.

Pada Apple M4 (darwin/arm64), membandingkan gosuda.org/sqlite3 (CGO_ENABLED=0) dengan github.com/mattn/go-sqlite3 (CGO_ENABLED=1) pada basis data SQLite yang sama menunjukkan efek dari pembersihan batch dan pipelining:

Beban Kerjagosuda.org/sqlite3 (CGO_ENABLED=0)mattn/go-sqlite3 (CGO_ENABLED=1)Kecepatan Relatif
Point Select (QueryRow oleh PK)3.52 ยตs/op (9 B, 1 alokasi)3.56 ยตs/op (232 B, 11 alokasi)1,01x (paritas, 11x alokasi lebih sedikit)
Pindaian 100-Baris (rows.Next)20.41 ยตs/op (30.5 KB, 211 alokasi)36.34 ยตs/op (21.8 KB, 309 alokasi)1,78x lebih cepat
Pindaian 1.000-Baris (rows.Next)125.92 ยตs/op (296 KB, 2.030 alokasi)349.10 ยตs/op (216 KB, 3.009 alokasi)2,77x lebih cepat
Sisipan Disiapkan (Autocommit)5.33 ยตs/op (0 B, 0 alokasi)5.88 ยตs/op (120 B, 5 alokasi)1,10x lebih cepat (0 alokasi)
Batch Transaksi 100-Baris189.99 ยตs/op (16 B, 1 alokasi)191.50 ยตs/op (12.1 KB, 503 alokasi)1,01x (500x alokasi lebih sedikit)

Pembatalan, Penyekatan Jalur, dan Pemulihan Kumpulan Koneksi

Menjalankan SQLite dalam pekerja terisolasi juga memperkuat cara driver menangani batas waktu, SQL yang tidak tepercaya, dan batas sistem berkas:

  • Pembatalan Konteks Deterministik (conn.go dan errors.go): Setiap panggilan driver mempersenjatai pelindung context.AfterFunc yang ringan (cn.guard). Jika context.Context pemanggil dibatalkan atau batas waktunya kedaluwarsa saat kueri rekursif yang lambat atau penantian kunci sedang berjalan di server, pelindung menandai koneksi rusak, mematikan tautan sesi lokal, dan mengirimkan bingkai Cancel() luar-band. Pekerja C memanggil sqlite3_interrupt(db) seketika, membuka blokir sqlite3_step, sementara driver Go memetakan status SQLITE_INTERRUPT yang dihasilkan ke context.Canceled atau context.DeadlineExceeded dan menginstruksikan database/sql untuk mengganti koneksi dalam kumpulan.
  • Penyekatan Direktori (sqlsrv_pathrules.c): Pekerja C memberlakukan aturan jalur --allow-dir pada setiap panggilan open_v2, menyelesaikan tautan simbolik dan menolak penelusuran .. relatif di luar direktori yang diizinkan. sqlite3_load_extension dan SQLITE_LIMIT_ATTACHED dinonaktifkan secara bawaan sehingga suntikan SQL tidak dapat lolos dari direktori basis data atau memuat objek bersama arbitrer.

Tata Letak Repositori

Repositori distrukturkan sedemikian rupa sehingga paket tingkat atas menyediakan driver database/sql langsung sementara launcher/ dan sqlite3/ tetap dapat diakses untuk penerapan server kustom tingkat lanjut:

 1gosuda.org/sqlite3
 2โ”œโ”€โ”€ driver.go / api.go         # registrasi driver database/sql ("sqlite3") dan API Konektor
 3โ”œโ”€โ”€ dsn.go                     # Pengurai DSN mattn/go-sqlite3 ruang bersih dan konfigurasi pragma
 4โ”œโ”€โ”€ conn.go / tx.go            # Siklus hidup sesi, pelindung konteks, dan mesin status transaksi
 5โ”œโ”€โ”€ stmt.go / rows.go          # Pipelining permintaan penerbangan-tunggal dan prefetch batch 64 KiB
 6โ”œโ”€โ”€ errors.go                  # Kode galat primer/ekspektasi SQLite bertipe dan pencocokan sentinel
 7โ”œโ”€โ”€ autolaunch.go / hashes.go  # Mewujudkan server tersemat konfigurasi-nol dan penyematan SHA-256
 8โ”œโ”€โ”€ internal/embedded/         # Biner bertag platform //go:embed (linux, darwin, windows)
 9โ”œโ”€โ”€ launcher/                  # Pengawas proses yang dapat dikonfigurasi untuk penerapan server kustom
10โ””โ”€โ”€ sqlite3/                   # Klien Go hqq-bindgen yang dihasilkan dan sumber server C11 SQLite
11    โ””โ”€โ”€ server/                # Implementasi server C11 (sqlite3_hooks.c, sqlsrv_policy.c)

Memulai

Instal modul dengan go get dan buka basis data Anda dengan sql.Open("sqlite3", dsn):

1go get gosuda.org/sqlite3@v0.3.3540100