1 初探
在平时的开发工作中,我们可能会有这样的需求:我们希望有一个内存数据库或者数据引擎,用比较Pythonic的方式进行数据库的操作(比如说插入和查询)。
举个具体的例子,分别向数据库db中插入两条数据,”a=1, b=1″ 和 “a=1, b=2”, 然后想查询a=1的数据可能会使用这样的语句db.query(a=1),结果就是返回前面插入的两条数据; 如果想查询a=1, b=2的数据,就使用这样的语句db.query(a=1, b=2),结果就返回前面的第二条数据。
那么是否拥有实现上述需求的现成的第三方库呢?几经查找,发现PyDbLite能够满足这样的需求。其实,PyDbLite和Python自带的SQLite均支持内存数据库模式,只是前者是Pythonic的用法,而后者则是典型的SQL用法。
他们具体的用法是这样的:
PyDbLite
SQLite
2 pydblite和sqlite的性能
毫无疑问,pydblite的使用方式非常地Pythonic,但是它的效率如何呢?由于我们主要关心的是数据插入和查询速度,所以不妨仅对这两项做一个对比。写一个简单的测试脚本:
在创建索引的情况下,10w次的插入和查询的时间如下:
在未创建索引的情况(把创建索引的测试语句注释掉)下,1w次的插入和查询时间如下:
我们不难得出如下结论:
sqlite的插入速度是pydblite的3-5倍;而在建立索引的情况下,sqlite的查询速度和pydblite相当;在未建立索引的情况下,sqlite的查询速度比pydblite慢1.5倍左右。
3 优化
我们的目标非常明确,使用Pythonic的内存数据库,提高插入和查询效率,而不考虑持久化。那么能否既拥有pydblite的pythonic的使用方式,又同时具备pydblite和sqlite中插入和查询速度快的那一方的速度?针对我们的目标,看看能否对pydblite做一些优化。
阅读pydblite的源码,首先映入眼帘的是对python2和3做了一个简单的区分。给外部调用的Base基于_BasePy2或者_BasePy3,它们仅仅是在__iter__上有细微差异,最终调用的是_Base这个类。
然后看下_Base的构造函数,做了简单的初始化文件的操作,由于我们就是使用内存数据库,所以文件相关的内容完全可以抛弃。
紧接着比较重要的是create(创建字段)、create_index(创建索引)两个函数:
可以看出,pydblite在内存中维护了一个名为records的字典变量,用来存放一条条的数据。它的key是内部维护的id,从0开始自增;而它的value则是用户插入的数据,为了后续查询和记录的方便,这里在每条数据中额外又加入了__id__和__version__。其次,内部维护的indices字典变量则是是个索引表,它的key是字段名,而value则是这样一个字典:其key是这个字段所有已知的值,value是这个值所在的那条数据的id。
举个例子,假设我们插入了“a=-1,b=0,c=1”和“a=0,b=1,c=2”两条数据,那么records和indices的内容会是这样的:
比方说现在我们想查找a=0的数据,那么就会在indices中找key为’a’的value,即{-1: set([0]), 0: set([1])},然后在这里面找key为0的value,即[1],由此我们直到了我们想要的这条数据它的id是1(也可能会有多个);假设我们对数据还有其他要求比如a=0,b=1,那么它会继续上述的查找过程,找到a=0和b=1分别对应的ids,做交集,就得到了满足这两个条件的ids,然后再到records里根据ids找到所有对应的数据。
明白了原理,我们再看看有什么可优化的地方:
数据结构,整体的records和indeices数据结构已经挺精简了,暂时不需要优化。其中的__version__可以不要,因为我们并不关注这个数据被修改了几次。其次是由于indices中最终的ids是个list,在查询和插入的时候会比较慢,我们知道内部维护的id一定是唯一的,所以这里改成set会好一些。
python语句,不难看出,整个_Base为了同时兼容python2和python3,不得不使用了2和3都支持的语句,这就导致在部分语句上针对特定版本的python就会造成浪费或者说是性能开销。比如说,d是个字典,那么为了同事兼容python2和3,作者使用了类似与for key in d.keys()这样的语句,在python2中,d.keys()会首先产生一个list,用d.iterkeys是个更明智的方案。再如,作者会使用类似set(d.keys()) – set([1])这样的语句,但是python2中,使用d.viewkeys() – set([1])效率将会更高,因为它不需要将list转化成set。
对特定版本python的优化语句就不一一举例,概括地说,从数据结构,python语句以及是否需要某些功能等方面可以对pydblite做进一步的优化。前面只是说了create和create_index两个函数,包括insert和__call__的优化也十分类似。此外,用普通方法来代替魔法方法,也能稍微提升下效率,所以在后续的优化中将__call__改写为了query。
优化后的代码,请见MemLite。
4 memlite、pydblite和sqlite的性能
让我们在上文的测试代码中加入对memlite的测试:
在创建索引的情况下,10w次的插入和查询的时间如下:
在未创建索引的情况(把创建索引的测试语句注释掉)下,1w次的插入和查询时间如下:
可以看出,在创建索引的情况下,memlite的插入和查询性能在sqlite和pydblite之上;而在未创建索引的情况下,memlite的插入性能和sqlite一样,好于pydblite,memlite的查询性能比pydblite稍差,但好于sqlite。综合来看,memlite即拥有pydblite的pythonic的使用方式,又拥有pydblite和sqlite中性能较高者的效率,符合预期的优化目标。
相关推荐
Delphi使用SQLite3,包括本地数据库和内存数据库,本地数据库加载到内存,内存数据库备份到本地,使用sqlite simple delphi包装类。
轻量级内存数据库管理工具SQLiteExpert。
一、建立数据库 根据需求建立数据库,建立了两个表,并保证了可以将数据存储到已有的数据库中,代码如下: import sqlite3 def createDataBase(): cn = sqlite3.connect('check.db') cn.execute('''CREATE TABLE IF ...
一种内存数据库到文件数据库的数据同步方法,包括:A、内存数据库将事务日志记录写入日志缓存区;B、定义内存数据库和文件数据库数据表的对应关系;C、根据事务日志刷新进程,异步地将所述日志缓存区中的所述事务日志...
Python操作SQLite数据库
go-sqlite3, 使用数据库/sql的for驱动程序 go-sqlite3 描述数据库/sql接口相符合的rtc驱动程序安装这个软件包可以用 go get命令安装:go get github.com/mattn/go-sqlite3go-sqli
编译好的sqlcipher nw版本0.48.0,32位
Python 数据库编程 02操作sqlite3数据库插入数据.mp4
Python 数据库编程 03操作SQLite3数据库查询数据.mp4
python连接sqlite数据库并完成增删改等基本操作,主界面可视化显示。用户可根据提示输入数字实现相关功能。
Python 数据库编程 04操作SQLite3数据库修改_删除数据.mp4
在Java下连接SQLite数据库 一、下载SQLite数据库的JDBC:http://www.zentus.com/sqlitejdbc/ 二、将下载到的包解压后得到jar包放到%JAVA_HOME%\lib下,并且将其添加到ClassPath系统环境变量中。一定要保证在类路径...
SQLite 定义 SQLite是嵌入式关系数据库管理系统。 它是独立的,无服务器的,零配置和事务性SQL数据库引擎 作用 可以自由地用于商业或私有的任何目的 本质 ... SQLite是python自带的数据库,
Python 数据库编程 01操作SQLite3创建表.mp4
python sqlite数据库存储
Python操作SQLite数据库.pdf
详细介绍Python操作数据库,包括数据库的简单介绍、常见的SQL语句、Python操作数据库的核心API等,并通过一个综合案例演示Python操作SQLite数据库的过程,非常适合高校教师教学和学生课后复习使用。
这个是qt调用sqlite3加密的源码,亲测可用,我的编译环境是qt5.9.0。
什么是内存数据库,常用内存数据库,SQLite最佳试用场合,哪些场合适合使用其他的关系型数据库,内存数据库之比较,性能测试
这是我最近做的C/S结构sqlite数据库管理器V1.2