提升SQLite数据插入效率低、速度慢的方法
0 前言
SQLite数据库由于其简单、灵活、轻量、开源,已经被越来越多的被应用到中小型应用中。甚至有人说,SQLite完全可以用来取代c语言中的文件读写操作。因此我最近编写有关遥感数据处理的程序的时候,也将SQLite引入进来,以提高数据的结构化程度,并且提高大数据的处理能力(SQLite最高支持2PB大小的数据)。但是最开始,我发现,直接使用SQL语句的插入效率简直低的令人发指的。后来不断查文档、查资料,才发现了一条快速的“数据插入”之路。本文就以插入数据为例,整合网上和资料书中的各种提高SQLite效率的方法,给出提高SQLite数据插入效率的完整方法。(大神们勿喷)
1 数据
我使用的电脑是Win7 64位系统,使用VC2010编译,SQLIte版本为3.7.15.2 ,电脑CPU为二代i3处理器,内存6G。
实验之前,先建立要插入数据的表:
create table t1 (id integer , x integer , y integer, weight real)
2 慢速——最粗暴的方法
SQLite的API中直接执行SQL的函数是:
int sqlite3_exec( sqlite3*, const char *sql, int (*callback)(void*,int,char**,char**), void *, char **errmsg)
直接使用INSERT语句的字符串进行插入,程序部分代码(完整代码见后文),如下:
for(int i=0;i<nCount;++i) {std::stringstream ssm;ssm<<"insert into t1 values("<<i<<","<<i*2<<","<<i/2<<","<<i*i<<")";sqlite3_exec(db,ssm.str().c_str(),0,0,0);}
这个程序运行的太慢了,我已经没时间等待了,估算了一下,基本上是 7.826 条/s
3 中速——显式开启事务
所谓”事务“就是指一组SQL命令,这些命令要么一起执行,要么都不被执行。在SQLite中,每调用一次sqlite3_exec()函数,就会隐式地开启了一个事务,如果插入一条数据,就调用该函数一次,事务就会被反复地开启、关闭,会增大IO量。如果在插入数据前显式开启事务,插入后再一起提交,则会大大提高IO效率,进而加数据快插入速度。
开启事务只需在上述代码的前后各加一句开启与提交事务的命令即可:
sqlite3_exec(db,"begin;",0,0,0);for(int i=0;i<nCount;++i){std::stringstream ssm;ssm<<"insert into t1 values("<<i<<","<<i*2<<","<<i/2<<","<<i*i<<")";sqlite3_exec(db,ssm.str().c_str(),0,0,0);}sqlite3_exec(db,"commit;",0,0,0);
显式开启事务后,这个程序运行起来明显快很多,估算效率达到了34095条/s,较原始方法提升约5000倍。
4 高速——写同步(synchronous)
我要使用一个遥感处理算法处理10000*10000的影像,中间有一步需要插入100000000条数据到数据库中,如果按照开启事务后的速度34095条/s,则需要100000000÷34095 = 2932秒 = 48.9分,仍然不能够接受,所以我接着找提升速度的方法。终于,在有关讲解SQLite配置的资料中,看到了“写同步”选项。
在SQLite中,数据库配置的参数都由编译指示(pragma)来实现的,而其中synchronous选项有三种可选状态,分别是full、normal、off。这篇博客以及官方文档里面有详细讲到这三种参数的设置。简要说来,full写入速度最慢,但保证数据是安全的,不受断电、系统崩溃等影响,而off可以加速数据库的一些操作,但如果系统崩溃或断电,则数据库可能会损毁。
SQLite3中,该选项的默认值就是full,如果我们再插入数据前将其改为off,则会提高效率。如果仅仅将SQLite当做一种临时数据库的话,完全没必要设置为full。在代码中,设置方法就是在打开数据库之后,直接插入以下语句:
sqlite3_exec(db,"PRAGMA synchronous = OFF; ",0,0,0);
此时,经过测试,插入速度已经变成了 41851条/s,也就是说,插入100000000条数据,需要2389秒 = 39.8分。
5 极速——执行准备
虽然写同步设为off后,速度又有小幅提升,但是仍然较慢。我又一次踏上了寻找提高SQLite插入效率方法的道路上。终于,我发现,SQLite执行SQL语句的时候,有两种方式:一种是使用前文提到的函数sqlite3_exec(),该函数直接调用包含SQL语句的字符串;另一种方法就是“执行准备”(类似于存储过程)操作,即先将SQL语句编译好,然后再一步一步(或一行一行)地执行。如果采用前者的话,就算开起了事务,SQLite仍然要对循环中每一句SQL语句进行“词法分析”和“语法分析”,这对于同时插入大量数据的操作来说,简直就是浪费时间。因此,要进一步提高插入效率的话,就应该使用后者。
“执行准备”主要分为三大步骤:
1.调用函数
int sqlite3_prepare_v2( sqlite3 *db, const char *zSql, int nByte, sqlite3_stmt **ppStmt, const char **pzTail);
并且声明一个指向sqlite3_stmt对象的指针,该函数对参数化的SQL语句zSql进行编译,将编译后的状态存入ppStmt中。
2.调用函数 sqlite3_step() ,这个函数就是执行一步(本例中就是插入一行),如果函数返回的是SQLite_ROW则说明仍在继续执行,否则则说明已经执行完所有操作;
3.调用函数 sqlite3_finalize(),关闭语句。
关于执行准备的API的具体语法,详见官方文档。本文中执行准备的c++代码如下:
sqlite3_exec(db,"begin;",0,0,0);sqlite3_stmt *stmt;const char* sql = "insert into t1 values(?,?,?,?)";sqlite3_prepare_v2(db,sql,strlen(sql),&stmt,0);for(int i=0;i<nCount;++i){sqlite3_reset(stmt);sqlite3_bind_int(stmt,1,i);sqlite3_bind_int(stmt,1,i*2);sqlite3_bind_int(stmt,1,i/2);sqlite3_bind_double(stmt,1,i*i);}sqlite3_finalize(stmt);sqlite3_exec(db,"commit;",0,0,0);
此时测试数据插入效率为: 265816条/s,也就是说,插入100000000条数据,需要376秒 = 6.27分。这个速度已经很满意了。
5 总结
综上所述啊,SQLite插入数据效率最快的方式就是:事务+关闭写同步+执行准备(存储过程),如果对数据库安全性有要求的话,就开启写同步。
参考资料:
1. SQLite官方文档: http://www.sqlite.org/docs.html
2.《解决sqlite3插入数据很慢的问题》: http://blog.csdn.net/victoryknight/article/details/7461703
3.《The Definitive Guide to SQLite》Apress出版: http://www.apress.com/9781430232254 (这是本好书)
附最终完整代码:
#include <iostream>#include <string>#include <sstream>#include <time.h>#include "sqlite3.h"const int nCount = 500000;int main (int argc,char** argv){sqlite3* db;sqlite3_open("testdb.db" ,&db);sqlite3_exec(db,"PRAGMA synchronous = OFF; ",0,0,0);sqlite3_exec(db,"drop table if exists t1",0,0,0);sqlite3_exec(db,"create table t1(id integer,x integer,y integer ,weight real)",0,0,0);clock_t t1 = clock();sqlite3_exec(db,"begin;",0,0,0);sqlite3_stmt *stmt;const char* sql = "insert into t1 values(?,?,?,?)";sqlite3_prepare_v2(db,sql,strlen(sql),&stmt,0);for(int i=0;i<nCount;++i){// std::stringstream ssm;// ssm<<"insert into t1 values("<<i<<","<<i*2<<","<<i/2<<","<<i*i<<")";// sqlite3_exec(db,ssm.str().c_str(),0,0,0);sqlite3_reset(stmt);sqlite3_bind_int(stmt,1,i);sqlite3_bind_int(stmt,2,i*2);sqlite3_bind_int(stmt,3,i/2);sqlite3_bind_double(stmt,4,i*i);sqlite3_step(stmt);}sqlite3_finalize(stmt);sqlite3_exec(db,"commit;",0,0,0);clock_t t2 = clock();sqlite3_close(db);std::cout<<"cost tima: "<<(t2-t1)/1000.<<"s"<<std::endl;return 0;}
本文转载自:https://blog.csdn.net/chenguanzhou123/article/details/9376537 (原文作者已失联,在此转载做学习使用)
提升SQLite数据插入效率低、速度慢的方法相关推荐
- mybatis-plus 批量插入效率低的问题【重写sql注入 SqlInjector】
背景 由于项目中需要大批量将数据插入数据库,直接使用mybatis-plus中的批量插入方法,结果发现效率奇低无比,线上批量插入一千条数据居然花销八九秒的时间.而我们的目标是想要单次插入一万条数据,这 ...
- c mysql 插入大量数据_C++操作MySQL大量数据插入效率低下的解决方法
通常来说C++操作MySQL的时候,往Mysql中插入10000条简单数据,速度非常缓慢,居然要5分钟左右, 而打开事务的话,一秒不到就搞定了! 具体实现代码如下: #include #include ...
- SQLite批量插入效率
一.循环单条数据插入 SQLite插入单条数据效率并不高,原因是: Because it does not have a central server to coordinate access, SQ ...
- 玩转SQLite4:SQLite数据插入与查看
之前两篇文章,介绍了命令行和图形化的方式进行数据库的创建和表的创建,相当于创建了一个框架,还没有具体数据,本篇就来介绍如何将数据添加到数据库的表中,以及如何查看表中的数据. 同样,本篇继续使用命令行和 ...
- 提升R代码运算效率的11个实用方法——并行、效率
转载于36大数据,原文作者:Selva Prabhakaran 译者:fibears 众所周知,当我们利用R语言处理大型数据集时,for循环语句的运算效率非常低.有许多种方法可以提升你的代码运算效率 ...
- 使用临时表关联数据时效率低的问题
今天我们在调查一个数据传送问题时,发现数据传输量比较大,造成传输超时.我们仔细的审查了一次数据表中的关联关系,我们发现 有接近20张表在进行关联,一个业务表的数据经过10多个基础关联后,数据量有几千条 ...
- SQLITE高速插入数据
SQLITE数据库在写入数据时,写入数据库的速度非常重要,如果写入的速度慢,有大量数据待写入时,一是会造成程序卡顿,二是数据写入会失败,数据缺失. 传统的往数据库里插入数据是执行一条SQL语句,多条数 ...
- Sqlite大批量导入数据提高效率的几种方式
前言 SQLite数据库由于其简单.灵活.轻量.开源,已经被越来越多的被应用到中小型应用中.甚至有人说,SQLite完全可以用来取代c语言中的文件读写操作.因此我最近编写有关遥感数据处理的程序的时候, ...
- 提升实景三维建模效率?这个问题不能忽视
不久前,自然资源部发布<自然资源部办公厅关于全面推进实景三维中国建设的通知>,提出到2025年,实现5米格网的地形级实景三维对全国陆地及主要岛屿覆盖,初步实现5厘米分辨率的城市级实景三维对 ...
- SQL必知必会-数据插入
INSERT 顾名思义,INSERT用来将行插入(或添加)到数据库表.插入有几种方式: 1.插入完整的行: 2.插入行的一部分: 3.插入某些查询的结果. 把数据插入表中的最简单方法是使用基本的INS ...
最新文章
- 蓝牙的自适应跳频技术
- 本科理工男如何学习Linux
- java操作storm,Storm集群常用批量操作命令
- Web服务器面临的五种应用层DOS威胁
- algorithm头文件函数全集——史上最全,最贴心
- 减治法在查找算法中的应用(JAVA)--折半查找
- 盘点 Greenplum 数据库的十大特点
- 信息学奥赛一本通 2022:【例4.7】最小n值
- java csv 双引号_Java-使用Scess编写CSV时从字符串类型数据中删除双引号
- (189)FPGA变量初始化方法reg
- 图片作为背景并且是链接的写法(背景图片加链接)
- 数据库导出成txt文件
- ssh远程登录报错Warning: Permanently added ‘111.124.131.312‘ (ECDSA) to the list of known hosts.
- CSDN开发者云平台体验
- 模拟电子技术基础 第二章 常用半导体器件原理
- idea打包jar的多种方式
- dellR230服务器如何进PE系统,戴尔服务器怎么进入u盘启动模式 选择oneshotbios
- 一、Filter的配置项dispatcher
- 卸载企业版360 含密码
- iOS包体积优化实践
热门文章
- free 显示系统内存的使用情况
- 前端页面怎么办啊一条长的信息换行展示_前端入门!不容错过!HTML基本标签知识大盘点...
- 小米总参php面试题_小米2019年PHP工程师面试题和答案解析
- html代码表白_七夕表白代码,樱花特效+爱心特效+花瓣+评论留言功能等
- python dataframe 取每行的最大值_在pandas DataFrame中查找列的值最大的行
- xcode动态改变窗口大小_[SwiftUI 100天] 在 SwiftUI 中动态过滤 @FetchRequest
- java怎么控制页面打印次数_java – 控制打印页面上面板的大小
- mysql教程泰牛程序员_mysql高级教程笔记.docx
- java public就是可选吗_java 中的public
- Java并发基础问题总结