Sqlite学习笔记(二)&&性能测试

测试目标

获取SQlite的常规性能指标

测试环境

CPU:8核,Intel(R) Xeon(R) CPU E5-2430 0 @ 2.20GHz

内存:16G

磁盘:SSD

Linux 2.6.32

SQlite最新版本3.8.11

测试场景

1)  主键查询测试

2)  主键更新测试

3)  批量导入测试

初始化

1)  测试表结构

CREATE TABLE user(
id integer primary key autoincrement,
c1 int,
c2 varchar(1000),
c3 varchar(1000));
CREATE TABLE orders(
id integer primary key autoincrement,
user_id int,
c1 varchar(1000),
c2 varchar(1000));

2)  初始化数据

通过程序往user表和orders表中导入10w条记录,整个db文件在400M左右。

3)  测试说明

sqlite本身通过PRAGMA命令可以设置程序缓存大小( cache_size),但同时sqlite的缓存策略中并没有忽略操作系统缓存的影响,因此本文的测试结果使用默认的cache_size(2000个page),通过多次测试取平均值,来得到一个大概的性能指标。此外,sqlite主要用于嵌入式设备,而本文的测试基于PC,因此测试数据仅作参考。

单表主键查询

1) 测试说明

该项测试主要测试主键查询的性能,测试语句形如:

“select * from user where id = xxx”,xxx通过随机函数生成,由于生成的测试数据id的范围是[1-100000],通过随机函数生成[1-1000000]的随机数,基本能保证1%的命中率(实际测试中得到印证)。Sqlite支持读并发,因此该项测试测试了多线程并发情况下的性能,测试结果的时间单位为毫秒(ms)。多线程测试模型很简单,每个线程执行同样的查询10w次,计算总耗时时间,然后根据平均值与时间的比值,计算出QPS和TPS,通过参数SQLITE_OPEN_SHAREDCACHE控制是否启用共享缓存模式。

2)   测试结果

a)  非共享缓存模式


线程数目


1


2


4


8


第一轮


2886


3641


8392


19615


第二轮


2867


3933


8088


21010


第三轮


2821


4131


8077


21220


第四轮


2941


4011


7787


20983


第五轮


2896


3724


7881


21332


平均值


2881


3949


7958


21136


CPU%


80%


180%


320%


670%


QPS


34w


50.6w


50.2w


37.85w

表一

b)  共享缓存模式


线程数目


1


2


4


第一轮


3050


12616


26554


第二轮


3077


12331


26396


第三轮


3131


12327


27070


第四轮


3096


13014


27031


第五轮


2972


12866


27778


平均值


3065


12634


26965


CPU%


80%


120%


120%


QPS


32.6w


15.8w


14.8w

表二

3) 结果分析

从表一结果来,随着并发度提升,主机CPU利用率也随着上升;QPS由单线程34w,上升到4线程并发50w左右,但是到8线程又出现了一定的回落,这说明,在高并发情况下,QPS由于其它因素,比如磁盘IO,或者程序本身的并发问题,会达到一定的瓶颈。从绝对值来看每秒50w的查询性能,也确实很不错!

从表二结果来看,设置共享缓存模式后,并发性能有很大的下降,从CPU利用率就可见一斑,QPS由单线程32.6w降低到8线程14.8w左右。关于这一点我一直很疑惑,为啥开了共享缓存后,并发性能还下降了。通过在程序运行过程中抓取堆栈并结合源码找到了原因,并发查询时,大量的线程会堵塞在sqlite3BtreeEnter函数中的mutex里面。共享内存模式下,进程内的多个线程通过共享同一个B树对象,达到共享内存的目的,B树对象通过一个mutex保护,正是由于这个mutex的竞争,导致并发度严重下降。所以共享内存模式虽然能减少内存的使用,但是以牺牲并发性能为代价的。

批量载入测试

1) 测试说明

导入数据是db最常用的一个功能,该项测试主要测试了3种模式的导入性能,单行单事务,多行事务和prepare模式的多行事务。主要模型如下:

a) 单行单事务

begin
insert into user values(1,’xxx’);
commit;

begin
insert into user values(1,’xxx’);
commit;
……

b)  多行单事务

begin
insert into user values(1,’xxx’);insert into user values(2,’xxx’);……
commit;

c)  prepare绑定

begin
prepare insert into user(id, c1) values(?,?);
bind (id,c1)
……
commit;

2)  测试结果


单行事务


10w行事务


10w行事务

(prepare)


第一轮


1693533


11856


9079


第二轮


1673983


11667


8375


第三轮



12075


8566


第四轮



11611


8773


第五轮



11331


8660


平均值


11671


8593


TPS


60


8568


1.16w

表三

3) 结果分析

从测试结果来看,单行事务和多行事务差别非常大,这也充分说明了,对于db而言,事务提交动作是非常耗时的。单行事务TPS只有60,而10w行事务TPS则达到了8500,有超过100倍的提升。与传统DBMS一样,sqlite提交事务时,也需要进行较慢的刷盘动作,因此刷1次盘与刷10w次盘,性能差别非常大。第三栏是prepare类型的事务,也是采用了10w行作为一个事务单位,但效果会更优。这主要原因是采用prepare模型事务,10w行记录只需要解析1次,而前者需要解析10w次,虽然解析时间不长,但积少成多,所以第三栏仅仅这一个优化点,就将TPS从8500提升到1.16w。

主键更新

1) 测试说明

本测试用例的语句也非常简单,就是简单的主键更新,将列值自增1。测试语句形如:update user set c1=c1+1 where id=xxx。SQLite不支持并发更新,因此测试写都是单线程。分别模拟单行事务,多行事务,观察SQLite的更新性能。

