ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

libsql sqld 用户指南:HTTP 数据库服务、复制集群、增量快照与多租户实战

libsql sqld 用户指南:HTTP 数据库服务、复制集群、增量快照与多租户实战 libsql sqld 用户指南HTTP 数据库服务、复制集群、增量快照与多租户实战【免费下载链接】libsqllibSQL is a fork of SQLite that is both Open Source, and Open Contributions.项目地址: https://gitcode.com/GitHub_Trending/li/libsql本指南围绕 libsql 项目中的sqld服务端程序展开讲解如何将其作为面向 HTTP 的数据库服务运行并搭建基于 gRPC TLS 的主从复制集群同时覆盖客户端 JWT 认证、Docker 与 Fly 部署、增量快照导出配合libsql客户端应用以及命名空间多租户路由。读完本文你将掌握从零启动一个sqld集群、验证读写分流、并把数据库增量快照同步到本地离线副本的完整链路。图 1libsql 集群总览概述sqld如何通过 HTTP 对外提供 libsqlsqldSQL daemon是 libsql 项目的服务端入口其核心能力有两条通过 HTTP 对外提供 libsql 数据库服务以及支持透明的复制transparent replication。其入口实现在 libsql-server/src/main.rs#[command(name sqld)]。从架构上看图 1一个 libsql 集群由三类角色构成客户端通过 HTTP 远程执行 SQL例如curl或任意 HTTP 客户端主节点primary位于集群中间负责接受写操作并向副本节点提供预写日志WAL更新副本节点replica若客户端执行写操作如INSERT副本会把写请求转发给主节点而读操作如SELECT直接在副本本地执行。副本通过 gRPC 连接周期性轮询主节点获取 WAL 更新。这种写走主、读走副本的模式意味着副本节点天然具备读扩展与就近读的能力。注意默认情况下所有无法确定命名空间的请求都会落入名为default的默认命名空间相关行为定义在 libsql-server/src/main.rs 的disable_default_namespace与enable_namespaces两个参数中后文多租户章节会详细介绍。从代码结构看sqld依据启动参数在三种模式间切换见 libsql-server/src/main.rs 的欢迎信息逻辑standalone独立模式既不设置--grpc-listen-addr也不设置--primary-grpc-urlprimary主节点模式设置了--grpc-listen-addrreplica副本模式设置了--primary-grpc-url指向主节点的 gRPC 地址。搭建复制集群TLS 配置集群节点间通信的前提sqld集群中的节点通过gRPC TLS通信。在 TLS 术语中主节点扮演服务端副本节点扮演客户端。搭建集群前你需要准备以下证书材料证书颁发机构CA证书与私钥主节点服务端证书与私钥副本节点客户端证书与私钥每个副本建议使用独立的证书配置。仅供开发与测试用途仓库提供了证书生成脚本 libsql-server/scripts/gen_certs.py在仓库根目录执行python scripts/gen_certs.py脚本基于 Ed25519 私钥生成证书源码见 gen_certs.py并在当前工作目录产出 6 个文件文件用途ca_cert.pem证书颁发机构证书ca_key.pem证书颁发机构私钥server_cert.pem主节点服务端证书server_key.pem主节点服务端私钥client_cert.pem副本节点客户端证书client_key.pem副本节点客户端私钥需要注意这是开发证书有效期只有 3 天脚本在 gen_certs.py 中设置not_after now 3 days脚本末尾也会打印过期时间提示生产环境请使用自己的 PKI 体系签发证书。启动主节点primary在主节点所在机器上执行以下命令启动主模式服务sqld \ --http-listen-addr 127.0.0.1:8081 \ --grpc-listen-addr 127.0.0.1:5001 \ --grpc-tls \ --grpc-ca-cert-file ca_cert.pem \ --grpc-cert-file server_cert.pem \ --grpc-key-file server_key.pem启动后主节点将在两个端口提供能力127.0.0.1:8081HTTP 协议供客户端执行 SQL127.0.0.1:5001gRPC启用 TLS供副本节点同步 WAL。从源码看这些参数定义在 libsql-server/src/main.rs。--grpc-tls一旦开启会强制要求同时提供--grpc-cert-file、--grpc-key-file、--grpc-ca-cert-file三个参数requires约束缺少任何一个都会导致启动失败。TLS 配置最终被组装为TlsConfig证书、私钥、CA 证书三个路径见 libsql-server/src/main.rs。启动副本节点replica在副本节点所在机器上执行sqld \ --http-listen-addr 127.0.0.1:8082 \ --primary-grpc-url https://127.0.0.1:5001 \ --primary-grpc-tls \ --primary-grpc-ca-cert-file ca_cert.pem \ --primary-grpc-cert-file client_cert.pem \ --primary-grpc-key-file client_key.pem此时副本节点在127.0.0.1:8082提供 HTTP 服务并通过 TLS 连接到127.0.0.1:5001的主节点。同样地--primary-grpc-tls开启时也必须提供对应的证书、私钥、CA 三个参数libsql-server/src/main.rs副本侧的 TLS 配置最终生成RpcClientConfig包含远端 URL、连接器与 TLS 配置见 libsql-server/src/main.rs。扩展副本非常简单再启动更多sqld进程即可加入集群但建议为每个副本生成独立的 TLS 证书配置即各自的client_cert.pem/client_key.pem。验证集群写入副本、读取主节点由于写操作会由副本转发到主节点你可以先在副本上建表并写入curl -d {statements: [CREATE TABLE IF NOT EXISTS users (username), INSERT INTO users VALUES (\alice\)]} 127.0.0.1:8082然后从主节点上查询验证数据已同步curl -d {statements: [SELECT * FROM users]} 127.0.0.1:8081若返回包含alice的行集说明复制链路已打通写入被副本转发至主节点、进入 WAL并经 gRPC 被各节点消费。客户端认证基于 JWT 的访问控制sqld通过命令行参数--auth-jwt-key-file FILENAME启用客户端认证。该参数在 libsql-server/src/main.rs 中定义其取值支持两种格式PKCS#8 编码的Ed25519 公钥PEM 格式直接使用 URL-safe base64 编码的Ed25519 公钥原始字节。从认证策略的组装逻辑看libsql-server/src/main.rs认证方式存在优先级若指定了--http-auth格式basic:$PARAM其中$PARAM是$USERNAME:$PASSWORD的 base64 编码则启用旧的 HTTP Basic 认证否则若提供 JWT 密钥文件--auth-jwt-key-file或环境变量SQLD_AUTH_JWT_KEY则启用 JWT 认证两者都没有时认证被禁用Auth::new(user_auth_strategies::Disabled::new())。JWT 认证有一个实用细节一个文件里可以拼接多个解码密钥连续写入即可服务端解析收到的 JWT 时会依次尝试所有密钥方便密钥轮换。此外密钥内容也可以通过环境变量SQLD_AUTH_JWT_KEY直接传入无需文件。部署使用 Docker 部署拉取官方镜像docker pull ghcr.io/libsql/sqld:main更完整的镜像使用说明镜像仓库、Apple Silicon 平台选择、数据持久化、Compose 示例见 DOCKER.md。要点如下数据持久化数据库文件位于镜像内/var/lib/sqld跨运行持久化需将其挂载为 Docker volume 或 bind mount若通过SQLD_DB_PATH改变数据目录挂载路径需保持一致关键环境变量详见 DOCKER.mdSQLD_NODE节点类型取值为primary默认、replica、standalonereplica 还需要SQLD_PRIMARY_URLSQLD_PRIMARY_URL副本节点连接主节点的 gRPC URLSQLD_HTTP_LISTEN_ADDRHTTP 监听地址默认0.0.0.0:8080SQLD_GRPC_LISTEN_ADDRgRPC 监听地址默认0.0.0.0:5001用于节点间通信。例如以主节点模式运行并挂载本地数据目录docker run --name some-sqld -ti \ -v $(pwd)/sqld-data:/var/lib/sqld \ -e SQLD_NODEprimary \ ghcr.io/tursodatabase/libsql-server:latest部署到 Fly仓库根目录自带 fly.toml它声明了sqld应用internal_port 8080对外暴露 HTTP 端口 80并配置了连接并发限制与 TCP 健康检查可直接复用。部署步骤flyctl launch然后按提示为应用命名在询问是否立即部署时选择 Yes。完成后sqld即在 Fly 上监听 HTTP 连接。用以下命令验证将$YOUR_APP替换为你的应用名curl -X POST -d {statements: [create table testme(a,b,c)]} $YOUR_APP.fly.dev curl -X POST -d {statements: [insert into testme values(1,2,3)]} $YOUR_APP.fly.dev curl -X POST -d {statements: [select * from testme]} $YOUR_APP.fly.dev最后一条命令应返回类似如下的 JSON 行[{ b: 2, a: 1, c: 3 }]增量快照把数据同步到离线本地副本背景与原理sqld会为数据库文件生成增量快照incremental snapshots快照可被应用到本地 libSQL 副本。典型场景是应用并非始终在线、无法依赖sqld的 gRPC 复制方法此时可以配置sqld在生成增量快照时回调通知脚本把快照文件同步到另一台机器再离线应用。两个关键命令行参数定义于 libsql-server/src/main.rs 与 libsql-server/src/main.rs--snapshot-exec FILE指定在快照生成时执行的程序如 shell 脚本--max-log-duration SECS控制快照生成的频率秒保证本地副本数据的新鲜度。它是复制日志压缩compaction的最大时间阈值日志超过该时长即被压缩为快照。快照的生成由复制日志压缩器LogCompactor驱动实现在 libsql-server/src/replication/snapshot.rs。SnapshotBuilder从日志末尾向前遍历帧只保留每个页面的最新版本最终把快照落盘为db_path/snapshots/{log_id}-{start}-{end}.snap命名格式见 snapshot.rs。配置snapshot.sh回调脚本首先创建脚本snapshot.sh#!/bin/bash SNAPSHOT_FILE$1 NAMESPACE$2 echo Generated incremental snapshot $SNAPSHOT_FILE for namespace $NAMESPACE # At this point we can ship the snapshot file to wherever we would like but we # must delete it from its location on disk or else sqld will panic. rm $SNAPSHOT_FILE然后启动sqld让它在每 5 秒生成一次增量快照并调用脚本sqld --snapshot-exec ./snapshot.sh --max-log-duration 5写入数据后日志中会逐渐出现类似输出2023-08-11T08:21:04.183564Z INFO sqld::replication::snapshot: snapshot e126f594-90f4-45be-9350-bc8a01160de9-0-2.snap successfully created Generated incremental snapshot data.sqld/dbs/default/snapshots/e126f594-90f4-45be-9350-bc8a01160de9-0-2.snap第一行是sqld自身的日志第二行则是sqld执行snapshot.sh的输出。注意脚本必须删除快照文件否则sqld会 panic。从源码看脚本实际会被传入5 个位置参数不只是文档示例中的 2 个。命令处理器CommandHandler依次追加快照路径、命名空间、起始帧号、结束帧号、log_id见 libsql-server/src/replication/script_backup_manager.rs。回调机制由ScriptBackupManager实现快照生成后通过硬链接登记到script_backup队列目录后台任务按命名空间与帧号顺序串行执行脚本脚本执行失败时会以指数退避重试成功则要求快照文件已被移除一致性断言见 script_backup_manager.rs。在本地副本应用快照将快照文件例如通过rsync拷贝到目标机器后用libsqlcrate 的Database::sync_frames()方法应用use libsql::Database; use libsql_replication::{Frames, TempSnapshot}; #[tokio::main] async fn main() { tracing_subscriber::fmt::init(); let opts libsql::Opts::with_sync(); let db Database::open_with_opts(test.db, opts).await.unwrap(); let conn db.connect().unwrap(); let args std::env::args().collect::VecString(); if args.len() 2 { println!(Usage: {} snapshot path, args[0]); return; } let snapshot_path args.get(1).unwrap(); let snapshot TempSnapshot::from_snapshot_file(snapshot_path.as_ref()).unwrap(); db.sync_frames(Frames::Snapshot(snapshot)).unwrap(); let rows conn .query(SELECT * FROM sqlite_master, ()) .unwrap() .unwrap(); while let Ok(Some(row)) rows.next() { println!( | {:024} | {:024} | {:024} | {:024} |, row.get::str(0).unwrap(), row.get::str(1).unwrap(), row.get::str(2).unwrap(), row.get::str(3).unwrap(), ); } }sync_frames是libsql客户端副本同步的核心接口定义在 libsql/src/database.rs它接受Frames枚举此处为Frames::Snapshot把快照中的页面帧应用到本地数据库文件。快照文件命名约定与顺序要求应用快照时文件名本身携带关键元信息。快照名格式为{namespace}:{log_id}:{start_frame_no:020x}-{end_frame_no:020x}.snap其中namespace快照所属的命名空间log_id唯一标识一条 WALwrite ahead log的 UUIDstart_frame_no/end_frame_no20 位十六进制表示的起始/结束帧号。该格式在 script_backup_manager.rs 中生成与上文snapshots目录内快照文件名略有差异前者以命名空间为前缀后者直接以 log_id 开头但帧号区间语义一致。对每一个log_id快照从第 0 帧开始直到末尾必须从帧 0 起按顺序依次应用。多租户一个sqld实例承载多个数据库sqld支持在同一实例内运行多个数据库命名空间namespace。命名空间是同一实例内相互隔离的数据库。启用命名空间与 Admin API默认情况下命名空间处于关闭状态所有请求都指向默认命名空间。要管理命名空间需要给sqld传入两个额外参数详见 ADMIN_API.md--admin-listen-addr addr:portAdmin API 的监听地址与端口必须与用户 API 监听地址不同用户 API 默认 8080--enable-namespaces启用命名空间特性默认关闭对应源码中的disable_namespaces: !config.enable_namespaces见 libsql-server/src/main.rs。创建名为db1的数据库命名空间curl -X POST http://localhost:8080/v1/namespaces/db1/createAdmin API 的路由定义在 libsql-server/src/http/admin/mod.rs除创建外还提供删除DELETE /v1/namespaces/:namespace、forkPOST /v1/namespaces/:namespace/fork/:to、checkpoint、stats 等端点。创建接口还支持可选的dump_url请求体字段用于从外部转储初始化数据库。Host 头路由namespace.local形式数据库命名空间的名称由 HTTP 请求的Host头决定。例如在/etc/hosts中写入127.0.0.1 db1.local 127.0.0.1 db2.local之后便可用http://db1.local:8080访问db1用http://db2.local:8080访问db2。两个数据库的文件分别存放在data dir/dbs/db1与data dir/dbs/db2。命名空间解析逻辑Host 头、x-namespace头、元数据头的优先级链见 libsql-server/src/http/user/db_factory.rs。Path based routing本地开发用路径路由为方便本地开发与测试也可以把命名空间放在 URL 路径中http://local:8080/dev/db1访问命名空间db1http://local:8080/dev/db2访问命名空间db2。通配符域名免改/etc/hosts的开发技巧如果不想每次测试新命名空间都编辑/etc/hosts可以使用任何能让所有子域名都解析到127.0.0.1的域名。文档示例*.db.sarna.dev就满足这一条件于是可以这样访问本地命名空间http://db1.db.sarna.dev→db1http://db2.db.sarna.dev→db2常见配置参数速查以下参数与环境变量均以当前仓库 libsql-server/src/main.rs 的实际定义为准命令行参数环境变量默认值说明--http-listen-addrSQLD_HTTP_LISTEN_ADDR127.0.0.1:8080用户 HTTP API 监听地址--grpc-listen-addrSQLD_GRPC_LISTEN_ADDR无节点间 gRPC 监听地址设置即为主节点模式--primary-grpc-urlSQLD_PRIMARY_GRPC_URL无主节点 gRPC URL设置即为副本模式--grpc-tls/--primary-grpc-tls无关启用 gRPC 双向 TLS需配套证书参数--auth-jwt-key-fileSQLD_AUTH_JWT_KEY_FILE/SQLD_AUTH_JWT_KEY无JWT 解码密钥文件或内容--snapshot-execSQLD_SNAPSHOT_EXEC无快照生成时执行的脚本/程序--max-log-durationSQLD_MAX_LOG_DURATION无仅按大小压缩复制日志按时间压缩阈值秒--max-log-sizeSQLD_MAX_LOG_SIZE200MB复制日志按大小压缩阈值--db-pathSQLD_DB_PATHdata.sqld数据目录--enable-namespaces无关启用命名空间多租户--admin-listen-addrSQLD_ADMIN_LISTEN_ADDR无Admin API 监听地址其余参数心跳、堆内存软硬限制、并发连接数、响应大小限制、检查点间隔、Bottomless 备份等也可在 main.rs 中逐一查阅它们大多支持同名环境变量便于容器化部署时配置。结语从一条curl到分布式复制集群sqld将 SQLite 内核的轻量与可靠暴露为一个可横向扩展的 HTTP 服务主从节点通过 gRPC TLS 交换 WAL写操作在主节点落盘、读操作在副本就近执行增量快照让离线终端也能通过libsqlcrate 无缝追平数据而命名空间机制则在单实例内实现了数据库级的多租户隔离。本文所有命令与参数均可在本仓库源码中溯源后续可继续阅读 ADMIN_API.md、DOCKER.md 以及 HTTP_V1_SPEC.md / HTTP_V2_SPEC.md 深入了解接口协议细节。【免费下载链接】libsqllibSQL is a fork of SQLite that is both Open Source, and Open Contributions.项目地址: https://gitcode.com/GitHub_Trending/li/libsql创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表