ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Turso 事务延迟基准测试指南:从零复现 SQLite 与 Turso 的写入延迟对比实验

Turso 事务延迟基准测试指南:从零复现 SQLite 与 Turso 的写入延迟对比实验 Turso 事务延迟基准测试指南从零复现 SQLite 与 Turso 的写入延迟对比实验【免费下载链接】tursoA SQL database in Rust: SQLite-compatible, now also speaking Postgres (experimental). The LLVM of databases.项目地址: https://gitcode.com/GitHub_Trending/tu/turso本文是 Turso 仓库中事务延迟基准测试transaction latency benchmark的完整技术指南。该基准位于 perf/latency 目录用于测量一个小型写事务从应该开始执行到提交完成在 SQLite 与 Turso 中的耗时分布并以 eCDF经验累积分布函数图形对比两个引擎在 1、8、16、32 个并发连接下的尾延迟表现。读完本文你将掌握该基准的完整设计思想开环负载、无协调遗漏、分阶段采样、全部环境变量与命令行参数、底层引擎调用链含 io_uring、MVCC、被动检查点等关键机制并能直接复现实验、读懂输出文件与绘图结果。基准测试要回答什么问题数据库性能对比中最常见的陷阱是协调遗漏coordinated omission如果测试程序在数据库变慢时停止向其发送新请求那么数据库忙于阻塞的时间段就不会被任何样本覆盖测得的结果会虚低——一个被写锁卡住的数据库看起来反而很快因为它只是暂时没人问它要活干。本基准的核心设计目标正是把等待写锁、排队阻塞的时间真实地计入延迟样本。具体做法是事务按照一个固定的到达时刻表运行这个时刻表不会因为数据库变慢而调整。turso-txn-latency的 main.rs 模块注释对此有明确的说明事务的延迟从它应当开始的时刻算起排在前面的事务造成的等待会被计入样本否则一个阻塞中的数据库会显得很快因为它忙的时候干脆没被派活只有它准备好的事务才会被计时——这恰好掩盖了本基准要测量的停顿。工作量、加载方式、持久化语义、检查点策略和运行规则五大部分构成了基准的方法论骨架接下来逐一展开。快速开始一键运行完整实验在perf/latency目录下执行./scripts/run.shrun.sh的工作分为两阶段见 run.sh调用scripts/bench.sh构建基准程序并以默认参数跑完全部测试检查系统是否装有uv绘图所需的 Python 包管理器缺失时会报错退出然后调用 plot-latency-ecdf.py 绘制 eCDF 图形。完整实验的行为如下在两个引擎上分别以 1、8、16、32 个连接各跑 3 次每次负载为 1000 事务/秒总耗时约 1 小时其中大部分时间花在每次运行前让磁盘空闲 1 分钟每次运行前都会请求sudo权限用于对磁盘执行fstrim告诉驱动哪些块空闲并清空页面缓存运行环境要求Rust 工具链、用于绘图的uv、以及 LinuxTurso 端依赖 io_uring 后端。快速试跑只想快速看效果时可以用环境变量压缩规模详见下文参数表CONNECTIONS1 8 REPEATS1 DURATION20 ./scripts/run.sh这条命令只跑 2 个连接数、每个引擎各 1 次、每次测量 20 秒。基准程序本身的帮助直接运行turso-txn-latency --help可查看 harness 自己的全部参数其中有两个值得关注的选项--mode immediate让 Turso 使用 WAL 日志 BEGIN IMMEDIATE与 SQLite 的配置方式对齐--io syscall让 Turso 使用系统调用 I/O 后端而非 io_uring。此外当一次运行未能跟上提供速率offered rate时摘要中会给出 WARNING——此时该次运行画出的曲线展示的是落后了多少而不是延迟是多少详见 main.rs 的告警逻辑。产物一次完整实验会得到什么运行结束后plot/与db/目录下会生成以下文件产物说明plot/latency-ecdf.png、.pdf、.tikz每个连接数一个 eCDF 面板SQLite 对 Turso曲线上标出 p50、p90另有一条 p99.9 竖线。.tikz是 pgfplots 图片可在论文中通过\input引入plot/engine-cN-ri.csv每次运行一个文件每个事务一行应开始时刻、是否处于预热期、重启次数、以及 queue/begin/work/commit 各阶段耗时plot/engine-cN-ri-checkpoints.csv该次运行中每次检查点的开始时刻与耗时plot/bench.log全部运行的摘要日志同样输出到终端分位数、进程自身 CPU 占用、数据库所在磁盘的活动、检查点计时db/timestamp/每次运行的数据库文件永不删除从 CSV 的列结构write_samples可以看到每个样本携带的完整信息engine,mode,connections,run,thread_id,scheduled_ns,warmup,restarts,queue_ns,begin_ns,work_ns,commit_ns,total_ns其中total_ns即从计划到达时刻到成功 COMMIT 的总延迟Sample结构体的字段注释见 main.rs检查点 CSV 的列为engine,mode,connections,run,at_ns,took_ns。方法论基准是如何设计的工作量Workload单张表test_table(id INTEGER PRIMARY KEY, data TEXT)。每个事务插入BATCH_SIZE默认 10行主键 id 从所有连接共享的一个计数器取号两个引擎分别用NEXT_ID原子计数器实现见 sqlite_engine.rs 与 turso_engine.rs因此事务之间绝不会触碰同一行唯一竞争点是引擎自身的写路径BEGIN、INSERT、COMMIT三条语句在每个连接上只 prepare 一次解析 SQL 的开销不计入事务延迟SQLite 端见 sqlite_engine.rsTurso 端见 turso_engine.rs注释明确说明这是为了避免把每次Connection::execute的重复解析计费给事务。加载方式Load开环 抗协调遗漏开环到达时刻在运行开始前一次性确定Pacer持有预生成的due: VecDuration时刻表见 main.rs事务在其到达时刻到期无论当时是否有空闲连接哪个连接空闲就领取下一个到期事务。延迟口径延迟从到期时刻算起因此排在停滞写者后面的事务其排队时间计入样本。到达过程默认为RATE速率下的泊松过程指数间隔来自一个带种子的生成器保证每个引擎、每次重复看到的到达时刻表完全相同ARRIVALSfixed则把间隔固定为精确的1/RATE。速率语义RATE是所有连接的总量——更多连接意味着同时在途的事务更多而不是负载更大。这正是 SQLite 同一时刻只能有一个写者体现得最明显的地方。预热前WARMUP秒的事务会被记录但打上 warmup 标记在所有摘要与图形中排除绘图脚本会跳过warmup 1的行见 plot-latency-ecdf.py。分阶段采样每个样本携带到期时刻、重启次数、以及 queue等待连接、begin、work插入、commit 四段时间。值得注意的实现细节Pacer的时钟在第一个连接请求工作时才启动started_at()使用OnceLock见 main.rs因此打开连接与 prepare 语句的开销不会挤占第一批事务的时隙。为了不把线程调度的误差算成数据库的延迟等待到期采用睡眠 最后 300 微秒自旋的方式wait_untilTurso 端在 tokio 计时器之上又叠加了一层处理——tokio 计时器每毫秒才 tick 一次可能晚醒最多 1 毫秒因此 tokio 睡眠提前 1.5 毫秒结束最后一段交给线程睡眠加自旋turso_engine.rs。持久化Durability两个引擎都以PRAGMA synchronous FULL运行即每次提交都以 fsync 收尾。这意味着对比的是两条持久提交路径之间的差异而不是两种持久化设置之间的差异。SQLite 端的配置WAL 模式通过rusqlite每个 OS 线程一个连接使用BEGIN IMMEDIATE。延迟的BEGIN会在第一条INSERT时才获取写锁若期间其他连接已提交锁升级会立刻以SQLITE_BUSY_SNAPSHOT失败事务必须回滚后由应用重试提前取锁则把等待放进 begin 阶段由 SQLite 的 busy handler 消化这正是 SQLite 官方文档为多写者应用推荐的做法实现见 sqlite_engine.rs。busy 超时 60 秒使用默认 busy handler--timeout默认值 60000 毫秒见 main.rs。Turso 端的配置MVCCPRAGMA journal_mode mvcc每个 OS 线程一个连接、各自持有独立的单线程 tokio runtime使用BEGIN CONCURRENTLinux 上默认 io_uring 后端DEFAULT_IO在 Linux 上为io_uring见 main.rs。并发事务互不阻塞只在提交时串行化——提交即追加日志记录并 fsync见 README 描述。发现快照过期的事务会重启restart重启次数计入其延迟由于行不相交正常情况下不应发生重启摘要中会报告实际发生的次数。Turso 写入线程捕获turso::Error::BusySnapshot后执行ROLLBACK并重新进入事务循环turso_engine.rs每笔样本的restarts字段记录该事务的重启次数。这里有一个值得留意的工程细节turso_engine.rs 的注释Turso 端刻意不使用共享的多线程 tokio runtime而是每个连接一个 current-thread runtime。原因是在 syscall I/O 后端下事务执行过程中没有任何 yield 点占住 worker 的任务会让 runtime 的计时器无人驱动其他连接可能错过时隙甚至睡到运行结束。检查点Checkpointing两个引擎都按照生产服务器的方式做检查点写者自身的自动检查点关闭SQLite 设wal_autocheckpoint 0Turso 设mvcc_checkpoint_threshold -1由一条独立连接每隔CHECKPOINTER毫秒默认 1000执行PRAGMA wal_checkpoint(PASSIVE)Turso 使用被动检查点模式passive checkpoint把已提交的版本排入 B-tree 而不阻塞写者每次检查点的耗时都会被上报因此可以把写者停顿与造成停顿的检查点一一对应。实现层面两个引擎各自有一个检查点线程/异步任务SQLite 端在独立连接上周期执行PRAGMA wal_checkpoint(PASSIVE)sqlite_engine.rsTurso 端在独立连接上执行相同 pragma 并排空结果行错误通常是 Busy意味着本轮未完成下一轮会重试turso_engine.rs。Turso 的setup还根据模式组合启用被动检查点仅在Concurrent Passive组合下调用Builder::experimental_mvcc_passive_checkpoint(true)turso_engine.rs而--mvcc-checkpoint-threshold可覆盖默认的 MVCC 自动检查点阈值默认约 4.12 MB见 main.rs。若--checkpointer 0则各写者自行在提交路径上自动检查点即引擎开箱即用的行为。运行规则Runs每次运行从自己的全新空数据库文件开始测量 60 秒DURATION前 5 秒为预热WARMUP每次运行前先sync再对数据库所在挂载点执行sudo fstrim随后让磁盘空闲IDLE默认 60秒最后清空页面缓存echo 3 | sudo tee /proc/sys/vm/drop_caches见 bench.sh。原因是消费级 SSD 会在空闲时排空写缓存、做垃圾回收不清洗的话本次运行会继承前几次运行的写积压每个引擎 × 连接数组合重复REPEATS默认 3次每次运行写出独立的样本文件每次运行的摘要包括进程自身 CPU 占用与数据库所在磁盘的活动输出到 stderr 与plot/bench.log。磁盘与 CPU 统计来自内核磁盘统计通过解析/proc/diskstats按设备主次设备号匹配main.rsCPU 统计来自getrusage(RUSAGE_SELF)main.rs。摘要会打印 p50/p99/p99.9/max 分位数、实际达到的吞吐、进程 CPU 占用占单核与全部硬件线程的百分比、磁盘写入量/每写耗时/繁忙占比、以及检查点 p50/max/总耗时report。配置参数全表所有设置都是环境变量由scripts/bench.sh读取run.sh会调用它因此两个脚本都接受这些变量变量默认值含义RATE1000每秒提供的事务数为所有连接的总额CONNECTIONS1 8 16 32要运行的连接数列表每个对应一个图形面板REPEATS3每个引擎和连接数的运行次数每次运行写出独立 CSVIDLE60每次运行前fstrim之后让磁盘空闲的秒数避免本次运行继承之前运行的写积压DURATION60每次运行测量的秒数WARMUP5测量开始前运行的秒数BATCH_SIZE10每个事务插入的行数CHECKPOINTER1000独立连接两次检查点之间的毫秒数0表示让各写者自行检查点ARRIVALSpoissonpoisson用指数间隔围绕1/RATE分布到达时刻fixed则精确间隔1/RATESEED1泊松时刻表的种子相同种子保证每次运行到达时刻一致OUTplot/CSV 与图形输出目录运行拒绝覆盖已存在的 CSVDB_DIRdb/timestamp/数据库文件目录每次运行在那里获得独立文件bench.sh通过cargo build --release -p turso-txn-latency构建基准程序并通过仓库根目录的scripts/cargo-target-dir脚本定位构建产物尊重CARGO_TARGET_DIR设置随后按连接数 × 引擎 × 重复次数三层循环逐个运行bench.sh。每次运行前会检查输出文件是否已存在——存在即报错退出避免覆盖旧数据这是Nothing is ever deleted原则的一部分。日志第一行会记录本次会话的完整配置rate connections repeats idle arrivals seed db。绘制 eCDF 图形绘图脚本 plot-latency-ecdf.py 通过uv run执行run.sh会把所有非 checkpoints 的 CSV 文件作为参数传入uv run plot-latency-ecdf.py sqlite-c1.csv sqlite-c8.csv turso-c1.csv turso-c8.csv \ -o latency-ecdf.png -o latency-ecdf.pdf -o latency-ecdf.tikz脚本要点图形语义x 轴为延迟对数刻度毫秒y 轴为延迟不超过该值的事务占比每条曲线在 p50、p90 处有标记p99.9 用虚线竖线标出并标注数值曲线右端即该引擎最慢的事务。面板布局每个连接数一个面板横向排列、共享坐标轴在同一面板内对比两个引擎跨面板观察并发数的影响。同配置多次运行合并同一引擎、同一连接数的多次运行样本会被合并为一条曲线read_series按(engine, mode, connections)分组并np.concatenate。颜色方案SQLite 与 Turso 使用 Okabe-Ito 色盲安全配色橙#E69F00与蓝#0072B2这也是系统论文常用的配色遇到未知引擎时回退到备选色板。LaTeX 输出.tikz/.tex输出生成 pgfplots 的groupplots图片按\linewidth自适应尺寸论文中可直接\input要求 pgfplots 的groupplots库与\pgfplotsset{compat1.18}。可选参数--column可选择绘制total_ns默认、queue_ns、begin_ns、work_ns、commit_ns中的任意一列用于分解延迟构成--name ENGINENAME可自定义图例名称如tursoLimbo。曲线点采样采用均匀网格 从最慢样本反向的 log 网格双网格抽稀确保最末尾的几个事务也能被单独画出来Curve.points。读取摘要输出每次运行的摘要写入终端与bench.log格式如下各字段含义与告警条件都可从 report 对应[turso/concurrent] N transactions in 60.0s, 1000/s achieved [turso/concurrent] p50 0.23ms p99 1.05ms p99.9 2.10ms max 4.87ms [turso/concurrent] cpu: user 3.2s sys 1.1s 7% of one core over 60.0s, 0.2% of 32 hardware threads [turso/concurrent] disk nvme0n1: 4500 writes, 22.5 MB written, 0.50ms per write, busy 1% of the run [turso/concurrent] checkpointer: 60 checkpoints, p50 0.4ms max 1.2ms total 30ms以上数值仅为示意格式非实测数据。需要特别留意两种告警WARNING: gave up after running Nx past the planned time. This database cannot sustain RATE transactions/s, so the tail here is cut short rather than measured.—— 运行超时被截断尾部数据缺失WARNING: only reached X/s of the Y/s offered. The latencies are real, but this database is past saturation.—— 实际吞吐低于提供速率的 95%数据库已过饱和此时曲线代表的是落后程度而非真实延迟。Turso 端还会额外打印一行turso_engine.rs[turso] io backend io_uring, checkpoint mode Passive, 0 transaction restarts用于核对 I/O 后端、检查点模式与重启次数。基准的工程结构一览该基准是工作区成员perf/latency见根目录 Cargo.toml包名为turso-txn-latency二进制入口为main.rsCargo.toml依赖rusqlite、tokio、turso工作区路径bindings/rust、clap与libc。核心模块划分main.rs参数解析、Pacer到达时刻表、Sample/Checkpoint/Run数据结构、CPU/磁盘统计、摘要报告与 CSV 写出sqlite_engine.rsSQLite 端——WAL、BEGIN IMMEDIATE、busy handler、独立检查点连接turso_engine.rsTurso 端——MVCC、BEGIN CONCURRENT、快照过期重启、tokio current-thread runtime、被动检查点plot-latency-ecdf.pyeCDF 绘图与 pgfplots 生成。适用前提与限制平台需要 LinuxTurso 端默认 io_uring 后端DEFAULT_IO在非 Linux 平台回退为syscall磁盘统计在非 Linux 上不可用。权限完整流程需要sudofstrim与清空页面缓存无 sudo 环境可跳过这两个步骤但要注意测量结果会受磁盘写积压影响。工具链Rustcargo、uv绘图。时长默认完整实验约 1 小时其中大部分是每次运行前的 60 秒磁盘空闲期快速试跑请用CONNECTIONS1 8 REPEATS1 DURATION20这类参数组合。结果解读只有能跟上提供速率achieved ≥ 95% × RATE的运行其曲线才反映真实延迟分布过饱和运行的曲线应视为掉队程度而非延迟。【免费下载链接】tursoA SQL database in Rust: SQLite-compatible, now also speaking Postgres (experimental). The LLVM of databases.项目地址: https://gitcode.com/GitHub_Trending/tu/turso创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表