2) 测试结果


单行事务


1000行事务


1w行事务


第一轮


164784


16623


16232


第二轮


170256


16382


17514


第三轮


166387


17099


17696


第四轮


172987


17030


17753


第五轮


166543


16386


17787


平均值


169043


16724


17832


TPS


59


598


560.7

表四

3) 结果分析

关于多行事务这一块,基本与导入操作类似,多行事务可以显著提高性能。同时,也要看到更新的TPS相比插入的TPS要相差很多。个人推断这个现象与磁盘IO有莫大关系,因为插入时,由于主键自增,写都是顺序写;而本测例的更新都是随机更新,而且产生的脏页远远大于cache_size,一定伴随着大量的随机写,导致更新性能比较差。

时间: 2024-11-05 12:24:34

Sqlite学习笔记(二)&&性能测试的相关文章

SQLite介绍、学习笔记、性能测试

SQLite介绍.学习笔记.性能测试 哪些人,哪些公司或软件在用SQLite: Nokia's Symbian,Mozilla,Abobe,Google,阿里旺旺,飞信,Chrome,FireFox可见SQLite的稳定性及性能是不会有什么问题的,详细列表请参见:http://www.sqlite.org/famous.html. 网上关于SQLite的介绍一抓一大把,总结起来,他有如下特点: SQLite优点及适应场合: 轻量级绿色组件单一文件跨平台查询效率极高使用事务插入速度极快支持limi

SQLite 学习笔记

SQLite 学习笔记. 一.SQLite 安装    访问http://www.sqlite.org/download.html下载对应的文件.    1.在 Windows 上安装 SQLite.需要下载 sqlite-shell-win32-*.zip 和 sqlite-dll-win32-*.zip 压缩文件.        创建文件夹 C:\sqlite,并在此文件夹下解压上面两个压缩文件,将得到 sqlite3.def.sqlite3.dll 和 sqlite3.exe 文件.   

sqlite学习笔记7:C/C++中使用sqlite之打开数据库

数据库的基本内容前面都已经说得差不多了,接下看看怎样在C语言中使用sqlite. 一 接口 sqlite3_open(const char *filename, sqlite3 **ppDb) 打开数据库,如果数据库不存在则新建一个数据库,并打开 sqlite3_close(sqlite3*) 关闭数据库,如果关闭之前还存在没有执行完的语句,将会返回SQLITE_BUSY 二 实例 1 目录结构 Projects{ main.c// 代码所在文件 sqlite{// 官网下载下来的sqlite压

sqlite学习笔记6:更新表数据

一 条件判断 在SQL中条件判断使用where,相当于其他变成语言中的if,基本用法如: SELECT column1, column2, columnN FROM table_name WHERE [condition] 另外,SQL支持数学运算,逻辑运算,位于运算等等,均可放在WHERE子句中. 二 更新表 基本语法如下: UPDATE table_name SET column1 = value1, column2 = value2...., columnN = valueN WHERE

sqlite学习笔记4:表的创建和删除

前面做了那么多不就是为了数据弄几张表么,接下来看看怎么新建表. 一 新建表 基本语法如下: CREATE TABLE database_name.table_name( column1 datatype PRIMARY KEY(one or more columns), column2 datatype, column3 datatype, ..... columnN datatype, ); 其中database_name为你的数据库名,table_name当然是表名啦,columnN为列明,

sqlite学习笔记7:C语言中使用sqlite之打开数据库

数据库的基本内容前面都已经说得差点儿相同了.接下看看如何在C语言中使用sqlite. 一 接口 sqlite3_open(const char *filename, sqlite3 **ppDb) 打开数据库,假设数据库不存在则新建一个数据库,并打开 sqlite3_close(sqlite3*) 关闭数据库.假设关闭之前还存在没有运行完的语句,将会返回SQLITE_BUSY 二 实例 1 文件夹结构 Projects{ main.c// 代码所在文件 sqlite{// 官网下载下来的sqli

sqlite学习笔记3:附加数据库和分离数据库

在前面说了如果创建一个数据库,接下来我们需要操作数据库: 但是sqlite3命令一次只能操作一个数据库,如果当前路径下有多个数据库该怎么办呢?这就需要用到附加数据库. 一  关联数据库 附加数据库实际上就是告诉sqlite3,你写的SQL语句是操作的哪一个数据库.具体如何操作呢? 基本语法如下: ATTACH DATABASE 'DatabaseName' As 'Alias-Name'; *sqlite3中的语句都需要以分好结束 使用上面的命名,如果数据库存在,将会被关联到'Alias-Nam

sqlite学习笔记8:C语言中使用sqlite之创建表

前面已经说了如何打开和关闭数据库,这次要说得是如何执行SQL语句,来创建一张表. 要用的的函数: sqlite3_exec(sqlite3* db, const char *sql, sqlite_callback callback, void *data, char **errmsg) 参数: db:已经打开的数据库实例 sql:SQL语句,是一个字符串 callback:是一个回调函数 data:做为回调函数的第一个参数 errmsg:用于带回错误信息 该回调函数有两种返回值类型. 1.返回

Caliburn.Micro学习笔记(二)----Actions

Caliburn.Micro学习笔记(二)----Actions 上一篇已经简单说了一下引导类和简单的控件绑定 我的上一个例子里的button自动匹配到ViewModel事件你一定感觉很好玩吧 今天说一下它的Actions,看一下Caliburn.Micro给我们提供了多强大的支持 我们还是从做例子开始 demo的源码下载在文章的最后 例子1.无参数方法调用 点击button把textBox输入的文本弹出来 如果textbox里没有文本button不可点,看一下效果图 看一下前台代码 <Stac