SQLite简介
- SQLite说明
SQLite是一款轻量级数据库,是遵守ACID的关联式数据库管理系统.它的设计目的是嵌入式。目前已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百KB的内存就够了。它能够支持Windows/Linux/Unix/Android等等主流的操作系统,同时能够跟很多程序语言相结合,比如 C/C++,Tcl、C#、PHP、Java等,还有ODBC接口,同样比起Mysql、PostgreSQL这两款开源世界著名的数据库管理系统来讲,它的处理速度比他们都快。SQLite的版权允许无限制地应用,不用支付任何使用和授权费用,已经成为了最受欢迎的开源数据库之一。SQLite网站(www.sqlite.org)同时提供SQLite的已编译版本和源程序。
SQLite的特性:
- 轻量级
SQLite 是一个轻量级数据库, 本身足够小, 大致3万行C代码, 250K, 但支持数据库大小至2TB。 - 独立性
SQLite 数据库的核心引擎不需要依赖第三方软件,也不需要所谓的“安装”。 - 隔离性
SQLite 数据库中所有的信息(比如表、视图、触发器等)都包含在一个文件夹内,方便管理和维护。 - 跨平台
SQLite 目前支持大部分操作系统,不至电脑操作系统更在众多的手机系统也是能够运行,比如:Android。 - 多语言接口
SQLite 数据库支持多语言编程接口。 - 安全性
SQLite 数据库通过数据库级上的独占性和共享锁来实现独立事务处理。这意味着多个进程可以在同一时间从同一数据库读取数据,但只能有一个可以写入数据. - 开源
SQLite 的源代码是完全开源的, 良好注释的源代码, 并且有着90%以上的测试覆盖率 , - 还内置地同时支持UTF-8和UTF-16编码。
缺点:并发性差,没有固定的事务日志,保密安全性不强。
SQLite 简介:
SQLite是D.Richard Hipp于2000年开发出来的一个自容纳、可嵌入、零配置数据库引擎
的小型数据库。它支持多数SQL92标准。SQLite3.0的C编程一共涉及到83个API函数,此外还有一些数据结构和预定义。这些接口的使用一般都不复杂,最简单的程序只要三个函数就可以完成:sqlite3_open(),sqlite3_exec() 和sqlite3_close() 。如果想更好地控制数据库引擎的执行, 可以使用sqlite3_prepare()函数把SQL语句编译成字节码,然后再使用sqlite3_step()来执行编译好的字节码,以sqlite3_column_xxx()表示的一组函数来获取查询结果集中的信息,其中xxx代表返回数值的数据类型,如int、text、blob,real等。
对于程序员来说,SQLite就象一个数据传送带,提供了一种方便的将应用程序绑定的数据的方法。你可以操作互相关联的数据,可以完成很繁重的任务不必写自己的算法来对数据结构操作和排序。
SQLite 的应用:
SQLite已经被多种软件和产品所使用:
Apple的Mac OS X操作系统;
Safari的Web浏览器;
Mail.app的电子邮件程序、RSS的管理;
Apple的Aperture照片软件;
Adobe的AIR;
Google 的android;
从这些说明它的健壮性、稳定性等方面不会有太大问题。
SQLite 的结构原理:
SQL语句编译器(Complier)由词法处理器( Tokenize) 、句法分析器( Parser)和代码生成器(Code generator) 组成。执行SQL 语句时,由接口把包含该SQL 语句的字符串传给词法处理器进行词法分析处理,把字符串分成一个一个的词法单元,然后把处理结果再传递句法分析器进行语法分析,它调用代码生成器产生能够在虚拟机(Virtual Machine) 上执行的指令码。
VirtualMachine 是为操作数据库文件而执行的一个抽象的计算机引擎,又称虚拟数据库引擎(VDBE) ,它是SQLite的核心.在SQL语句被分析之后,代码生成器将生成用VDBE的虚拟机器语言表示的一系列指令。如此反复,VDBE执行每条指令,最终完成SQL语句指定的查询,删除,存储等请求。
B - Tree,是SQLite实现数据存储的模式, 每个表和索引使用单独的B -Tree, 有助于的查找减到最小,快速有效。Pager Cache 对数据块读写提供高速缓冲,同时也提供回退、原子操作及对数据库文件加锁,通过缓冲机制,可以避免频繁进行I/O操作而降低应用程序的性能。
- 数据库接口
1.sqlite3_open()
连接数据库用sqlite3_open()函数,它们的声明如下
int sqlite3_open(
const char *filename, /* Database filename (UTF-8) */
sqlite3 **ppDb /* OUT: SQLite db handle */
);
在执行SQL命令之前,首先要连接数据库。因为SQLite数据库存储在一个单独的操作系统文件当中,所以连接数据库可以理解为“打开”数据库。filename不为空,先尝试打开,如果文件不存在,则用这个名字创建一个新的数据库。
2.sqlite3_close()
关闭连接使用sqlite3_close()函数,它的声明如下:
int sqlite3_close(sqlite3* ppDb);
为了sqlite3_close()能够成功执行,所有与连接所关联的已编译的查询必须被定案。如果仍然有查询没有完成,sqlite3_close()将返回SQLITE_BUSY和错误信息
3. sqlite3_exec()
- 函数sqlite3_exec()提供了一种执行SQL命令的快速、简单的方法,它特别适合处理对数据库的修改操作(不需要返回数据)。sqlite3_exec()的声明如下:
int sqlite3_exec(
sqlite3*, /* An open database */
const char *sql, /* SQL to be executed */
sqlite_callback, /* Callback function */
void *data /* 1st argument to callback function */
char **errmsg /* Error msg written here */
);
SQL命令由sql参数提供,它可以由多个SQL命令构成,sqlite3_exec()会对其中每个命令进行分析并执行,直到命令串结束或遇到一个错误。
列表6-1 对简单的命令使用sqlite3_exec()
#include <stdio.h>
#include <stdlib.h>
#include "util.h"
#pragma comment(lib, "sqlite3.lib")
int main(int argc, char **argv)
{
sqlite3 *db;
char *zErr;
int rc;
char *sql;
rc = sqlite3_open("test.db", &db);
if (rc) {
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
exit(1);
}
sql = "create table episodes( name text, id int)";
rc = sqlite3_exec(db, sql, NULL, NULL, &zErr);
if (rc != SQLITE_OK) {
if (zErr != NULL) {
fprintf(stderr, "SQL error: %s\n", zErr);
sqlite3_free(zErr);
}
}
sql = "insert into episodes (name,id) values ('Cinnamon Babka2',1)";
rc = sqlite3_exec(db, sql, NULL, NULL, &zErr);
if (rc != SQLITE_OK) {
if (zErr != NULL) {
fprintf(stderr, "SQL error: %s\n", zErr);
sqlite3_free(zErr);
}
}
sqlite3_close(db);
return 0;
}
- 预处理的查询
int sqlite3_prepare(
sqlite3 *db, /* Database handle */
const char *zSql, /* SQL statement, UTF-8 encoded */
int nByte, /* Maximum length of zSql in bytes. */
sqlite3_stmt **ppStmt, /* OUT: Statement handle */
const char **pzTail /* OUT: Pointer to unused portion of zSql */
);
跟sqlite3_exec()相似,sqlite3_prepare()也可以接受一个SQL语句的字符串查询
#include <string.h>
int main(int argc, char **argv)
{
int rc, i, ncols;
sqlite3 *db;
sqlite3_stmt *stmt;
char *sql;
const char *tail;
rc = sqlite3_open("test.db", &db);
if(rc) {
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
exit(1);
}
sql = "select * from episodes;";
rc = sqlite3_prepare(db, sql, (int)strlen(sql), &stmt, &tail);
if(rc != SQLITE_OK) {
fprintf(stderr, "SQL error: %s\n", sqlite3_errmsg(db));
}
rc = sqlite3_step(stmt);
ncols = sqlite3_column_count(stmt);
while(rc == SQLITE_ROW) {
for(i=0; i < ncols; i++) {
fprintf(stderr, "'%s' ", sqlite3_column_text(stmt, i));
}
fprintf(stderr, "\n");
rc = sqlite3_step(stmt);
}
sqlite3_finalize(stmt);
sqlite3_close(db);
return 0;
}
每次调用sqlite3_step()的时候,只返回一行数据,使用sqlite3_column_XXX()函数来取出这些数据.要取出全部的数据需要反复调用sqlite3_step().
接口函数返回一些状态参数,主要有几个:
SQLITE_ROW | sqlite3_step() has another row ready. |
SQLITE_DONE | sqlite3_step() has finished executing. |
SQLITE_BUSY | The database file is locked. |
SQLITE_OK | The operation was successful. |
4.其他主要接口函数
Get Table查询
int sqlite3_get_table(
sqlite3*, /* An open database */
const char *sql, /* SQL to be executed */
char ***resultp, /* Result written to a char *[] that this points to */
int *nrow, /* Number of result rows written here */
int *ncolumn, /* Number of result columns written here */
char **errmsg /* Error msg written here */
);
取字段信息
你可以使用sqlite3_column_name()来取得各字段的名称:
const char *sqlite3_column_name( sqlite3_stmt*, int iCol);
类似地,你可以使用sqlite3_column_type()取得各字段的存储类:
int sqlite3_column_type( sqlite3_stmt*, int iCol );
这个函数返回一个整数值,代表5个存储类的代码,定义如下:
#define SQLITE_INTEGER 1
#define SQLITE_FLOAT 2
#define SQLITE_TEXT 3
#define SQLITE_BLOB 4
#define SQLITE_NULL 5
取字段值
可以使用sqlite3_column_xxx()函数取当前记录中每个字段的值,其一般形式为:
xxx表示你希望得到的数据类型。sqlite3_column_xxx()包括以下函数:
int sqlite3_column_int(sqlite3_stmt*, int iCol);
double sqlite3_column_double(sqlite3_stmt*, int iCol);
long long int sqlite3_column_int64(sqlite3_stmt*, int iCol);
const void *sqlite3_column_blob(sqlite3_stmt*, int iCol);
const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
const void *sqlite3_column_text16(sqlite3_stmt*, int iCol);
对每个函数,SQLite都会将字段值从存储类转化为函数指定的结果类型
还有其他的一些接口函数,不常用。(sqlite.h)
- 使用说明
SQLite 存储的数据类型
SQLite有5个原始的数据类型,被称为存储类。SQLite存储类型:
名称 | 说明 |
INTEGER | 整数值是全数字(包括正和负)。整数可以是1, 2, 3, 4, 6或 8字节。整数的最大范围(8 bytes)是{-9223372036854775808, 0, +9223372036854775807}。SQLite根据数字的值自动控制整数所占的字节数。 |
REAL | 实数。SQLite使用8字节的浮点数来存储实数。IEEE标准浮点数 |
TEXT | 文本(TEXT)是字符数据。SQLite支持几种字符编码,包括UTF-8和UTF-16。字符串的大小没有限制。 |
BLOB | 二进制大对象(BLOB)是任意类型的数据。 |
NULL | NULL表示没有值。SQLite具有对NULL的完全支持。 |
SQLite通过值的表示法来判断其类型,下面就是SQLite的推理方法:
- SQL语句中用单引号或双引号括起来的文字被指派为TEXT。
- 如果文字是未用引号括起来的数据,并且没有小数点和指数,被指派为INTEGER。
- 如果文字是未用引号括起来的数据,并且带有小数点或指数,被指派为REAL。
- 用NULL说明的值被指派为NULL存储类。
- 如果一个值的格式为X'ABCD',其中ABCD为16进制数字,则该值被指派为BLOB。X前缀大小写皆可。
SQL函数typeof()根据值的表示法返回其存储类。使用这个函数,下面SQL语句返回的结果为:
typeof(3.14) typeof('3.14') typeof(314) typeof(x'3142') typeof(NULL)
real text integer blob null
采用的弱类型数据处理方式。
数据库锁
SQLite采用粗放型的锁。当一个连接要写数据库,所有其它的连接被锁住,直到写连接结束了它的事务。SQLite有一个加锁表,来帮助不同的写数据库都能够在最后一刻再加锁,以保证最大的并发性。
SQLite有5个不同的锁状态:未加锁(UNLOCKED)、共享(SHARED)、保留(RESERVED)、未决(PENDING)和排它(EXCLUSIVE)。每个数据库连接在同一时刻只能处于其中一个状态。每种状态(未加锁状态除外)都有一种锁与之对应。
锁机制可以对多进程访问时能预防读写冲突,同一时刻只能有一连接可以写数据,数据库可能会被写操作独占,从而导致其它读写操作阻塞或出错。
(1)一个事务可以在UNLOCKED,RESERVED或EXCLUSIVE三种状态下开始。默认情况下在UNLOCKED时开始。
(2)白色框中的UNLOCKED, PENDING, SHARED和 RESERVED可以在一个数据库的同一时存在。
(3)从灰色的PENDING开始,事情就变得严格起来,意味着事务想得到排斥锁(EXCLUSIVE)(注意与白色框中的区别)。
虽然锁有这么多状态,但是从体质上来说,只有两种情况:读事务和写事务。
- 注意事项
- 多个应用程序或一个应用程序的多个实例可以同时访问同一个数据库文件吗? 多个进程可同时打开同一个数据库。多个进程可以同时进行SELECT 操作,但在任一时刻,只能有一个进程对数据库进行更改。SQLite允许多进程 同时打开和读取数据库。任何一个进程需要写入时,整个数据库将在这一过程中被锁定。但这一般仅耗时 几毫秒。其他进程只需等待然后继续其他事务。
- SQLite在并发(包括多进程和多线程)读写方面的性能一直不太理想。数据库可能会被写操作独占,从而导致其它读写操作阻塞或出错。尽量减少并发性访问。
- SQLite是线程安全的吗?
一个sqlite3结构只能在调用 sqlite3_open创建它的那个进程中使用。你不能在一个线程中打开一个数据库然后把指针传递给另一个线程使用。这是因为大多数多线程系统的限制()当在线程间传递数据库连接时会出现严重的问题。因此,SQLite目前不允许在线程间共享句柄(连接)。
4.sqlite毕竟是面向嵌入式系统的小型数据库,我们 无法要求它拥有很高的并发能力
不要在一个进程或者线程里面开两个或者更多的连接
总结:减少并发性访问,不要在一个进程里面打开 同一个数据库文件的多个连接。