惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Martin Fowler
Martin Fowler
T
The Blog of Author Tim Ferriss
J
Java Code Geeks
M
MIT News - Artificial intelligence
F
Fortinet All Blogs
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
B
Blog
Microsoft Azure Blog
Microsoft Azure Blog
I
InfoQ
Microsoft Security Blog
Microsoft Security Blog
N
Netflix TechBlog - Medium
G
Google Developers Blog
L
LangChain Blog
腾讯CDC
大猫的无限游戏
大猫的无限游戏
U
Unit 42
Google DeepMind News
Google DeepMind News
人人都是产品经理
人人都是产品经理
罗磊的独立博客
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
小众软件
小众软件
The GitHub Blog
The GitHub Blog
博客园_首页
GbyAI
GbyAI

SQLite

sqlite 性能是否够用?看上去并发似乎还是很强 - V2EX 分享适合高并发场景的 SQLite 设置 在什么情况下一定要放弃 SQLite 采用 MySQL 呢? SQLite 背后的故事 应该怎么理解 rowid 在 sqlite 中的使用 macOS 上有什么好用的 SQLite GUI ? SQLite In-Memory 作为数据缓存代替 Java Object 什么时候调用 sqlite.close()呢 回复“什么场景下用 SQLite”的主题并分享一个年收入 60 万刀的技术栈 大家有用 Sqlite 的吗,都是什么场景下要用这个,能说一两点非要用它的理由最好啦 SQLite Viewer Web App 本地数据库 除了 SQLite 还有什么好用的. 请教一个方案,边缘设备的 sqlite 数据如何才能获取到 有人知道怎么在 termux 上编译 sqlcipher 吗? 求一个数据库设计问题! SQLite 被曝漏洞,影响范围很大 两个读写 SQLite 数据库的浏览器扩展 How SQLite is tested 如果需要寫入數據, 不僅數據文件要有寫入權限, 數據文件所在的目錄也要有寫入權限 用sqlite3作为论坛的数据库行不行? About Sqlite 之前的一篇笔记 如何把一个2G的sqlite数据库分割成小的? Base 2.0
SQLite 开启 WAL 后,我的同步服务吞吐量提升了 3 倍
AhFei · 2026-06-07 · via SQLite

SQLite 默认未开启 WAL ,这会显著限制并发性能。

PlanTodo 是一个计划管理软件,最近我为它的同步服务编写了性能测试,经过实测,仅开启 WAL 就让同步服务的吞吐量便提升至原来的 3 倍,

其实关于 SQLite 性能优化的文章早有珠玉在前,比如 Optimal SQLite settings for DjangoOptimizing SQLite for servers ,所以本篇文章并没有独创性,只是为了让更多人了解 SQLite 的性能以及分享一个真实的性能测试用例

PlanTodo 同步服务性能测试

性能测试分为三个:

  1. oo_upload (one user, one device for a user, only upload) ,就是一个用户一台设备仅上传
  2. oo_download 就是仅下载
  3. oo_cross 是上传和下载交错进行

oo_upload 和 oo_download 是为了查看上传和下载场景下的极限性能,是为了将来专门优化时用来参考的。而 oo_cross 则较为贴近真实使用场景:用户的某个设备上传几个更新,另一个设备被触发下载;因此可以拿它计算服务器能承受的用户量。

如果你不想看下面具体的测试数据,这里简单展示了吞吐量的变化:

  1. oo_upload ,18027 -> 61682 ,是原来的 3.42 倍
  2. oo_download ,17082 -> 49635 ,是原来的 2.90 倍
  3. oo_cross ,17085 -> 44203 ,是原来的 2.58 倍

一般查询是比写入要快的,因此下载应该比上传快,但 PlanTodo 的同步服务却反了过来,说明有很大的优化空间。


下面是开启 WAL 前的测试数据:

+ just -f services/sync/justfile headless_oo_upload --less-output
============================================================
Performance Summary for test_oo_upload
============================================================
Requests      : 18,027
Failures      : 0
Failure Rate  : 0.00%

Average RT    : 78.14 ms
P50           : 78 ms
P95           : 110 ms
P99           : 130 ms
Max           : 272.81 ms

