文档库 最新最全的文档下载
当前位置:文档库 › SQLite 数据库文件分析

SQLite 数据库文件分析

SQLite 数据库文件分析
SQLite 数据库文件分析

SQLite数据库文件分析

前言

性急的兄弟可以跳过前言直接看第1章,特别性急的兄弟可以跳过前面各章,直接看鸣谢。最近对SQLite数据库很感兴趣,认真地学了有半个多月了,越学越觉着好玩。好玩归好玩,只是目前没什么实际用途,那就写点儿东西吧,否则半个月不是白学了嘛!

SQLite数据库包括多方面的知识,比如VDBE什么的。据说那些东西会经常变。确实,我用的是3.6.18版,我看跟其它文档中描述的3.3.6的VDBE已经很不一样了。所以决定先写文件格式,只要是3.?.?的版本,文件格式应该不会有太大变化吧。

网上介绍SQLite文件格式的文章并不少,但一般都是针对小文件:一个表,几条记录,两个页。本文准备一直分析到比较大的文件,至少B-tree和B+tree中得有内结点(就是说不能只有一个既是根又是叶的结点,就是说表中得多点记录,得建索引),还要争取对SQLite 的各类页都做出分析。

在分析的过程中,争取把SQLite数据库关于文件格式的基本规定也都介绍一下。这样,本文既是一个综合性的技术文档,又带有实例说明,兄弟们参考时岂不是就很方便了吗?既然是技术文档,要想读懂总得先掌握点SQLite数据库的基本知识吧。所以,先介绍参考文献。

0.1 参考文献

1-The Definitive Guide to SQLite . Michael Owens:据说是比较经典的SQLite著作,我看写得是挺好的。边看边翻译了其中的主要部分,但不敢拿出来,大家还是看原文吧。

2-SQLite源代码:其实有关SQLite的最原始说明可能都在源代码中了。把此项列在第2,只是因为我是先看的书再看的代码,估计大家也会是这个顺序吧。先浏览一下代码还是很有收获的,特别是几个主要的.h文件,对本文的写作很有帮助。有关文件格式的说明主要在btreeInt.h中。

3-SQLite入门与分析:网上Arrowcat的系列文章。Arrowcat应该是一个很博学的人,看他的文章收获很大,在此也算是鸣谢吧。

4-SQLite . Chris Newman:我没看,因为也是网上能够下载到的重要资源,所以也在此列出。看目录内容应该比参考文献1简单一些,但出版日期也更早了一些。

5-NULL:在网上搜了半天,国内为什么就没有关于SQLite的好书呢?

6- https://www.wendangku.net/doc/2f4337303.html,/fileformat.html:如果这篇文章看懂了,其实我这篇东西根本就不用再看了。这是介绍SQLite文件格式的权威文档,列在最后,是因为我也是写完这篇东西后才看到的。该文档由SQLite官方网站提供,当初没看,一是因为上网少,还没仔细浏览人家的网站就开始干了(太激动),其实归根结蒂还是因为英语不好。看到此文档这后还敢把我的东西发出来,有两个原因:一、为其他英语比我强不了多少的兄弟提供一点方便,二、我这里有例子,看起来更形象一些吧。

0.2 术语

这里不集中讨论大量术语了,那样显得太正式,还真成“文章”了。绝大多数术语都是在出现时再进行简单解释,但还是有个别概念需要先说明清楚,比如:

(1) Btree、B-tree和B+tree:

Btree是为磁盘存储而优化了的一种树结构,其一般性说明可参考各类《数据结构》教材。根据实现方法的不同,Btree又分为很多类型。在SQLite中,存储表数据用的是B+tree,存储表索引用的是B-tree。由于历史原因,SQLite在3.0版以前只使用B-tree,从3.0版开始,才对表数据使用了B+tree。因此,在SQLite的官方文档中,有时B-tree表示存储表索引的B-tree,有时又是两种Btree的统称。本文中将两种Btree的概念加以了区分,而将Btree作为两种树的统称,这是与SQLite官方文档及当前大多数SQLite介绍性文档相区别的地方。

(2) auto-vacuum数据库:

一般情况下,当一个事务从数据库中删除了数据并提交后,数据库文件的大小保持不变。即使整页的数据都被删除,该页也会变成“空闲页”等待再次被使用,而不会实际地被从数据库文件中删除。执行vacuum操作,可以通过重建数据库文件来清除数据库内所有的未用空间,使数据库文件变小。但是,如果一个数据库在创建时被指定为auto_vacuum数据库,当删除事务提交时,数据库文件会自动缩小。使用auto_vacuum数据库可以节省空间,但却会增加数据库操作的时间,有利有弊。Auto_vacuum数据库需要使用附加的格式,如指针位图页,本文只讨论非auto_vacuum数据库。

(3) 数据库映像、数据库文件和日志文件:

“数据库映像”是SQLite数据库的磁盘映像。SQLite数据库存储在单一的“数据库文件”中。一般情况下,数据库映像和数据库文件是一致的,可以理解为数据库映像就是数据库文件的内容,但有例外。如果事务对数据库进行了修改,这些修改会暂存在“日志文件”中,此时可以认为数据库映像分布在数据库文件和日志文件两个文件中。日志文件有自己的格式,本文不涉及。

(4) SQLite的当前版本:

我写这篇东西时,SQLite的当前版本为3.6.18。等我写完时,已经变成3.6.19了,文件格式没变。

1小文件的分析

1.1 准备数据库

执行SQLite的命令行工具,创建一个新的数据库food_test.db。

D:\SQLite\CLP>sqlite3 foods_test.db

创建一个新表。

CREATE TABLE foods(

id integer primary key,

type_id integer,

name text );

插入2条记录。

INSERT INTO "foods" V ALUES(1, 1, 'Bagels');

INSERT INTO "foods" V ALUES(2, 1, 'Bagels, raisin');

退出命令行工具。

当前目录下多了一个文件foods_test.db,大小为2K。

现在,就可以用UltraEdit或WinHex之类的软件对其进行分析了。

1.2 SQLite文件格式

SQLite有3类数据库。除内存数据库外,SQLite把每个数据库(main或temp)都存储到一个单独的文件中。

SQLite数据库文件由固定大小的“页(page)”组成。页的大小可以在512~32768之间(包含这两个值,必须是2的指数),默认大小为1024个字节(1KB)。页大小可以在数据库刚刚创建时设置,一旦创建了数据库对象之后,这个值就不能再改变了。

数据库中所有的页从1开始顺序编号。在具体的实现中,页号用4字节来表示(但我没搞清是否有符号位)。文件的第1个页被称为page 1,第2个页被称为page 2,依此类推。编号为0的页表示“无此页”。

页的类型可以是:Btree页、空闲(free)页或溢出(overflow)页。Btree又可以是B-tree或B+tree,每一种树的结点又区分为内部页和叶子页。一个数据库文件中可能没有空闲页或溢出页,但必然有Btree页。关于Btree页的格式规定是SQLite数据库的核心内容,本文的前半部分都是在介绍Btree页。

注:其实SQLite还有两种页。一种称为“锁页(locking page)”。只有1页,位于数据库文件偏移为1G开始的地方,如果文件不足1G,就没有此页。该页是用于文件加锁的区域,不能存储数据(参源代码io.h)。好在,如果只是读数据,即使文件大于1G,也不会有指针指向此页,因此下面我们就不再提它了。另一种称为“指针位图页(pointer-map page)”,这类页用于在auto-vacuum的数据库中存储元数据。本文不涉及auto-vacuum数据库,也就不讨论这种页了。

从逻辑上来说,一个SQLite数据库文件由多个多重Btree构成。每个Btree存储一个表的数据或一个表的索引,索引采用B-tree,而表数据采用B+tree,每个Btree占用至少一个完整的页,每个页是Btree的一个结点。每个表或索引的第1个页称为根页,所有表或索引的根页编号都存储在系统表sqlite_master中,表sqlite_master的根页为page 1。

注:sqlite_master是一个系统表,保存了数据库的schema信息,详参“关于sqlite_master 表”一节。

数据库中第一个页(page 1)永远是Btree页。Page 1的前100个字节是一个对数据库文件进行描述的“文件头”。它包括数据库的版本、格式的版本、页大小、编码等所有创建数据库时设置的永久性参数。关于这个特殊文件头的文档在btreeInt.h中,具体格式如下:

偏移量大小说明

0 16 头字符串,如果不改源程序,此字符串永远是"SQLite format 3"。

16 2 页大小(以字节为单位)。

18 1 文件格式版本(写)。对于SQLite的当前版本,此值为1。如果该值大于1,

表示文件为只读。SQLite将来版本对此域的规定可能改变。

19 1 文件格式版本(读)。对于SQLite的当前版本,此值为1。如果该值大于1,

SQLite认为文件格式错,拒绝打开此文件。SQLite将来版本对此域的规

定可能改变。

20 1 每页尾部保留空间的大小。(留作它用,默认为0。)

21 1 Btree内部页中一个单元最多能够使用的空间。

255意味着100%,默认值为0x40,即64(25%),这保证了一个结点(页)

至少有4个单元。

22 1 Btree内部页中一个单元使用空间的最小值。默认值为0x20,即32(12.5%)。

23 1 Btree叶子页中一个单元使用空间的最小值。默认值为0x20,即32(12.5%)。

注:SQLite的当前版本规定21~23的3个字节值只能是0X402020。原来

这3个字节值是可变的,从3.6.0版开始被固定下来了。

24 4 文件修改计数,通常被事务使用,由事务增加其值。SQLite用此域的值

验证内存缓冲区中数据的有效性。

28 4 未使用。

32 4 空闲页链表首指针。参“空闲页”一节。

36 4 文件内空闲页的数量。

40 60 15个4字节的元数据变量。

从偏移40开始的15个4字节元数据变量在btreeInt.h中的定义如下:

40 4 Schema版本:每次schema改变(创建或删除表、索引、视图或触发器等对象,造成sqlite_master表被修改)时,此值+1。

44 4 File format of schema layer:当前允许值为1~4,超过此范围,将被认为是文件格式错。

