sqlite3_exec最简单但回调不可耗时;预编译流程更安全可控;需静态链接、最小化配置、适配内存与存储约束。

用 sqlite3_exec 执行 SQL 语句最简单,但别在回调里做耗时操作
嵌入式场景下,sqlite3_exec 是最快上手的方式,适合建表、插入单条记录、执行 DDL/DML。它把 SQL 字符串和回调函数一起扔给 SQLite,内部自动完成编译、执行、清理。
常见错误是误以为回调函数(第 4 个参数)能用来“取数据”,结果发现只调用一次、或者根本没进回调——其实是 SQL 没返回结果集(比如 INSERT 不带 RETURNING),或者传了 NULL 回调却期望拿到数据。
- 只读查询(如
SELECT)必须提供非NULL回调,否则查不到结果 - 回调函数里别调用
sqlite3_exec或其他阻塞操作,容易卡住主线程(尤其在裸机或 FreeRTOS 环境) - 字符串参数要用
sqlite3_mprintf或手动转义单引号,直接拼接用户输入会触发 SQL 注入
sqlite3_prepare_v2 + sqlite3_step 是安全读写的标配
需要反复执行同一条语句(比如传感器数据批量写入)、或要精确控制每一步(比如判断是否主键冲突、获取自增 ID),就得用预编译流程。它比 sqlite3_exec 多两步,但换来的是参数绑定、错误定位和资源可控。
典型坑是忘记调用 sqlite3_finalize —— 每次 prepare 都分配内存,不释放会导致句柄泄漏,在长期运行的嵌入式设备上几小时就崩。
立即学习“C++免费学习笔记(深入)”;
- 绑定参数用
sqlite3_bind_text/sqlite3_bind_int,别用sqlite3_bind_blob传字符串(编码易错) -
sqlite3_step返回SQLITE_ROW表示有数据可读,SQLITE_DONE表示执行完,SQLITE_BUSY要重试(尤其多线程写时) - 嵌入式 Flash 存储下,频繁
INSERT建议加BEGIN IMMEDIATE事务,否则每次写都刷盘,寿命掉得快
编译时链接 sqlite3.o 而不是动态库,避免运行时找不到
嵌入式环境基本没有 libsqlite3.so,必须静态链接。官方源码里的 sqlite3.c 是单文件实现,直接加进工程就行,但要注意编译选项。
常见错误是开了 SQLITE_ENABLE_FTS5 或 SQLITE_ENABLE_JSON1 —— 这些模块在 Cortex-M3/M4 上可能吃掉几百 KB Flash,而多数传感器应用根本用不到全文检索或 JSON 解析。
- 最小化配置推荐加:
-DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_OMIT_COMPLETE - 如果用 C++ 编译,把
sqlite3.c加进 C 编译器(不是 C++),否则 C++ name mangling 会导致undefined reference to sqlite3_open - 裸机环境下,确认
malloc已适配你的堆管理(比如用pvPortMalloc替换标准 malloc),SQLite 内部大量依赖它
数据库路径写成 "/data/db.sqlite" 前先检查挂载点和权限
Linux/RTOS 下路径看似简单,实则最容易出 runtime 错误:目录不存在、Flash 分区只读、UID/GID 无写权限。SQLite 打开失败时返回 SQLITE_CANTOPEN,但不会告诉你具体卡在哪一层。
一个被忽略的细节是:SQLite 默认以 0600 创建文件,如果挂载时指定了 uid/gid 或用了 overlayfs,新文件可能属于 root,后续普通用户进程就打不开。
- 打开前用
access("/data", W_OK)检查目录可写,比靠sqlite3_open返回值更早暴露问题 - 避免用相对路径(如
"db.sqlite"),嵌入式程序工作目录不可控,绝对路径才可靠 - 如果 Flash 寿命敏感,考虑把 WAL 日志关掉:
PRAGMA journal_mode = DELETE,虽然并发差一点,但少一次写放大
sqlite3_step 和 sqlite3_exec 的每一次调用里。