Endpoints
------------------------------------------------------------
POST /v1/sync/delta
  Requests=18,011  Avg=78.1ms  P95=110ms  P99=130ms  Max=272.8ms

POST /v1/clients
  Requests=8  Avg=76.2ms  P95=110ms  P99=110ms  Max=109.9ms

GET /v1/sync/full
  Requests=8  Avg=57.2ms  P95=120ms  P99=120ms  Max=120.3ms


+ just -f services/sync/justfile headless_oo_download --less-output
============================================================
Performance Summary for test_oo_download
============================================================
Requests      : 17,082
Failures      : 0
Failure Rate  : 0.00%

Average RT    : 82.63 ms
P50           : 82 ms
P95           : 110 ms
P99           : 130 ms
Max           : 370.33 ms

Endpoints
------------------------------------------------------------
POST /v1/sync/delta
  Requests=16  Avg=172.6ms  P95=370ms  P99=370ms  Max=370.3ms

GET /v1/sync/delta?cursor=1780662404990&limit=100
  Requests=2,087  Avg=83.4ms  P95=110ms  P99=130ms  Max=316.1ms

GET /v1/sync/delta?cursor=1780662403978&limit=100
  Requests=2,102  Avg=83.3ms  P95=110ms  P99=130ms  Max=325.5ms


+ just -f services/sync/justfile headless_oo_cross --less-output
============================================================
Performance Summary for test_oo_cross
============================================================
Requests      : 17,085
Failures      : 0
Failure Rate  : 0.00%

Average RT    : 83.05 ms
P50           : 82 ms
P95           : 120 ms
P99           : 150 ms
Max           : 245.89 ms

Endpoints
------------------------------------------------------------
GET /v1/sync/delta?cursor=1780662760888&limit=100
  Requests=1  Avg=245.9ms  P95=250ms  P99=250ms  Max=245.9ms

GET /v1/sync/delta?cursor=1780662760900&limit=100
  Requests=1  Avg=245.4ms  P95=250ms  P99=250ms  Max=245.4ms

GET /v1/sync/delta?cursor=1780662758760&limit=100
  Requests=1  Avg=228.2ms  P95=230ms  P99=230ms  Max=228.2ms

下面是开启 WAL 之后的测试数据:

+ just -f services/sync/justfile headless_oo_upload --less-output
============================================================
Performance Summary for test_oo_upload
============================================================
Requests      : 61,682
Failures      : 0
Failure Rate  : 0.00%

Average RT    : 22.30 ms
P50           : 22 ms
P95           : 32 ms
P99           : 41 ms
Max           : 74.82 ms

Endpoints
------------------------------------------------------------
POST /v1/clients
  Requests=8  Avg=33.8ms  P95=48ms  P99=48ms  Max=48.1ms

POST /v1/sync/delta
  Requests=61,666  Avg=22.3ms  P95=32ms  P99=41ms  Max=74.8ms

GET /v1/sync/full
  Requests=8  Avg=20.7ms  P95=32ms  P99=32ms  Max=32.5ms


+ just -f services/sync/justfile headless_oo_download --less-output
============================================================
Performance Summary for test_oo_download
============================================================
Requests      : 49,635
Failures      : 0
Failure Rate  : 0.00%

Average RT    : 28.03 ms
P50           : 28 ms
P95           : 38 ms
P99           : 46 ms
Max           : 305.64 ms

Endpoints
------------------------------------------------------------
POST /v1/sync/delta
  Requests=16  Avg=127.0ms  P95=310ms  P99=310ms  Max=305.6ms

GET /v1/sync/delta?cursor=1780663066610&limit=100
  Requests=6,109  Avg=28.4ms  P95=38ms  P99=46ms  Max=246.0ms

GET /v1/sync/delta?cursor=1780663068640&limit=100
  Requests=6,064  Avg=28.3ms  P95=38ms  P99=46ms  Max=95.8ms


+ just -f services/sync/justfile headless_oo_cross --less-output
============================================================
Performance Summary for test_oo_cross
============================================================
Requests      : 44,203
Failures      : 0
Failure Rate  : 0.00%