48 4 Size of page cache。

52 4 Largest root-page (auto/incr_vacuum):对于auto-vacuum数据库,此域为数据库中根页编号的最大值,非0。对于非auto-vacuum数据库,此域值为0。

56 4 1=UTF-8、2=UTF16le、3=UTF16be。

60 4 User version。此域值供用户应用程序自由存取,其含义也由用户定义。

64 4 Incremental vacuum mode:对于auto-vacuum数据库,如果是Incremental vacuum模式,此域值为1。否则,此域值为0。

68 4 未使用。

72 4 未使用。

用UltraEdit打开文件foods_test.db,page 1在0X0000~0X03FF。其中文件头内容如下(深蓝色部分):

前16个字节为头字符串,程序中固定设为"SQLite format 3"。

0X0400:页大小,0X0400=1024字节。

0X01:文件格式版本(写),值为1。

0X01:文件格式版本(读),值为1。

0X40:Btree内部页中一个单元最多能够使用的空间。0X40=64,即25%。

0X20:Btree内部页中一个单元使用空间的最小值。0X20=32,即12.5%。

0X20:Btree叶子页中一个单元使用空间的最小值。0X20=32,即12.5%。

0X00000003:文件修改计数,现在已经修改了3次,分别是1次创建和两次插入。

从0X20开始的4个字节:空闲页链表首指针。当前值为0,表示该链表为空。

从0X24开始的4个字节:文件内空闲页的数量。当前值为0。

从0X28开始的4个字节:Schema version。当前值为0X00000001。以后,每次sqlite_master 表被修改时,此值+1。

从0X38开始的4个字节:采用的字符编码。此处为0X00000001,表示采用的是UTF-8编码。

注意:在SQLite文件中,所有的整数都采用大端格式,即高位字节在前。

1.3 Btree页格式介绍

1.3.1Btree页的分区

页的类型有Btree页、空闲页和溢出页,本文前3章介绍的都是Btree页,其他类型的页在第4、5章介绍。

每个Btree页由四个部分构成:

1.页头

2.单元指针数组

3.未分配空间

4.单元内容区

首先介绍“单元”的概念:Btree页内部以单元(cell)为单位来组织数据,一个单元包含一个(或部分,当使用溢出页时)payload(也称为Btree记录)。由于各类数据大小各不相同,每个单元的大小也就是可变的,所以Btree页内部的空间需要进行动态分配(程序内部动态分配,不是动态申请空间),单元是Btree页内部进行空间分配和回收的基本单位。

页内所有单元的内容集中在页的底部,称为“单元内容区”,由下向上增长。由于单元的大小可变,因此需要对每个单元在页内的起始位置(称为单元指针)进行记录。单元指针保存在单元指针数组中,位于页头之后。单元指针数组包含0个或多个指针,由上向下增长。

单元指针数组和单元内容区相向增长,中间部分为未分配空间。系统尽量保证未分配空间位于最后的指针之后,这样,就很容易增加新的单元,而不需要整理碎片。

单元不需要是相邻和有序的,但单元指针是相邻和有序的。每个指针占2个字节,表示该单元在单元内容区中距页开始处的偏移。页中单元的数量保存在页头中。

1.3.2页头格式

页头包含用来管理页的信息,它通常位于页的开始处。对于数据库文件的page 1,页头始于

第100个字节处,因为前100个字节是文件头(file header)。

页头的格式如下:

偏移量大小说明

0 1 页类型标志。1: intkey, 2: zerodata, 4: leafdata, 8: leaf。

1 2 第1个自由块的偏移量。

3 2 本页的单元数。

5 2 单元内容区的起始地址。

7 1 碎片的字节数。

8 4 最右儿子的页号(the Ptr(n) value)。仅内部页有此域。

下面对页头各域分别进行介绍。

页类型标志:

如果leaf位被设置,则该页是一个叶子页,没有儿子;

如果zerodata位被设置,则该页只有关键字,而没有数据;

如果intkey位被设置,则关键字是整型;

如果leafdata位设置,则tree只存储数据在叶子页。

注:

以上内容见于大多数SQLite介绍性文档,btreeInt.h中也这么说。但通过分析程序代码,并从参考文献6中得到确认,结论如下:

上述描述与实际实现是矛盾的。可以这样理解:就不用管各标志位的含义了,如果是B+tree 的叶子页,该字节值为0X0D,如果是B+tree的内部页,该字节值为0X05,如果是B-tree 的叶子页,该字节值为0X0A,如果是B-tree的内部页,该字节值为0X02。

第1个自由块的偏移量:

由于随机地插入和删除单元,将会导致一个页上单元和空闲区域互相交错。单元内容区域中没有使用的空间收集起来形成一个空闲块链表,这些空闲块按照它们地址的升序排列。页头偏移为1的2个字节指向空闲块链表的头。每个空闲块至少4个字节,因为一个空闲块的开始4个字节存储控制信息:前2个字节指向下一个空闲块(0意味着没有下一个空闲块了),后2个字节为该空闲块的大小。

碎片的字节数:

由于空闲块大小至少为4个字节,所以单元内容区中的3个字节或更小的自由空间(称为碎片,fragment)不能存在于空闲块列表中。所有碎片的总的字节数将记录在页头偏移为7的位置(碎片最多为255个字节,在它达到最大值之前,页会被整理)。

单元内容区的起始地址:

单元内容区的起始地址记录在页头偏移为5的地方。这个值为单元内容区域和未使用区域的分界线。

最右儿子的页号:

如果本Btree页是叶子页,则无此域,页头长为8个字节。如果本Btree页为内部页,则有此域,页头长为12个字节。页头偏移为8的4个字节包含指向最右儿子的指针,该指针的含义将在第2章介绍。

有关Btree页格式的其它规定,将在下一节中用到时再介绍。

1.4 Page 1格式分析

Btree的基本原理这里就不详细介绍了。Btree有多种实现方法,各类《数据结构》教材中的

介绍也各不相同,但原理大同小异,随便找一本参考一下吧。最简单的Btree只有一个结点,既是根页,也是叶子页。

当前foods_test.db(大小为2K)只有两个页,都是Btree页。每个页都是一个Btree(B+tree,因为存储的是表数据),都是上述的单结点Btree。其中page 1为系统表sqlite_master的根页,下面我们对该页进行详细分析。

1.4.1页头分析

该页的页头从0X64=100处开始(前面100个字节是文件头),8个字节(因为是叶子页)。如下图中深蓝色部分所示:

说明:

0X0D:说明该页为B+tree的叶子结点。

0X0000:第1个自由块的偏移量。值为0,说明当前自由块链表为空。

0X0001:本页的单元数。当前sqlite_master表中只有一条记录,所以本页当前只有1个单元。

0X0399:单元内容区的起始地址。

0X00:碎片的字节数。当前值为0。

1.4.2单元指针数组

单元指针数组在页头之后,当前只有一个指针,为0X0399。

1.4.3关于可变长整数

可变长整数是SQLite的特色之一,使用它既可以处理大整数,又可以节省存储空间。由于单元中大量使用可变长整数,故在此先加以介绍。

可变长整数由1~9个字节组成,每个字节的低7位有效,第8位是标志位。在组成可变长整数的各字节中,前面字节(整数的高位字节)的第8位置1,只有最低一个字节的第8位置0,表示整数结束。

可变长整数可以不到9个字节,即使使用了全部9个字节,也可以将它转换为一个64-bit 整数。

下面是一些可变长整数的例子:

0x00 转换为 0x00000000

0x7f 转换为 0x0000007f

0x81 0x00 转换为 0x00000080

0x82 0x00 转换为 0x00000100

0x80 0x7f 转换为 0x0000007f

0x8a 0x91 0xd1 0xac 0x78 转换为 0x12345678

0x81 0x81 0x81 0x81 0x01 转换为 0x10204081

可变长整数可用于存储rowid、字段的字节数或Btree单元中的数据。

1.4.4关于sqlite_master表

sqlite_master是一个系统表,保存了数据库的schema信息。在逻辑上sqlite_master包含5个字段,如下表所示:

编号字段说明

1 Schema item type 值为"table"、"index"、"trigger"或"view"之一。

2 Schema item name 对象名称,值为字符串。

3 Associated table name 如果是表或视图对象,此字段值与字段2相同。如果是

索引或触发器对象,此字段值为与其相关的表名。

4 The "root page" number 对触发器或视图对象,此字段值为0。对表或索引对象,

此字段值为其根页的编号。

5 The SQL statement 字符串,创建此对象时所使用的SQL语句。

1.4.5B+tree叶子页的单元格式

单元是变长的字节串。一个单元包含一个(或部分,当使用溢出页时)payload。B+tree叶子页单元的结构如下:

大小说明

var(1–9) Payload大小,以字节为单位。

var(1–9) 数据库记录的Rowid值。

* Payload内容,存储数据库中某个表一条记录的数据。

4 溢出页链表中第1个溢出页的页号。如果没有溢出页,无此域。

结合实例来说明吧。

当前的单元内容区中只有一个单元,从0X0399开始,内容如下图所示:

0X65:Payload数据的字节数。可以看出Payload数据是从07 17 ~20 29。

0X01:foods(table对象)在sqlite_master表中对应记录的rowid,值为0X01。

Payload的格式如下图所示:

每个payload由两部分组成。第1部分是记录头,由N+1个可变长整数组成,N为记录中的字段数。第1个可变长整数(header-size)的值为记录头的字节数。跟着的N个可变长整数与记录的各字段一一对应,表示各字段的数据类型和宽度。用可变长整数表示各字段类型和宽度的规定如下表所示:

类型值含义数据宽度(字节数)

0 NULL 0

N in 1..4 有符号整数N

5 有符号整数 6

6 有符号整数8

7 IEEE符点数8

8-11 未使用N/A

N>12的偶数BLOB (N-12)/2

N>13的奇数TEXT (N-13)/2

header-size的值包括header-size本身的字节和Type1~TypeN的字节。

Data1~DataN为各字段数据,与Type1~TypeN一一对应,类型和宽度由Type1~TypeN指定。本例的payload数据为:

