测试目标

获取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

16

第一轮

2886

3641

8392

19615

27875

第二轮

2867

3933

8088

21010

28635

第三轮

2821

4131

8077

21220

28689

第四轮

2941

4011

7787

20983

27965

第五轮

2896

3724

7881

21332

28654

平均值

2881

3949

7958

21136

28363

CPU%

80%

180%

320%

670%

710%

QPS

3.4w

5w

5w

3.8w

5.6w

表一

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

3.3w

1.5w

1.4w

表二

3) 结果分析

从表一结果来,随着并发度提升,主机CPU利用率也随着上升;QPS由单线程3.4w,上升到4线程并发5w左右,但是到8线程又出现了一定的回落,16线程并发时,QPS又回到5w左右。测试过程中,通过观察CPU利用率和磁盘IO,基本上可以断定是CPU限制了QPS的上升。因为主机CPU核数为8核,因此CPU的利用率在高并发下可以接近800%,基本上已经到达极限。当然,从绝对值来看每秒5w的查询性能,也确实很不错!

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

上面的测试实际上是多线程模式下的共享和非共享模式下的测试结果。实际上使用sqlite连接有几种方式:单线程模式,多线程串行化模式和多线程模式,一般情况下,我们会选择多线程模式,这几种模式可以编译阶段指定,也可以在打开数据库连接时指定,还可以在运行时指定。
(1)编译阶段
这几种模式可以通过参数SQLITE_THREADSAFE在编译阶段指定,可以取值0,1,2,默认是1。这三种取值的含义如下:
0:单线程模式,即内部不做mutex保护,多线程运行sqlite不安全。
1:多线程的串行模式,sqlite帮助多线程实现串行化。
2:多线程的并发模式,要求同一个时刻,同一个连接不被多个线程使用。
(2)打开数据库阶段     
除了可以在编译阶段指定运行模式,还可以在打开数据库时(sqlite3_open_v2())通过参数指定,主要的几个参数以及含义如下:
SQLITE_OPEN_NOMUTEX: 设置数据库连接运行在多线程模式(没有指定单线程模式的情况下)
SQLITE_OPEN_FULLMUTEX:设置数据库连接运行在串行模式。
SQLITE_OPEN_SHAREDCACHE:设置运行在共享缓存模式。
SQLITE_OPEN_PRIVATECACHE:设置运行在非共享缓存模式。
SQLITE_OPEN_READWRITE:指定数据库连接可以读写。
SQLITE_OPEN_CREATE:如果数据库不存在,则创建。
(3)运行时阶段
通过调用sqlite3_config接口,也可以设置运行模式。
若编译参数SQLITE_THREADSAFE=1 or SQLITE_THREADSAFE=2,那么可以在运行时设置线程模式。
SQLITE_CONFIG_SINGLETHREAD:单线程模式
SQLITE_CONFIG_MULTITHREAD:多线程模式,应用层保证同一个时刻,同一个连接只有一个线程使用。
SQLITE_CONFIG_SERIALIZED:串行模式,sqlite帮助多线程实现串行化。

