应预编译语句并配合事务批量插入:先begin开启事务,sqlite3_prepare_v2一次,循环中sqlite3_bind_*→sqlite3_step→sqlite3_reset,最后commit;注意绑定字符串用sqlite_transient确保安全,二进制数据必须用sqlite3_bind_blob;正确检查sqlite3_step返回值,区分sqlite_done、sqlite_busy等状态。

用sqlite3_bind_*配合sqlite3_step循环插入效率太低
单条执行INSERT再sqlite3_step一次,对万级std::vector数据来说,性能会掉一个数量级。根本原因是每次调用都触发完整语句编译、参数绑定、执行、清理流程,还可能反复刷盘。
真正该做的是预编译一次语句,然后复用sqlite3_stmt*,在事务中批量提交:
- 用
sqlite3_exec(db, "BEGIN", nullptr, nullptr, nullptr)手动开启事务(或sqlite3_exec(db, "BEGIN IMMEDIATE", ...)避免写冲突) - 调用
sqlite3_prepare_v2一次,得到sqlite3_stmt* - 对
vector每个元素,调用sqlite3_bind_*→sqlite3_step→sqlite3_reset(不是sqlite3_clear_bindings,后者不重置执行状态) - 最后
sqlite3_exec(db, "COMMIT", ...)
绑定std::vector<:string></:string>时要注意sqlite3_bind_text的生命周期
sqlite3_bind_text默认使用SQLITE_TRANSIENT,意味着SQLite会在sqlite3_step返回后立刻拷贝字符串内容——这对vector里临时std::string对象是安全的;但若你传了&v[i][0]或v[i].c_str()且没保证v[i]在整个事务期间不被移动/销毁,就可能崩溃。
更稳妥的做法是显式传SQLITE_STATIC仅当字符串内存长期有效(比如全局const char*),否则一律用SQLITE_TRANSIENT并确保std::string对象在sqlite3_step完成前不析构:
for (size_t i = 0; i <h3>插入含二进制数据(如<code>std::vector<uint8_t></uint8_t></code>)必须用<code>sqlite3_bind_blob</code> </h3><p><code>sqlite3_bind_text</code>会把<code>uint8_t*</code>当C字符串处理,遇到<code><p><code>sqlite3_bind_text</code>会把<code>uint8_t*</code>当C字符串处理,遇到<code>\0</code>就截断;直接<code>reinterpret_cast<const char>(vec.data())</const></code>传给<code>sqlite3_bind_text</code>是常见错误。</p></code>就截断;直接<code>reinterpret_cast<const char>(vec.data())</const></code>传给<code>sqlite3_bind_text</code>是常见错误。</p><div class="aritcle_card flexRow artxards"> <div class="artcardd flexRow"> <a class="aritcle_card_img" rel="nofollow" href="/xiazai/shouce/1510" title="C函数速查手册(CHM版)"><img src="https://img.php.cn/upload/manual/000/000/001/5d6de31fedca2993.png" alt="C函数速查手册(CHM版)" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a> <div class="aritcle_card_info flexColumn"> <a rel="nofollow" href="/xiazai/shouce/1510" title="C函数速查手册(CHM版)" class="overflowclass">C函数速查手册(CHM版)</a> <p class="overflowclass">C函数速查手册(CHM版)</p> </div> <a rel="nofollow" href="/xiazai/shouce/1510" title="C函数速查手册(CHM版)" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a> </div> </div><p>正确方式:</p>
- 用
sqlite3_bind_blob(stmt, idx, vec.data(), vec.size(), SQLITE_STATIC)—— 注意这里用SQLITE_STATIC,因为vec.data()的生命周期由std::vector管理,只要vec不被clear()或resize(),内存就有效 - 如果
vec在循环中是局部变量,必须保证它在整个事务结束前不销毁(比如把vector移到函数外或用std::move传入,但注意移动后原对象不可再访问)
别忽略sqlite3_step返回值检查,尤其SQLITE_DONE和SQLITE_BUSY
很多人只检查!= SQLITE_OK,但sqlite3_step成功返回的是SQLITE_DONE(单行执行完毕),不是SQLITE_OK;而SQLITE_BUSY表示锁冲突,需重试,不是致命错误。
典型误写:if (sqlite3_step(stmt) != SQLITE_OK) { /* error */ } —— 这会让所有正常插入都被判错。
正确判断逻辑:
int rc = sqlite3_step(stmt);
if (rc == SQLITE_DONE) {
// 成功
} else if (rc == SQLITE_BUSY || rc == SQLITE_LOCKED) {
// 等待后重试,或改用 BEGIN IMMEDIATE 提前抢占
} else {
// 真正出错,查 sqlite3_errmsg(db)
}
事务越长,锁等待风险越高,BEGIN IMMEDIATE比BEGIN更能减少SQLITE_BUSY发生概率,但会阻塞其他写操作。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