0X07:记录头包括7个字节。

0X17:字段1。TEXT,长度为:(23-13)/2=5。值为:table。

0X17:字段2。TEXT,长度为:(23-13)/2=5。值为:foods。

0X17:字段3。TEXT,长度为:(23-13)/2=5。值为:foods。

0X01:字段4。整数,长度为1。值为:0X02。表示本表B+tree的根页编号为2。

0X8129:字段5。TEXT。0X8129为可变长整数,转换为定长为0XA9=169。可知字段长度为:(169-13)/2=78=0X4E。对应数据为下图中蓝色部分。

1.5 Page 2格式分析

Page 2为表foods的根页。foods的Btree只有一个结点,既是根页,也是叶子页。

有了page 1的分析经验,page 2就好分析了。作为对上一节内容的巩固,再简单分析一下吧。用UltraEdit打开文件foods_test.db,page 2在0X0400~0X07FF。本页不再是文件首页(没有文件头),所以页头起始于本页的开始处,其内容如下(深蓝色部分):

因为是Btree的叶子页,所以页头只有8个字节。说明:

0X0D:说明该页为B+tree的叶子结点。

0X0000:第1个自由块的偏移量。0,说明当前自由块链表为空。

0X0002:本页的单元数。当前foods表中只有2条记录。

0X03DE:单元内容区的起始地址。

0X00:碎片的字节数。当前为0。

两个单元的指针分别为0X03F3和0X03DE。由于单元指针按次序排列,所以指针0X03F3指向本表的第1条记录,我们选择它进行分析。

0X03F3指向的单元内容如下图所示(深蓝色部分):

说明:

0X0B:Payload数据的字节数。可以看出Payload数据是从04 00 ~6C 73,共11个字节。

0X01:本记录rowid的值为1。

Payload数据:

0X04:记录头包括4个字节。

0X00:字段1。类型为NULL(详细说明参“关于自增长的主键字段”一小节)。

0X01:字段2。1字节整数。值为01,表示本记录type_id字段的值为01。

0X19:字段3。TEXT,长度为:(25-13)/2=6。值为:Bagels。

按此方法,第2条记录所对应的单元应该很容易分析了吧。

1.5.1关于自增长的主键字段

SQLite规定:SQLite会对所有整型主键字段应用自动增长属性。

个人分析,还没找到权威说明:如果一个表具有整型主键字段,如表foods具有主键字段id,则该表的id字段值即为其rowid值。

对上述字段1的进一步说明是:

由于rowid值已经在单元头部保存了,所以将字段1的类型设为NULL,这样既节省空间,又容易保持数据一致性。

以上内容可以用以下语句创建一个新的数据库来验证。但请等看完下一章后再来验证这些内容吧,因为现在有关索引的内容还没介绍呢。

CREATE TABLE foods(

id integer primary key,

type_id integer,

name text );

CREATE INDEX foods_name_idx on foods (name COLLATE NOCASE);

INSERT INTO "foods" V ALUES(1, 1, 'Bagels');

INSERT INTO "foods" V ALUES(2, 1, 'Bagels, raisin');

INSERT INTO "foods" V ALUES(40, 1, 'Bavarian Cream Pie');

INSERT INTO "foods" V ALUES(30, 1, 'Bear Claws');

INSERT INTO "foods" (type_id,name) VALUES(1, 'Black and White cookies');

当然,如果主键字段为非整数,则rowid与主键字段值分别存储,则不存在上述问题,可用以下语句创建一个新的数据库来验证。

CREATE TABLE test(

id text primary key,

name text );

INSERT INTO test V ALUES('aaa', 'Bagels');

INSERT INTO test V ALUES('bbb', 'Bagels, raisin');

CREATE INDEX test_name_idx on test (name COLLATE NOCASE);

2“大”文件的分析

前面分析的数据库文件只有2个页,每个页自成一个Btree,每个Btree只有一个结点,既是根页,也是叶子页。所以,至此,我们还没见过Btree的内部页呢。

2.1 准备数据库

向表foods继续插入记录。

INSERT INTO "foods" V ALUES(3, 1, 'Bavarian Cream Pie');

INSERT INTO "foods" V ALUES(4, 1, 'Bear Claws');

INSERT INTO "foods" V ALUES(5, 1, 'Black and White cookies');

INSERT INTO "foods" V ALUES(6, 1, 'Bread (with nuts)');

INSERT INTO "foods" V ALUES(7, 1, 'Butterfingers');

INSERT INTO "foods" V ALUES(8, 1, 'Carrot Cake');

INSERT INTO "foods" V ALUES(9, 1, 'Chips Ahoy Cookies');

INSERT INTO "foods" V ALUES(10, 1, 'Chocolate Bobka');

INSERT INTO "foods" V ALUES(11, 1, 'Chocolate Eclairs');

INSERT INTO "foods" V ALUES(12, 1, 'Chocolate Cream Pie');

INSERT INTO "foods" V ALUES(13, 1, 'Cinnamon Bobka');

INSERT INTO "foods" V ALUES(14, 1, 'Cinnamon Swirls');

INSERT INTO "foods" V ALUES(15, 1, 'Cookie');

INSERT INTO "foods" V ALUES(16, 1, 'Crackers');

INSERT INTO "foods" V ALUES(17, 1, 'Cupcake');

INSERT INTO "foods" V ALUES(18, 1, 'Cupcakes');

INSERT INTO "foods" V ALUES(19, 1, 'Devils Food Cake');

INSERT INTO "foods" V ALUES(20, 1, 'Dinky Donuts');

INSERT INTO "foods" V ALUES(21, 1, 'Dog biscuits');

INSERT INTO "foods" V ALUES(22, 1, 'Donuts');

INSERT INTO "foods" V ALUES(23, 1, 'Drakes Coffee Cakes');

INSERT INTO "foods" V ALUES(24, 1, 'Entenmann''s Cake');

INSERT INTO "foods" V ALUES(25, 1, 'Kaiser Rolls');

INSERT INTO "foods" V ALUES(26, 1, 'Marble Ryes');

INSERT INTO "foods" V ALUES(27, 1, 'Mini Ritz');

INSERT INTO "foods" V ALUES(28, 1, 'Muffin');

INSERT INTO "foods" V ALUES(29, 1, 'Muffin Tops');

INSERT INTO "foods" V ALUES(30, 1, 'Muffin Stumps');

INSERT INTO "foods" V ALUES(31, 1, 'Nut Bread');

INSERT INTO "foods" V ALUES(32, 1, 'Pastries (of the Gods)');

INSERT INTO "foods" V ALUES(33, 1, 'Peach Muffin');

INSERT INTO "foods" V ALUES(34, 1, 'Peppridge Farms Cookies (Milanos)'); INSERT INTO "foods" V ALUES(35, 1, 'Pizza Bagels');

INSERT INTO "foods" V ALUES(36, 1, 'Pie');

INSERT INTO "foods" V ALUES(37, 1, 'Pie (blueberry)');

INSERT INTO "foods" V ALUES(38, 1, 'Pie (Blackberry) Pie');

INSERT INTO "foods" V ALUES(39, 1, 'Pie (Boysenberry)');

INSERT INTO "foods" V ALUES(40, 1, 'Pie (Huckleberry)');

INSERT INTO "foods" V ALUES(41, 1, 'Pie (Raspberry)');

INSERT INTO "foods" V ALUES(42, 1, 'Pie (Strawberry)');

INSERT INTO "foods" V ALUES(43, 1, 'Pie (Cranberry)');

INSERT INTO "foods" V ALUES(45, 1, 'Poppy Seed Muffins');

INSERT INTO "foods" V ALUES(46, 1, 'Triscuits');

INSERT INTO "foods" V ALUES(47, 1, 'Wedding Cake (Royal)');

INSERT INTO "foods" V ALUES(48, 2, 'Bran');

INSERT INTO "foods" V ALUES(49, 2, 'Cheerios');

INSERT INTO "foods" V ALUES(50, 2, 'Corn Flakes');

INSERT INTO "foods" V ALUES(51, 2, 'Double Crunch');

INSERT INTO "foods" V ALUES(52, 2, 'Fruit Loops');

INSERT INTO "foods" V ALUES(53, 2, 'Grape Nuts');

INSERT INTO "foods" V ALUES(54, 2, 'Honey Combs');

INSERT INTO "foods" V ALUES(55, 2, 'Kasha');

INSERT INTO "foods" V ALUES(56, 2, 'Kix');

INSERT INTO "foods" V ALUES(57, 2, 'Life');

INSERT INTO "foods" V ALUES(58, 2, 'Pancakes');

INSERT INTO "foods" V ALUES(59, 2, 'Reese''s Peanut-Butter Puffs');

INSERT INTO "foods" V ALUES(60, 2, 'Rice Krispies');

INSERT INTO "foods" V ALUES(61, 2, 'Special K');

INSERT INTO "foods" V ALUES(62, 2, 'Tightly Wrapped Magic Pan Crepes');

INSERT INTO "foods" V ALUES(63, 3, 'Broiled Chicken');

INSERT INTO "foods" V ALUES(64, 3, 'Casserole');

INSERT INTO "foods" V ALUES(65, 3, 'Chicken');

INSERT INTO "foods" V ALUES(66, 3, 'Chicken (for wedding)');

INSERT INTO "foods" V ALUES(67, 3, 'Chicken Cashew (not ordered)');

INSERT INTO "foods" V ALUES(68, 3, 'Chicken Kiev');

INSERT INTO "foods" V ALUES(69, 3, 'Kung-Pao Chicken');

INSERT INTO "foods" V ALUES(70, 3, 'Chicken Marsala');

INSERT INTO "foods" V ALUES(71, 3, 'Chicken Piccata');

INSERT INTO "foods" V ALUES(72, 3, 'Chicken with Poppy Seeds');

INSERT INTO "foods" V ALUES(73, 3, 'Chicken, whole stuffed w/Gorganzolla and Ham'); INSERT INTO "foods" V ALUES(74, 3, 'Chicken Skins');

INSERT INTO "foods" V ALUES(75, 3, 'Chicken Wing (Shoulder Blades)');