批量载入测试

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的更新性能。统计更新1w行,程序执行的时间,并根据更新记录数目与执行时间计算TPS。

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,一定伴随着大量的随机写,导致更新性能比较差。

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

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

    SQLite介绍.学习笔记.性能测试 哪些人,哪些公司或软件在用SQLite: Nokia's Symbian,Mozilla,Abobe,Google,阿里旺旺,飞信,Chrome,FireFox可 ...

  2. Sqlite学习笔记(四)&&SQLite-WAL原理

    Sqlite学习笔记(三)&&WAL性能测试中列出了几种典型场景下WAL的性能数据,了解到WAL确实有性能优势,这篇文章将会详细分析WAL的原理,做到知其然,更要知其所以然. WAL是 ...

  3. Sqlite学习笔记(四)&&SQLite-WAL原理(转)

    Sqlite学习笔记(三)&&WAL性能测试中列出了几种典型场景下WAL的性能数据,了解到WAL确实有性能优势,这篇文章将会详细分析WAL的原理,做到知其然,更要知其所以然. WAL是 ...

  4. python3.4学习笔记(二十四) Python pycharm window安装redis MySQL-python相关方法

    python3.4学习笔记(二十四) Python pycharm window安装redis MySQL-python相关方法window安装redis,下载Redis的压缩包https://git ...

  5. SQLite 学习笔记

    SQLite 学习笔记. 一.SQLite 安装    访问http://www.sqlite.org/download.html下载对应的文件.    1.在 Windows 上安装 SQLite. ...

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

    数据库的基本内容前面都已经说得差点儿相同了.接下看看如何在C语言中使用sqlite. 一 接口 sqlite3_open(const char *filename, sqlite3 **ppDb) 打 ...

  7. SQLite学习笔记(七)&&事务处理

    说到事务一定会提到ACID,所谓事务的原子性,一致性,隔离性和持久性.对于一个数据库而言,通常通过并发控制和故障恢复手段来保证事务在正常和异常情况下的ACID特性.sqlite也不例外,虽然简单,依然 ...

  8. WPF的Binding学习笔记(二)

    原文: http://www.cnblogs.com/pasoraku/archive/2012/10/25/2738428.htmlWPF的Binding学习笔记(二) 上次学了点点Binding的 ...

  9. AJax 学习笔记二(onreadystatechange的作用)

    AJax 学习笔记二(onreadystatechange的作用) 当发送一个请求后,客户端无法确定什么时候会完成这个请求,所以需要用事件机制来捕获请求的状态XMLHttpRequest对象提供了on ...

随机推荐

  1. JAVA实现的微信扫描二维码支付

    吐槽一下 支付项目采用springMvc+Dubbo架构实现,只对外提供接口. 话说,为什么微信支付比支付宝来的晚了那么一点,一句话,那一阵挺忙的,然后就没有时间整理,最近做完支付宝支付,顺便也把微信 ...

  2. x8086汇编在显存中显示字符串

    题目:在屏幕中间显示绿色,绿底红色,白底蓝色的字符串‘welcome to masm!’ 80X25彩色字符模式显示缓冲区的结构: 在内存地址结构中,B8000H~BFFFFH共32KB的空间,为80 ...

  3. poj-1611-The Suspects

    The Suspects Time Limit: 1000MS   Memory Limit: 20000K Total Submissions: 34284   Accepted: 16642 De ...

  4. Linux下安装mongodb详细过程

    本次安装mongodb使用yum.repo方式.详细过程请参考,也列出一些安装过程中的错误,欢迎指正. mongodb版本:3.0 先在linux下cd 到 /etc/yum.repos.d/ 新建脚 ...

  5. 《高可用MySQL》1 – Windows环境下压缩版MySQL安装

    近日在读O’REILIY系列的<高可用MySQL>, 自然少不了主从(Master-Slave)配置和横向扩展相关的内容.Master-Slave这东西吧,在许多公司都是标配,开发中基本天 ...

  6. Python WMI获取Windows系统信息 监控系统

    #!/usr/bin/env python # -*- coding: utf-8 -*- #http://www.cnblogs.com/liu-ke/ import wmi import os i ...

  7. HTML学习总结

    首先,我们要问,什么是html?官方的解释是:超文本标记语言.什么意思呢?简单的来说,就是一种用来制作网页的特殊语言.那么,什么是网页呢?我们说,网页是一个在浏览器窗口下显示的页面,实质上是一个文档. ...

  8. JavaScript实战(原生range和自定义特效)

    今天我又码了两个特效:一个是用原生input[type=range]的,另一个完全自定义的:下面是完整代码和演示: #tip{ position: absolute; top: 30px; left: ...

  9. 2013最常用的NoSQL数据库

    摘要:与关系数据库相比,每个NoSQL都有自己不同的适用场景,这里带大家盘点文档数据库.图数据库.键值数据存储.列存储数据库与内存数据网络等领域的常用的NoSQL. 在几年内,NoSQL数据库一直以性 ...

  10. Service和Thread的关系及如何启用Service,如何停用Service

    Service和Thread的关系: 不少Android初学者都可能会有这样的疑惑,Service和Thread到底有什么关系呢?什么时候应该用Service,什么时候又应该用Thread?答案可能会 ...