Average RT    : 31.80 ms
P50           : 28 ms
P95           : 45 ms
P99           : 150 ms
Max           : 477.37 ms

Endpoints
------------------------------------------------------------
GET /v1/sync/delta?cursor=1780663421960&limit=100
  Requests=1  Avg=477.4ms  P95=480ms  P99=480ms  Max=477.4ms

GET /v1/sync/delta?cursor=1780663421963&limit=100
  Requests=1  Avg=475.9ms  P95=480ms  P99=480ms  Max=475.9ms

GET /v1/sync/delta?cursor=1780663421966&limit=100
  Requests=1  Avg=475.4ms  P95=480ms  P99=480ms  Max=475.4ms

每个测试只持续了 3 min ,因此数据量很小,在大数据量的情况下,性能可能会下降很多,这个会在将来补充。

在测试 oo_cross 里,3min 处理了 44203 个请求,也就是一秒 245 个。测试其实是不断在重复先上传再下载,而客户端也是上传下载成对出现,因此一秒能处理 122 ( 245 / 2 )台设备的请求,即便因为多用户、多设备带来的其他压力,至少能稳定在一秒 100 个请求。

考虑到,真实使用下,平均要几分钟到十几分钟才会更新一次内容,取 10 分钟一次的话,一个同步服务极限下能支撑 100 x 60 x 10 ,6 万个设备正常使用。

如何开启 WAL

不管是什么 ORM ,开启的方式都是一样的,就是在连接数据库后,执行一次 PRAGMA journal_mode=WAL

开启了 WAL 后,在原本的 SQLite 数据库文件那里,会多出两个 .db-shm.db-wal 的文件,可以以此判断是否成功开启。

SQLAlchemy

from sqlalchemy import create_engine, event

engine = create_engine(
    DATABASE_URL,
    echo=False,
    connect_args={
        "timeout": 5,
    },
)

if engine.dialect.name == "sqlite":
    @event.listens_for(engine, "connect")
    def set_sqlite_pragma(dbapi_connection, _):
        cursor = dbapi_connection.cursor()

        # cursor.execute("PRAGMA foreign_keys=ON")
        cursor.execute("PRAGMA journal_mode=WAL")
        cursor.execute("PRAGMA synchronous=NORMAL")
        cursor.execute("PRAGMA temp_store=MEMORY")
        cursor.execute("PRAGMA cache_size=2000")
        cursor.execute("PRAGMA mmap_size=134217728")

        cursor.close()

PlanTodo 的同步服务就是使用 FastAPI + SQLAlchemy 开发的。由于同步服务的特殊性,这里没有开启外键约束。

Django

在项目的 settings.py 文件里,添加 init_command:

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.sqlite3",
        "NAME": BASE_DIR / "db.sqlite3",
        "OPTIONS": {
            "init_command": (
                "PRAGMA foreign_keys = ON;"
                "PRAGMA journal_mode = WAL;"
                "PRAGMA synchronous = NORMAL;"
                "PRAGMA busy_timeout = 5000;"
                "PRAGMA temp_store = MEMORY;"
                "PRAGMA cache_size = 2000;"
                "PRAGMA mmap_size = 134217728;"
            ),
        },
    }
}

drift

在数据库类的 migration get 方法里,在 beforeOpen 这个回调里增加执行命令:

class PtdDatabase extends _$PtdDatabase {

  // 省略无关代码

  @override
  MigrationStrategy get migration {
    return MigrationStrategy(
      beforeOpen: (details) async {
        // 在每次打开数据库,正式使用之前,执行的命令
        await customStatement('PRAGMA journal_mode = WAL');
        await customStatement('PRAGMA synchronous = NORMAL');
        await customStatement('PRAGMA busy_timeout = 5000');
        await customStatement('PRAGMA temp_store = MEMORY');
        await customStatement('PRAGMA cache_size = -2000');
      },
    );
  }
}

原文链接: https://yanh.tech/2026/06/sqlite-performance-optimization/

版权声明:本博客所有文章除特別声明外,均为 AhFei 原创,采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 技焉洲 (yanh.tech)