INSERT INTO "foods" V ALUES(76, 3, 'Chicken (Kenny Rogers) ');

INSERT INTO "foods" V ALUES(77, 3, 'Chicken (Tyler) ');

INSERT INTO "foods" V ALUES(78, 3, 'Colonel Chang Chicken');

INSERT INTO "foods" V ALUES(79, 3, 'Cornish Game Hen');

INSERT INTO "foods" V ALUES(80, 3, 'Duck');

INSERT INTO "foods" V ALUES(81, 3, 'Duck, juicy breasts of');

INSERT INTO "foods" V ALUES(82, 3, 'Turkey');

INSERT INTO "foods" V ALUES(83, 3, 'Turkey, Kramer');

INSERT INTO "foods" V ALUES(84, 3, 'Turkey Jerky');

INSERT INTO "foods" V ALUES(85, 3, 'Turkey Chili');

INSERT INTO "foods" V ALUES(86, 4, 'A1 Sauce');

INSERT INTO "foods" V ALUES(87, 4, 'Barbeque Sauce');

INSERT INTO "foods" V ALUES(89, 4, 'Dill');

INSERT INTO "foods" V ALUES(90, 4, 'Ginger');

INSERT INTO "foods" V ALUES(91, 4, 'Gravy');

INSERT INTO "foods" V ALUES(92, 4, 'Honey Mustard');

INSERT INTO "foods" V ALUES(93, 4, 'Ketchup and Mustard together');

INSERT INTO "foods" V ALUES(94, 4, 'Ketchup');

INSERT INTO "foods" V ALUES(95, 4, 'Ketchup (secret)');

INSERT INTO "foods" V ALUES(96, 4, 'Maple Syrup');

INSERT INTO "foods" V ALUES(97, 4, 'Mustard (fancy)');

INSERT INTO "foods" V ALUES(98, 4, 'Parsley');

INSERT INTO "foods" V ALUES(99, 4, 'Pepper');

INSERT INTO "foods" V ALUES(100, 4, 'Pesto');

列出上述命令,是为了兄弟们自行验证时方便。现在表内有100条记录,文件大小为5K,说明foods表在文件中占4个页,其B+tree的逻辑结构如下图所示:

Page 2,内部页。

Page 3,叶子页。Page 4,叶子页。Page 5,叶子页。

用UltraEdit打开文件foods_test.db,观察文件头,其内容如下(深蓝色部分):

与前文比较,文件头内容基本无变化,只有偏移为24处的文件修改计数变成了0X00000065,表示现在文件已经修改了101次,包括1次创建和100次插入。

Page 1其他部分无变化。

2.2 B+tree内部页格式介绍

Page 2仍然是表foods的根页,但已经变成了内部页,格式有较大的变化。

对于数据库表,从SQLite3开始采用了B+tree,在此,先对B+tree的结构做一个简单介绍。B+tree与B-tree的主要区别在于,B-tree的所有页上都包含数据,而B+tree的数据只存在于叶子页上,内部页只存储导航信息。B+tree所有的叶子页都在同一层上,并按关键字排序,所有的关键字必须唯一,其逻辑结构举例如下图所示:

B+tree中根页(root page)和内部页(internal pages)都是用来导航的,这些页的指针域都是指向下级页的指针,数据域仅仅包含关键字。所有的数据库记录都存储在叶子页(leaf pages)内。在叶节点一级,页和页内的单元都是按照关键字的顺序排列的,所以B+tree可以沿水平方向遍历,时间复杂度为O(1)。

我们将根页和内部页统称为内部页,它们的结构是相同的,其逻辑结构如下:

----------------------------------------------------------------

| Ptr(0) | Key(0) | Ptr(1) | Key(1) | ... | Key(N-1) | Ptr(N) |

----------------------------------------------------------------

内部页包含N个关键值(Key(0)~ Key(N-1))和N+1个子页指针(Ptr(0)~ Ptr(N)),其值为子页的页号。其中,Ptr(N)存储在页头中偏移为8的地方(4字节整数,只有内部页的页头有此域,参“Btree页格式介绍”一节)。其他的每对子页指针和关键值(Ptr(i) 和Key(i))组成1个单元,共N个单元。Ptr(i) 所指向子树中关键字的最大值<=Key(i),Ptr(N) 所指向子树中关键字的值都>Key(N-1)。

2.3 B+tree内部页格式分析

现在对foods B+tree仅有的内部页进行分析。

文件第2页页头的内容如下:(图中深蓝色部分)

前文对页头格式已经有比较详细的介绍,这里不再赘述。直接对内容进行说明:

0X05:说明该页为B+tree的内部页。

0X0000:第1个自由块的偏移量。此处为0,表示本页没自由块。

0X0002:本页有2个单元。

0X03F6:单元内容区的起始位置。

0X00:碎片的字节数,此处为0。

0X00000005:最右儿子的页号,即Ptr(N)。由于本页有2个单元,所以此处即Ptr(2)。其值为0X05,即Ptr(2)指向第5页。第5页是表数据的最后一页,也是当前文件的最后一页。

单元指针数组在页头之后,有2个指针,分别为0X03FB和0X03F6。

注意:这两个指针后面还有一些乱七八糟内容,我也曾为此迷惑过。这些不是指针,而是属于“未分配空间”的一部分。因为此页在还没有成为内部页(还是叶子页)时,曾经插入过不少记录,有过不少指针。现在成为内部页了,只使用两个指针,但以前使用过的空间也没必要清零,再次使用时自然会覆盖。提示:此页尾部的内容区也存在这个情况,不再单独解释。

下面来看单元内容区的数据,内容如下:(

图中深蓝色部分)

由于单元内容区中各单元是反向增长的,所以两个单元的数据分别为:

0X00000003,0X2C

0X00000004,0X56

每个单元包括两部分内容:

一个4字节的页号,指向相应的儿子,即Ptr(i)。此处分别指向第3页和第4页。

一个可变长整数,即Key(i)。0X2C 表示最左儿子(文件第3页)中关键字值都<=0X2C 。0X56表示第2个儿子(文件第4页)中关键字都>0X2C ,都<=0X56。注意:关键字值使用可变长整数,我们插入的记录少,在此都只有1个字节,所以看不出来。

前文刚介绍过,最右儿子的页号存储在页头中,值为0X00000005,说明第5页中关键字值都>0X56。

重画前文B+tree 的逻辑结构图如下所示: Page 3,Key<=0X2C Page 4,Key<=0X56 Page 5,Key>0X56

Ptr(0)

Key(0) Ptr(1) Key(1) Ptr(2) 3 0X2C 4

0X56 5

2.4 叶子页格式分析

其实在上一章中分析page 1和page 2

时,这两个页都是叶子页。这里再次对叶子页的格式进行分析,主要是为了验证前一节对内部页的分析结果,所以,咱就别嫌麻烦了。 文件第4页页头的内容如下:(图中深蓝色部分)

之所以选择第4页,是因为该页为中间叶子,其记录的关键值应该在Key(0)和Key(1)之间。 从上图可以看出,本页有0X2A=42个单元。第1个单元的入口地址为0X03E7,最后一个单元的入口地址为0X006F 。

0X03E7单元的内容为:

这是本页关键值最小的记录,可以看出其rowid 值为0X2D ,恰大于0X2C 。

0X006F 单元的内容为:

这是本页关键值最大的记录,可以看出其rowid 值为0X56=0X56。

3索引格式分析

前面分析的都是表数据页的格式。表数据用B+tree来存储,而索引用B-tree来存储。两者的区别主要是:B-tree中只存储关键字段的值和对应记录的rowid值;B-tree树中的内部页也可以存储数据。

3.1 准备数据库

为表创建一个索引。

CREATE INDEX foods_name_idx on foods (name COLLATE NOCASE);

创建索引后文件大小为9K,说明索引有4个页,其B-tree的逻辑结构如下图所示:

Page 6,内部页。

Page 7,叶子页。Page 8,叶子页。Page 9,叶子页。

此处4个页是连续的,如果在创建表时同时创建索引,然后再插入记录,则用于存储数据的页和用于存储索引的页应该是交叉的。

此时观察page 1的内容,单元指针数组中多了一个单元指针,sqlite_master表中多了索引对象记录,从其数据可以看出其根页为第6页。另外,Schema版本的值变成了2。

3.2 索引页的内部页

现在我们对索引B-tree唯一内部页(也就是根页,文件第6页)的格式进行分析。

文件第6页页头的内容如下:(图中深蓝色部分)

说明:

0X02:说明该页为B-tree的内部页。

0X0000:第1个自由块的偏移量。0,说明当前自由块链表为空。

0X0002:本页有2个单元。

0X03DC:单元内容区的起始位置为0X03DC。

0X00:碎片的字节数,当前为0。

0X00000009:最右儿子的页号,即Ptr(N)。由于本页有2个单元,所以此处即Ptr(2)。其值为0X09,即Ptr(2)指向第9页。第9页是索引数据的最后一页,也是当前文件的最后一页。

单元指针数组在页头之后,有2个指针,分别为0X03F1和0X03DC。

下面来看单元内容区的数据。

0X03F1单元的内容如下:(图中深蓝色部分)

0X00000007:为Ptr(0),指向第7页。

0X0A:Payload数据的字节数。数据从03~0F。

0X03:记录头包括3个字节。

0X19:字段1。TEXT,长度为:(25-13)/2=6。字段值为:cookie。

0X01:字段2。整数,长度为1。字段值为:0X0F,表示索引值cookie所对应记录的关键值(即记录的rowid值)为15。

注:如果是通过索引字段查找“cookie”,现在就可以按其rowid值=15到数据B+tree树中去检索记录的全部数据了。

0X03DC单元的内容如下:(图中深蓝色部分)

0X00000008:为Ptr(1),指向第8页。

0X10:Payload数据的字节数。数据从03~21。

0X03:记录头包括3个字节。

0X25:字段1。TEXT,长度为:(37-13)/2=12。字段值为:Peach Muffin。

0X01:字段2。整数,长度为1。字段值为:0X21,表示索引值Peach Muffin所对应记录的关键值为33。

3.3 索引页的叶子页格式

对于B-tree来说,内部页与叶子页的格式差别其实不大。简单分析一下,为了验证上一节的结果。

文件第8页前半部分的内容如下:

页头的第1个字节值为0X0A,说明该页为B-tree的叶子页。其它不再详述。

单元内容区的开始部分内容如下:

单元内容区的开始和结束部分内容如下:

不再详细分析了,粗看可以看出:

此页记录的索引值确实都在cookie和Peach Muffin之间,不包括这两个值(这两个值已经在根页中了)。单元数据都按索引值大小排序。

4碎片、自由块和空闲页

到目前为止,我们只对数据库表进行了插入操作,因此,文件格式还是很“整齐”的。如果对数据库进行删除和修改操作,就会产生碎片、自由块和空闲页。本文1.2节和1.3节对相应的概念有较详细介绍,本章就算是对前述内容的例证吧。

4.1 碎片

执行下面的update语句,每执行一句都将原记录的name字段长度减少2字节(可与前文的insert语句对照)。

update foods set name='Bavarian Cream P' where id=3;

执行完上面语句后,文件第3页的页头内容如下图所示:

可以看到,偏移为7的字节值变成了2,说明当前页中有2字节的碎片。再执行下面语句:update foods set name='Bear Cla' where id=4;

可以看到当前页中的碎片字节数已经变成了4,如下图所示:

至于碎片字节数到多大才会整理,这得分析源程序了。

4.2 自由块

执行下面删除语句:

delete from foods where id=5;

文件第3页的页头内容如下:

可以看到,第1个自由块的偏移量为0X0396。

观察0X0396处的单元,数据如下图所示:

单元的前2个字节指向下一个空闲块,此处值为0,表示没有下一个空闲块了。

0X001E表示该空闲块的大小,从数值上来分析,应该是包含了这两个字节本身。

如果再删除1条记录,就可以看到自由块是如何串接的,我们就不做了。

4.3 空闲页

执行下面删除语句:

delete from foods where id>10;

文件大小仍为9K,但此时文件中应该有了6个空闲页(索引和数据都只需要一个页就能放下了),分别是页3、4、5、7、8和页9。

观察此时的文件头,内容为:

注意其中深蓝色部分,可以看到,偏移为32的4个字节中值为0X00000005,表示空闲页链表首指针指向第5个页。对了,就应该是第5个页先空闲出来。

偏移为36的4个字节中值为0X00000006,表示文件中空闲页的数量为6。

现在得介绍一下空闲页链表的格式了。

空闲页有两种类型:trunk page(主干页)和leaf page(叶子页)。文件头偏移为32处的指针指向空闲链表的第一个trunk page ,每个trunk page 指向多个叶子页。偏移36处的4个字节为空闲页的总数量,包括所有的trunk page 和leaf page 。空闲页链表的结构如下图所示: 文件头

空闲页链表首指针 下一个trunk page

trunk page leaf page 数量

leaf page leaf page leaf page

… 下一个trunk page trunk page leaf page 数量 … 下一个trunk page trunk page leaf page 数量 …

其中,trunk page 的格式(从页的起始处开始)如下:

(1)4个字节,指向下一个trunk page 的页号,0表示链表结束;

(2)4个字节,该页leaf page 的数量;

(3)0个或多个指向leaf page 的页号,每项4个字节。

文件第6页前部的内容如下:

说明:

下一个trunk page 的页号为0X00000000,表示链表中只有此一个trunk page ,没有后继结点了。

该页leaf page 的数量为0X00000005,表示本页带有5个leaf page ,依次为0X00000009,0X00000008,0X00000007、0X00000004和0X00000003。

SQLite 对leaf page 的格式没有规定。

5 溢出页的格式

5.1 溢出页格式说明

如前所述,单元(cell)具有可变的大小,而页的大小是固定的,这就有可能一个单元比一个完整的页还大,这样的单元就会溢出到由溢出页组成的链表上,如下图所示: Btree 页

单元头 Payload(第1部分) 溢出页号

单元头 Payload

Payload(第2部分)

溢出页号 Payload(最后部分)

0 …

溢出页

溢出页

在上图中,Btree 页的最后一个单元超大,需要使用溢出页。此时,单元的最后4个字节为溢出页链表中第1个溢出页的页号。对于每个溢出页,其头4个字节为下一溢出页的页号,该值为0时表示此页为溢出页链表的表尾页。

除最后一个溢出页外,每个溢出页全部填充数据(除了最开始的4个字节)。最后一个溢出页可能数据很少,甚至只有一个字节的数据,但一个溢出页不会存储来自两个单元的数据。

5.2 准备数据库

得换一个数据库了。我们用下面的方法来分析溢出页的格式,并对上节内容进行验证。 执行SQLite 的命令行工具,创建一个新的数据库food_text.db 。

D:\SQLite\CLP>sqlite3 foods_text.db

创建一个新表。

CREATE TABLE foods(

id integer primary key,

type_id integer,

name text );

插入1条记录,该记录包含一段较长的文本。

INSERT INTO "foods" V ALUES(1, 1,

'0000000001000000000200000000030000000004000000009

0000000001000000000200000000030000000004000000009

sqlite3数据库使用实例

SQLite version 3.7.9 2011-11-01 00:52:41 Enter ".help" for instructions Enter SQL statements terminated with a ";" sqlite> create table students( ...> id integer primary key, ...> name text not null unique, ...> sex int default 1, ...> bak text); sqlite> .tables students sqlite> .help .backup ?DB? FILE Backup DB (default "main") to FILE .bail ON|OFF Stop after hitting an error. Default OFF .databases List names and files of attached databases .dump ?TABLE? ... Dump the database in an SQL text format If TABLE specified, only dump tables matching LIKE pattern TABLE. .echo ON|OFF Turn command echo on or off .exit Exit this program .explain ?ON|OFF? Turn output mode suitable for EXPLAIN on or off. With no args, it turns EXPLAIN on. .header(s) ON|OFF Turn display of headers on or off .help Show this message .import FILE TABLE Import data from FILE into TABLE .indices ?TABLE? Show names of all indices If TABLE specified, only show indices for tables matching LIKE pattern TABLE. .load FILE ?ENTRY? Load an extension library .log FILE|off Turn logging on or off. FILE can be stderr/stdout .mode MODE ?TABLE? Set output mode where MODE is one of: csv Comma-separated values column Left-aligned columns. (See .width) html HTML

code insert SQL insert statements for TABLE line One value per line list Values delimited by .separator string tabs Tab-separated values tcl TCL list elements .nullvalue STRING Print STRING in place of NULL values .output FILENAME Send output to FILENAME .output stdout Send output to the screen .prompt MAIN CONTINUE Replace the standard prompts .quit Exit this program .read FILENAME Execute SQL in FILENAME

内存数据库介绍

常用内存数据库介绍(一) 博客分类: 内存数据库 数据结构Oracle企业应用网络应用设计模式 (注:部分资料直接来源于Internet) 1. 内存数据库简介 1.1 概念 一、什么是内存数据库 传统的数据库管理系统把所有数据都放在磁盘上进行管理,所以称做磁盘数据库(DRDB:Disk-Resident Database)。磁盘数据库需要频繁地访问磁盘来进行数据的操作,由于对磁盘读写数据的操作一方面要进行磁头的机械移动,另一方面受到系统调用(通常通过CPU中断完成,受到CPU时钟周期的制约)时间的影响,当数据量很大,操作频繁且复杂时,就会暴露出很多问题。 近年来,内存容量不断提高,价格不断下跌,操作系统已经可以支持更大的地址空间(计算机进入了64位时代),同时对数据库系统实时响应能力要求日益提高,充分利用内存技术提升数据库性能成为一个热点。 在数据库技术中,目前主要有两种方法来使用大量的内存。一种是在传统的数据库中,增大缓冲池,将一个事务所涉及的数据都放在缓冲池中,组织成相应的数据结构来进行查询和更新处理,也就是常说的共享内存技术,这种方法优化的主要目标是最小化磁盘访问。另一种就是内存数据库 (MMDB:Main Memory Database,也叫主存数据库)技术,就是干脆重新设计一种数据库管理系统,对查询处理、并发控制与恢复的算法和数据结构进行重新设计,以更有效地使用CPU周期和内存,这种技术近乎把整个数据库放进内存中,因而会产生一些根本性的变化。两种技术的区别如下表:

内存数据库系统带来的优越性能不仅仅在于对内存读写比对磁盘读写快上,更重要的是,从根本上抛弃了磁盘数据管理的许多传统方式,基于全部数据都在内存中管理进行了新的体系结构的设计,并且在数据缓存、快速算法、并行操作方面也进行了相应的改进,从而使数据处理速度一般比传统数据库的数据处理速度快很多,一般都在10倍以上,理想情况甚至可以达到1000倍。 而使用共享内存技术的实时系统和使用内存数据库相比有很多不足,由于优化的目标仍然集中在最小化磁盘访问上,很难满足完整的数据库管理的要求,设计的非标准化和软件的专用性造成可伸缩性、可用性和系统的效率都非常低,对于快速部署和简化维护都是不利的。 2. 内存数据库历史和发展 一、雏形期 从上个世纪60年代末到80年代初。在这个时期中,出现了主存数据库的雏形。1969年IBM公司研制了世界上最早的数据库管理系统------基于层次模型的数据库管理系统IMS,并作为商品化软件投入市场。在设计IMS时,IBM考虑到基于内存的数据管理方法,相应推出了IMS/VS Fast Path。Fast Path是一个支持内存驻留

浅谈嵌入式SQLITE数据库实现与应用

开源(Opensource):这是它最强大的地方。开源,意味着你可以品读它的源码,你可以随时修改它,加入你自己的特性,而这一切完全免费的。开源,是一种精神。 实现部分 好了,现在从实现的角度来谈谈个人体会,这也是我比较关注的。 SQLite是一款优秀的嵌入式数据库管理系统,这里有两层含义:一是它经常作为动态库嵌入到应用程序; 另外一方面它通常用于嵌入式设备或其它要求较低的桌面应用。如果把它作为内存数据库,个人觉得不是很适合。毕竟,它的写并发性不是很好,此时,TimesTen也许会更好,Berkey DB也许是一个不错的选择。SQLite这样的嵌入式数据库与主存数据库的应用场景、实现以及对资源的需求都是不一样的。 (1)事务处理 事务的核心问题有两个:并发控制和恢复。解决了并发控制和恢复问题的系统,就能允许它的用户假设程序是原子的(atomically)执行的——好像没有其它的程序同时执行;而且是可靠的(reliably)——不会产生失败。原子性和可靠性的抽象,则称为事务(transaction)。其实,事务并不是DBMS的专利,任何分布式系统,都面对并发和恢复问题,而解决的方法就是事务,只不过,我们更常听到DBMS中的事务。 并发控制保证事务的原子执行,它使得交错执行的事务看起来是一个接一个的顺序执行的,完全没有交错执行。如果交错执行的结果与顺序执行的结果一致,则称为串行化(serializable)。 恢复使得数据库仅仅包含那些正常完成的事务的结果。如果事务在执行的过程中发生错误,不能继续进行,恢复算法必须清除部分完成事务产生的影响。 ?并发控制 SQLite只支持库级锁,库级锁意味着什么?——意味着同时只能允许一个写操作,也就是说,即事务T1在A表插入一条数据,事务T2在B表中插入一条数据,这两个操作不能同时进行,即使你的机器有100个CPU,也无法同时进行,而只能顺序进行。表级都不能并行,更别说元组级了——这就是库级锁。但是,SQLite尽量延迟申请X锁,直到数据块真正写盘时才申请X锁,这是非常巧妙而有效的。 ?恢复 SQLite的恢复技术是影子分页技术(shadow paging)技术的典型代表。 DBMS的常用恢复技术有影子分页技术与基于日志的技术,前者在早其数据库管理系统中用到,比如Sys tem R,现代DBMS中已经很难见到它的身影了。 影子分页技术与基于日志技术相比,优点是实现简单,消除了写日志记录的开销,恢复的速度也快(不需要redo和undo)。影子分页的缺点就是事务提交时要输出多个块,这使得提交的开销很大,而且以块为单位,很难应用到允许多个事务并发执行的情况——这是它致命的缺点。 (2)查询处理 SQLite的查询处理本质上就是一个SQL编译器和一个虚拟机。而实现这些功能只用了十多个文件,整个实现实现简单而有效,但是也存在一些问题。首先,SQLite字典数据很简单,实际上它的字典就一个表s qlite_mater,所有的信息都是通过对sqlite_master中SQL语句进行解析获取的,而解析一个SQL语句,都需要进行词法分析、语法分析、甚至虚拟机代码的生成。而这一过程是很需要时间的,而且,查询计划也没有重用。其次,查询优化还比较简单,特别是连接操作,只通过循环来做(MySQL也一样)。但是,仅仅数万代码,我们不能对它要求太苛求。 (3)存储模型

Qt4访问sqlite数据库

目录 Qt4访问sqlite数据库 (2) RedHat 9 Linux下在QT3.1中连接SQLite3全过程详细记录 (6) 基于ARM-Linux的SQLite嵌入式数据库技术 (17) 关于在qt中如何连接sqlite3数据库的问题 (23) SQLite 完整中文FAQ (32) C/C++中调用SQLITE3的基本步骤 (40) SQLite嵌入式数据库系统的研究与实现 (50)

Qt4访问sqlite数据库 https://www.wendangku.net/doc/2f4337303.html,/index.php/2008/09/qt-sqlite/ sqlite简介 sqlite 是一款轻量级的、基于文件的嵌入式数据库,2000年就已经诞生,经过7年多的发展,直到今天已经成为最流行的嵌入式数据库,包括google在内的公司在其桌面软件中亦使用sqlite 存储用户数据。由此可以看出,已经没有任何理由去怀疑sqlite的稳定性了。 sqlite的优势 1. 免配置,和access一样,只要把数据库文件通过ftp上传到服务器上就可以使用,不需要服务器的额外支持 2. 备份方便,因为只是一个文件,只要复制一份该文件,就能备份整个数据库 3. 虽然是轻量级数据库,但他支持最大2tb 的单个库文件。 4. 快,无与伦比的快。经过实际测试,在几百万记录的情况下,sqlite的插入和查询速度和mysql 不分上下,快于sql server,10倍于access (但这并不意味着它可以替代sql server ) 用QT操作sqlite 由于sqlite属于轻量级的数据库,不需要配置,不需要安装,也不需要管理员,所以也就没必要像操作mysql等数据库一样的设置主机,用户和密码了。样例如下: 1 2 3 4 5 6 7 8 9 10 11 12 #include #include #include #include #include #include

嵌入式系统技术报告(题目 SQLITE数据库的概述和使用)

合肥学院 嵌入式系统设计课程 技术报告 (2014-2015第2学期) 报告题目:SQLite数据库概述和使用专业:自动化 班级:级自动化卓越班 姓名: 指导老师:干开峰

摘要 自几十年前出现的商业应用程序以来,数据库就成为软件应用程序的主要组成部分。正与数据库管理系统非常关键一样,它们也变的非常庞大,并占用相当多的系统资源。嵌入式数据库直接在应用程序进程中运行,提供了零配置运行模式,并且资源占用非常少。作为一个开源的嵌入式数据库产品,SQLite具有系统开销小,检索效率高的特性,嵌入式数据库无须独立运行的数据库引擎,它是由程序直接调用相应的API去实现对数据的存取操作。更直白的讲,嵌入式数据库是一种具备了基本数据库特性的数据文件。嵌入式数据库与其它数据库产品的区别是,前者是程序驱动式,而后者是引擎响应式。嵌入式数据库的一个很重要的特点是体积非常小,同时,很多嵌入式数据库在性能上也优于其它数据库,所以在高性能的应用上也常见嵌入式数据库的身影。SQLite是D·理查德·希普开发出来的用一个小型C库实现的一种强有力的嵌入式关系数据库管理体制。SQLite虽然很小巧,但是支持的SQL语句不会逊色于其他开源数据库。 关键词:SQLite;嵌入式;数据库

目录 1SQLite简介 (1) 2SQLite工作原理 (1) 3SQLite的功能特性 (2) 4SQLite的结构 (2) 5SQLite的使用 (4) 5.1SQLite里面的一些基本的操作: (4) 5.2SQLite的一些类的使用及说明: (5) 5.3SQLite嵌入式数据库使用注意: (10) 6总结 (11)

IT行业个人简历

个人简历 个人信息: 姓名:性别:男 年龄: 2 居住地: 学历:本科毕业院校: 专业:计算机科学与技术电话: 电子邮箱: 求职意向:java软件工程师及相关职业 IT技能: 1. 熟练Java、JSP/Servlet、Struts1.2、Hibernate、Spring、Struts 2.1 2. 熟练HTML、JavaScript、XML、AJAX、iReport、JQuery; 3. 熟悉Oracle、MySQL,能使用Oracle、MySQL进行开发 4. 能够以Oracle/Tomcat/MyEclipse/搭配开发环境,Oracle是企业级的数据库,T omcat是免 费开源的Web服务器,MyEclipse是很强大的Java集成开发工具。 5. 熟练使用流行的辅助技术(JDBC/Log4j/JUnit/ AJAX),JDBC用以进行数据库操作,Log4j用 以记录日志,JUnit用以进行单元测试,AJAX用以进行动态验证和页面局部刷新。 6. 熟悉OOAD和UML思想。 7. 熟练在Windows环境下的编程,开发和环境的搭建,以及服务器的搭建和项目的部署。熟悉 liunx,unix 环境下的编程。 另外:熟悉C和VC++编程语言。 项目经验及工作经验: ?2011 / 1——2011 / 5:北京达内科技集团: 项目名称:TOLO航空公司3G应用门户(T-3GABS) 项目时间:2011/02 软件环境:Windows + Android + SQlite 开发工具:Eclipse Galileo 项目描述:为了改进服务质量,提升服务品质,为智能手机用户提供实时在线的订票服务,TOLO 航空公司决定开发一套3G应用门户系统,以适应日益增长的客户需求。该系统使得

sqlite命令

Sqlite命令操作 建立数据库档案 用sqlite3建立数据库的方法很简单,只要在shell下键入(以下$符号为shell 提示号,请勿键入): $ sqlite3 foo.db 如果目录下没有foo.db,sqlite3就会建立这个数据库。sqlite3并没有强制数据库档名要怎么取,如果你喜欢,也可以取个foo.icannameitwhateverilike 的档名。 在sqlite3提示列下操作 进入了sqlite3之后,会看到以下文字: SQLite version 3.1.3 Enter ".help" for instructions sqlite> 这时如果使用.help可以取得求助,.quit则是离开(请注意:不是quit) SQL的指令格式 所以的SQL指令都是以分号(;)结尾的。如果遇到两个减号(--)则代表注解,sqlite3会略过去。 建立资料表 假设我们要建一个名叫film的资料表,只要键入以下指令就可以了:

create table film(title, length, year, starring); 这样我们就建立了一个名叫film的资料表,里面有name、length、year、starring 四个字段。 这个create table指令的语法为: create table table_name(field1, field2, field3, ...); table_name是资料表的名称,fieldx则是字段的名字。sqlite3与许多SQL数据库软件不同的是,它不在乎字段属于哪一种资料型态:sqlite3的字段可以储存任何东西:文字、数字、大量文字(blub),它会在适时自动转换。 建立索引 如果资料表有相当多的资料,我们便会建立索引来加快速度。好比说: create index film_title_index on film(title); 意思是针对film资料表的name字段,建立一个名叫film_name_index的索引。这个指令的语法为 create index index_name on table_name(field_to_be_indexed); 一旦建立了索引,sqlite3会在针对该字段作查询时,自动使用该索引。这一切的操作都是在幕后自动发生的,无须使用者特别指令。 加入一笔资料 接下来我们要加入资料了,加入的方法为使用insert into指令,语法为:insert into table_name values(data1, data2, data3, ...); 例如我们可以加入

SQLite 数据库文件分析

SQLite数据库文件分析 前言 性急的兄弟可以跳过前言直接看第1章,特别性急的兄弟可以跳过前面各章,直接看鸣谢。最近对SQLite数据库很感兴趣,认真地学了有半个多月了,越学越觉着好玩。好玩归好玩,只是目前没什么实际用途,那就写点儿东西吧,否则半个月不是白学了嘛! SQLite数据库包括多方面的知识,比如VDBE什么的。据说那些东西会经常变。确实,我用的是3.6.18版,我看跟其它文档中描述的3.3.6的VDBE已经很不一样了。所以决定先写文件格式,只要是3.?.?的版本,文件格式应该不会有太大变化吧。 网上介绍SQLite文件格式的文章并不少,但一般都是针对小文件:一个表,几条记录,两个页。本文准备一直分析到比较大的文件,至少B-tree和B+tree中得有内结点(就是说不能只有一个既是根又是叶的结点,就是说表中得多点记录,得建索引),还要争取对SQLite 的各类页都做出分析。 在分析的过程中,争取把SQLite数据库关于文件格式的基本规定也都介绍一下。这样,本文既是一个综合性的技术文档,又带有实例说明,兄弟们参考时岂不是就很方便了吗?既然是技术文档,要想读懂总得先掌握点SQLite数据库的基本知识吧。所以,先介绍参考文献。 0.1 参考文献 1-The Definitive Guide to SQLite . Michael Owens:据说是比较经典的SQLite著作,我看写得是挺好的。边看边翻译了其中的主要部分,但不敢拿出来,大家还是看原文吧。 2-SQLite源代码:其实有关SQLite的最原始说明可能都在源代码中了。把此项列在第2,只是因为我是先看的书再看的代码,估计大家也会是这个顺序吧。先浏览一下代码还是很有收获的,特别是几个主要的.h文件,对本文的写作很有帮助。有关文件格式的说明主要在btreeInt.h中。 3-SQLite入门与分析:网上Arrowcat的系列文章。Arrowcat应该是一个很博学的人,看他的文章收获很大,在此也算是鸣谢吧。 4-SQLite . Chris Newman:我没看,因为也是网上能够下载到的重要资源,所以也在此列出。看目录内容应该比参考文献1简单一些,但出版日期也更早了一些。 5-NULL:在网上搜了半天,国内为什么就没有关于SQLite的好书呢? 6- https://www.wendangku.net/doc/2f4337303.html,/fileformat.html:如果这篇文章看懂了,其实我这篇东西根本就不用再看了。这是介绍SQLite文件格式的权威文档,列在最后,是因为我也是写完这篇东西后才看到的。该文档由SQLite官方网站提供,当初没看,一是因为上网少,还没仔细浏览人家的网站就开始干了(太激动),其实归根结蒂还是因为英语不好。看到此文档这后还敢把我的东西发出来,有两个原因:一、为其他英语比我强不了多少的兄弟提供一点方便,二、我这里有例子,看起来更形象一些吧。

Android学习笔记---SQLite介绍,以及使用Sqlite,进行数据库的创建,完成数据添删改查的理解

Android学习笔记---SQLite介绍,以及使用Sqlite,进行数据库的创建,完成数据添删改查的理解 17_创建数据库与完成数据添删改查--------------------------------------1.SQLite介绍:最大特点是,无数据类型; 除了可以使用文件或SharedPreferences存储数据,还可以选择使用SQLite数据库存储数据。在Android平台上,集成了一个嵌入式关系型数据库—SQLite,SQLite3支持 NULL、INTEGER n n 、REAL(浮点数字)、TEXT(字符串文本)和BLOB(二进制对象)数据类型,虽然它支持的类型 n n 只有五种,但实际上sqlite3也接受varchar(n)、char(n)、decimal(p,s) 等数据类型,只不 n n 过在运算或保存时会转成对应的五种数据类型。 SQLite最大的特点是你可以把各种类型的数n n 据保存到任何字段中,而不用关心字段声明的数据类型是什么。例如:可以在Integer类型的 n n 字段中存放字符串,或者在布尔型字段中存放浮点数,或者在字符型字段中存放日期型值。n n n 但有一种情况例外:定义为INTEGER PRIMARY KEY的字段只能存储64位整数, 当向这种字段 n n 保存除整数以外的数据时,将会产生错误。 另外, SQLite 在解析CREATE TABLE 语句时, n n 会忽略 CREATE TABLE 语句中跟在字段名后面的数据类型信息,如下面语句会忽略 name字段 n n 的类型信息: CREATE TABLE person (personid integer primary key autoincrement, name varchar n n (20)) SQLite可以解析大部分标准SQL语句,如:查询语句:select * from 表名 where 条件子句 group by 分组字句 having ... order byn n n 排序子句如:select * from person n n n n select * from person order by id desc n n n n select name from person group by name having count(*)>1 ---------------------------------------------------------------------------2.a.分页SQL与mysql类似,下面SQL语句获取5条记录,跳过前面3条记录n n select * from Account limit 5 offset 3 或者 select * from Account limit 3,5 n b.select * from Account limit 3,5,指的是跳过前面的3条记录,然后获取5条记录n c.select * from Account limit 3,5是select * from Account limit 5 offset 3语句 n n 的简写版 -------------------------------------------------------------------------------n 3.常用操作: a.插入语句:insert into 表名(字段列表) values(值列表)。如: insert into person nn n n (name, age) values(‘传智’,3) b.更新语句:update 表名 set 字段名=值where 条件子句。如:update person set name=n n n n'credream ‘where id=10 c.删除语句:delete from 表名 where 条件子句。如:delete from person nwhere id=10 -------------------------------------------------------------------------------2.虽然无数据类型,但是建议加上,这样可以增加可读性,支持标准sql,oracle中的不行 ---------------------------------------------------3.获取添加记录后的自增长的ID值:select last_insert_rowid(); -----------------------------------------------------------4.在android中开发数据库应用: n a.创建数据库:以前在javaee时候,需要手工数据,但是android应用,需要运行在用户的 n n 手机上,所以,android应用,要有自动创建数据库功能,当第一次使用软件的时候 n n 就创建数据库----------------------------------------5.关于数据库自动创建的详细介绍: 我们在编写数据库应用软件时,需要考虑这样的问题:因为我们开发的软件可能会安装在很 n n 多用户的手机上,如果应用使用到了SQLite数据库,我们必须在用户初次使用软件时创建出 n n 应用使用到的数据库表结构及添加一些初始化记录,另外在软件升级的时候,也需要对数据 n n 表结构进行更新。那么,我们如何才能实现在用户初次使用或升级软件时自动在用户的手机 n n 上创建出应用需要的数据库表呢?总不能让我们在每个需要安装此软件的手机上通过手工方 n n 式创建数据库表吧?因为这种需求是每个数据库应用都要面临的,所以在Android系统,为我 n n 们提供了一个名为SQLiteOpenHelper的抽象类,必须继承它才能使用,它是通过对数据库版 n n 本进行管理来实现前面提出的需求。n -----------------------------------------6.SQLite数据库添加,删除,改查操作 n A.创建数据库:SQLiteOpenHelper .getWritableDatabase ()或getReadableDatabase() n n 可以创建数据库7.创建完成以后可以使用SQLITE Expert软件打开生成的数据库n 可以看到除了生成的自己的需要的表之外,还生成了一张:android_metadata表: n 如果在sqlite中使用数据库一定会有一张android_metadata表,用来登记用户的 n 使用语言:zh_cn -----------------------------------------------------n b.数据库自动创建的过程及方法详细介绍: n n我们在编写数据库应用软件时,需要考虑这样的问题:因为我们开发的软件可能会安装在 n n 很多用户的手机上,如果应用使用到了SQLite数据库,我们必须在用户初次使用软件时创建 n n 出应用使用到的数据库表结构及添加一些初始化记录,另外在软件升级的时候,也需要对数n n 据表结构进行更新。那么,我们如何才能实现在用户初次使用或升级软件时自动在用户的手 n n 机上创建出应用需要的数据库表呢?总不能让我们在每个需要安装此软件的手机上通过手工 n n 方式创建数据库表吧?因为这种需求是每个数据库应用都要面临的,所以在Android系统,为n n 我们提供了一个名为SQLiteOpenHelper的抽象类,必须继承它才能使用,它是通过对数据库n n 版本进行管理来实现前面提出的需求。n -------------------------------------------8.详细介绍: 为了实现对数据库版本进行管理,SQLiteOpenHelper类提供了两个重要的方法,分别是 n n onCreate(SQLiteDatabase db)和onUpgrade(SQLiteDatabase db, int oldVersion, intn n n newVersion),前者用于初次使用软件时生成数据库表,后者用于升级软件时更新数据库表结n n 构。当调用SQLiteOpenHelper的getWritableDatabase()或者getReadableDatabase()方法获n n 取用于操作数据库的SQLiteDatabase实例的时候,如果数据库不存在,Android系统会自动生 n n 成一个数据库,接着调用onCreate()方法,onCreate()方法在初次生成数据库时才会被调用 n n ,在onCreate()方法里可以生成数据库表结构及添加一些应用使用到的初始化数据。 n n onUpgrade()方法在数据库的版本发生变化时会被调用,一般在软件升级时才需改变版本号, n n 而数据库的版本是由程序员控制的,假设数据库现在的版本是1,由于业务的变更,修改了数n n 据库表结构,这时候就需要升级软件,升级软件时希望更新用户手机里的数据库表结构,为n n 了实现这一目的,可以把原来的数据库版本设置为2(有同学问设置为3行不行?当然可以,如 n n 果你愿意,设置为100也行),并且在onUpgrade()方法里面实现表结构的更新。当软件的版本 n n 升级次数比较多,这时在onUpgrade()方法里面可以根据原版号和目标版本号进行判断,然后 n n 作出相应的表结构及数据更新。 getWritableDatabase()和getReadableDatabase()方法都可以获取一个用于操作数据库的 n n SQLiteDatabase实例。但getWritableDatabase() 方法以读写方式打开数据库,一旦数据库n n 的磁盘空间满了,数据库就只能读而不能写,倘若使用的是getWritableDatabase() 方法就 n n 会出错。getReadableDatabase()方法先以读写方式打开数据库,如果数据库的磁盘空间满了 n n ,就会打开失败,当打开失败后会继续尝试以只读方式打开数据库。 ------------------------------------------------------------------------9.创建数据库的代码: n a.创建项目:DBSQLIte n b./DBSQLIte/src/com/credream/service/DBOpenHelter.java n n package com.credream.service; n n import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteDatabase.CursorFactory; n n public class DBOpenHelter extends SQLiteOpenHelper { //父类没有默认构造器,需要显示调用 public DBOpenHelter(Context context) { super (context, "credream.db", null, 1); //数据库创建完成后,默认会保存在<包>/database/文件夹下 //当修改版本号时候,会触发:onUpgrade方法 //第二个:指定数据库名称, //第三个:游标工厂,用来迭代,查询后的结果集,null代表使用系统默认的 n n 游标工厂//版本号,大于0 n } /** n* 这个方法是在数据库第一次被创建的时候调用的 n*/ @Override public void onCreate(SQLiteDatabase db) { //SQLiteDatabase这个类,封装了增删改查操作,也叫做数据库操作实例 db.execSQL("CREATE TABLE person (personid integer primary keyn n n autoincrement, name varchar(20))"); //这里也可以不写name的数据类型,因为sqlite是数据类型无关的,就是写n n 了varchar(20),也可以写入超过20的内容 n n } /** n* 当数据库的版本号变更的时候被调用 n*/ @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("alter table person add phone varchar(12) null"); n n } n n } --------------------------------------------------------2.在清单文件中写入测试环境 n n n n n n n n n n ---------------------------------------------------- 3./DBSQLIte/src/com/credream/test/PersonServiceTest.java package com.credream.test; n n import com.credream.service.DBOpenHelter; n n import android.test.AndroidTestCase; n n public class PersonServiceTest extends AndroidTestCase { //创建数据库,在<包>/database/ public void testCreateDB(){ DBOpenHelter dbOpenHelter=new DBOpenHelter(getContext()); dbOpenHelter.getWritableDatabase(); n } n n } -------------------------------------------------------4.选择方法开始测试,然后,在data/data/<包>/database/下 n 查看并用sqlite打开生成的数据库检查是否正确---------------------------------------------然后将版本号,改成2,然后再次执行,看到,表已经被更新,增加了一列phone -----------------------------------------------5.了解sqlite工作的原理: n DBOpenHelter dbOpenHelter=new DBOpenHelter(getContext()); dbOpenHelter.getWritableDatabase(); n 打开getWritableDatabase();代码:

SQLite数据库中如何列出所有的表和索引

SQLite数据库中如何列出所有的表和索引 如果你运行sqlite3命令行来访问你的数据库,可以键入“.tables”来获得所有表的列表。或者,你可以输入“.schema” 来看整个数据库模式,包括所有的表的索引。输入这些命令,后面跟一个LIKE模式匹配可以限制显示的表。 在一个C/C++ 程序中(或者脚本语言使用Tcl/Ruby/Perl/Python 等)你可以在一个特殊的名叫SQLITE_MASTER上执行一个SELECT查询以获得所有表的索引。每一个SQLite 数据库都有一个叫SQLITE_MASTER 的表,它定义数据库的模式。 SQLITE_MASTER 表看起来如下: CREATE TABLE sqlite_master ( type TEXT, name TEXT, tbl_name TEXT, rootpage INTEGER, sql TEXT ); 对于表来说,type字段永远是'table',name字段永远是表的名字。所以,要获得数据库中所有表的列表,使用下列SELECT语句: SELECT name FROM sqlite_master WHERE type='table' ORDER BY name; 对于索引,type等于'index', name则是索引的名字,tbl_name是该索引所属的表的名字。不管是表还是索引,sql字段是原先用CREATE TABLE 或CREATE INDEX 语句创建它们时的命令文本。对于自动创建的索引(用来实现PRIMARY KEY 或UNIQUE 约束),sql字段为NULL。 SQLITE_MASTER 表是只读的。不能对它使用UPDATE、INSERT 或DELETE。它会被CREATE TABLE、CREATE INDEX、DROP TABLE 和DROP INDEX 命令自动更新。临时表不会出现在SQLITE_MASTER 表中。临时表及其索引和触发器存放在另外一个叫SQLITE_TEMP_MASTER 的表中。SQLITE_TEMP_MASTER 跟SQLITE_MASTER 差不多,但它只是对于创建那些临时表的应用可见。如果要获得所有表的列表,不管是永久的还是临时的,可以使用类似下面的命令:SELECT name FROM (SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type='table' ORDER BY name

浅谈SQLite

浅谈SQLite:实现与应用 1、前言 有一些日子没有仔细关注SQLite了,今天打开其主页,发现其最新的版本已经是3.6.22了,更让我惊喜的是它的用户越来越多,而且邮件列表的关注者也越来越多,突然觉得自己已经太old了。惊喜的同时,不得不聊上几句了。 首先,来看看都有哪些人在使用SQLite,主页上列举一长串NB的用户,其中不乏像Adobe,Apple,Firefox,甚至连google,Microsoft,SUN这样的用户。 Firefox:这是我的机器上V3.5.7安装目录下的文件: 可以发现用的SQLite 3.6.16.1。 据说,Google在它的Desktop for Mac,Google Gears,以及Android,甚至Chrome 中都用到SQLite,而且,Google的工程师对SQLite的全文检索功能作了很大的贡献(contribution)。还有Apple,Micorsoft,SUN等等,这里就不列举了。详细见 https://www.wendangku.net/doc/2f4337303.html,/famous.html。有这些公司的参与,对SQLite的发展应该有很大的帮助,尤其是像Google这样的用户。 2、实现与应用 下面从实现及应用的角度来谈谈SQLite,先看看SQLite的特点(功能)吧。 特点

简单(simple):SQLite是一个非常轻量级自包含(lightweight and self-contained)的DBMS:一个头文件,一个动态库文件,你就拥有了关系数据库的所有功能了。简单,是SQLite最明显的哲学。它提供的API少而简单。只需要一个DLL文件,你的程序马上就拥有了一个功能强大的数据库引擎,这是一件很美妙的事。 小巧(small):我用VS 2005在Windows下编译的3.6.11,Release版为368K,用时不到20秒——而编译MySQL时,要花上几分钟。而当我插入10000条int数据时,内存开销660K,磁盘开销92K。 事务(transaction):事务是现代商业数据处理系统最基本的要求,而Access,不论是在可执行文件大小(看了一下Access2003的可执行文件大小为6.32M,两者不是一个量级),还是事务特性,都是不能和SQLite 相比的。 并发性(Concurrency):由于SQLite通过OS的文件锁来实现库级锁,粒度很大,但是,它通过一些复杂特殊的处理(具体可以参见分析系列),尽量的提升了读写的并发度。如果你还有担心,你可以看看这篇文章: https://www.wendangku.net/doc/2f4337303.html,/database/sqlite_cms.html。 SQL92:SQLite支持绝大部分的标准SQL语句,你只需要几百K的空间,就可以换来需要上百兆的通用DBMS几乎所有操作了。 方便(Convenience):如果你的程序要使用SQLite,只需要将拷贝你的程序目录即可。 开源(Opensource):这是它最强大的地方。开源,意味着你可以品读它的源码,你可以随时修改它,加入你自己的特性,而这一切完全免费的。开源,是一种精神。 实现部分 好了,现在从实现的角度来谈谈个人体会,这也是我比较关注的。 SQLite是一款优秀的嵌入式数据库管理系统,这里有两层含义:一是它经常作为动态库嵌入到应用程序;另外一方面它通常用于嵌入式设备或其它要求较低的桌面应用。如果把它作为内存数据库,个人觉得不是很适合。毕竟,它的写并发性不是很好,此时,TimesTen

QSQLite 数据库

这里我们只是演示了一下使用这个框架完成最简单的程序的过程,只起到抛砖引玉的作用。这个框架很复杂,但是功能也很强大,Qt Creator中自带了几个相关的例子(在帮助中查找Graphics View Examples即可),你可以参考一下。因为篇幅问题,我们就只讲这么多,如果以后有机会,我会推出一个相关的专题来讲述这个框架。 分类:Qt系列教程作者: yafeilinux 日期:四月 30th, 2010. 3,006 views Tags: 2D绘图, creator, qt, yafeilinux, 教程

二十一、Qt数据库(一)简介 本文章原创于https://www.wendangku.net/doc/2f4337303.html,转载请注明出处。 从今天开始我们学习Qt数据库编程的内容。 先说明:我们以后使用现在最新的基于Qt 4.6.2的Qt Creator 1.3.1 Windows版本,该版本是2010年2月17日发布的。 数据库几乎是每个较大的软件所必须应用的,而在Qt中也使用QtSql模块实现了对数据库的完美支持。我们在Qt Creator的帮助中查找QtSql Module,其内容如下图: 可以看到这个模块是一组类的集合,使用这个模块我们需要加入头文件#include ,而在工程文件中需要加入一行代码:QT += sql 这里每个类的作用在后面都有简单的介绍,你也可以进入其中查看其详细内容。下面我们先简单的说一下QSqlDatabase类和QSqlQuery类。 QSqlDatabase类实现了数据库连接的操作,现在Qt支持的数据库类型有如下几种: 而现在我们使用的免费的Qt只提供了SQLite和ODBC数据库的驱动(我们可以在Qt Creator 安装目录下的qt\plugins\sqldrivers文件夹下查看),而其他数据库的驱动需要我们自己添加。SQLite是一个小巧的嵌入式数据库,关于它的介绍你可以自己在网上查找。 QSqlQuery类用来执行SQL语句。(关于SQL语句:在我的教程中只会出现很简单的SQL语句,你没有相关知识也可以看懂,但是如果想进行深入学习,就需要自己学习相关知识了。)