SQLite简介

1. SQLite说明

SQLite是一款轻量级数据库,是遵守ACID的关联式数据库管理系统.它的设计目的是嵌入式。目前已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百KB的内存就够了。它能够支持Windows/Linux/Unix/Android等等主流的操作系统,同时能够跟很多程序语言相结合,比如 C/C++,Tcl、C#、PHP、Java等,还有ODBC接口,同样比起Mysql、PostgreSQL这两款开源世界著名的数据库管理系统来讲,它的处理速度比他们都快。SQLite的版权允许无限制地应用,不用支付任何使用和授权费用,已经成为了最受欢迎的开源数据库之一。SQLite网站(www.sqlite.org)同时提供SQLite的已编译版本和源程序。

 

 May you do good and not evil. 愿你行善莫行恶May you find forgiveness for yourself and forgive others. 愿你原谅自己宽恕他人May you share freely, never taking more than you give. 愿你宽心与人分享,索取不多于你所施予

 

SQLite的特性:

  • 轻量级
     SQLite 是一个轻量级数据库 本身足够小,大致3万行C代码, 250K,但支持数据库大小至2TB
  • 独立性
    SQLite 数据库的核心引擎不需要依赖第三方软件,也不需要所谓的“安装”。
  • 隔离性
    SQLite 数据库中所有的信息(比如表、视图、触发器等)都包含在一个文件夹内,方便管理和维护。
  • 跨平台
    SQLite 目前支持大部分操作系统,不至电脑操作系统更在众多的手机系统也是能够运行,比如:Android。
  • 多语言接口
    SQLite 数据库支持多语言编程接口。
  • 安全性
    SQLite 数据库通过数据库级上的独占性和共享锁来实现独立事务处理。这意味着多个进程可以在同一时间从同一数据库读取数据,但只能有一个可以写入数据.
  • 开源
    SQLite的源代码是完全开源的,  良好注释的源代码,并且有着90%以上的测试覆盖率 ,  

·       还内置地同时支持UTF-8和UTF-16编码。

·       ACID

原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)

 

SQLite 简介:

 

SQLite是D.Richard Hipp于2000年开发出来的一个自容纳、可嵌入、零配置数据库引擎

的小型C库。它支持多数SQL92标准。SQLite3.0的C编程一共涉及到83个API函数,此外还有一些数据结构和预定义。这些接口的使用一般都不复杂,最简单的程序只要三个函数就可以完成:sqlite3_open(),sqlite3_exec() 和sqlite3_close() 。如果想更好地控制数据库引擎的执行, 可以使用sqlite3_prepare()函数把SQL语句编译成字节码,然后再使用sqlite3_step()来执行编译好的字节码,以sqlite3_column_xxx()表示的一组函数来获取查询结果集中的信息,其中xxx代表返回数值的数据类型,如int、text、blob等。

总的来说,只要修改数据库的记录而不用获得返回值的时候,如使用insert、update、

delete语句来分别向某个表中插入、更新、删除某条记录,可以选择使用sqlite3_exec()函数,

即采用打包(wrapped) SQL查询语句的方法。这时sqlite3_exec()可以不用指定任何回调函数,

从而快速地执行一条或多条SQL语句。当SQL语句有返回值时,如使用select语句来获取某

个表中的查询结果的时候,可使用sqlite3_prepare()函数,即采用预编译(prepared) SQL语句

的方法。这样可以更好地获取返回记录的详细信息,并且还可以使用参数化的SQL语句从而

避免重复地编译一条类似的查询语句。

 

对于程序员来说,SQLite就象一个数据传送带,提供了一种方便的将应用程序绑定的数据的方法。就象传送带一样,对SQLite的使用没有终点。

除了仅仅作为一个存储容器,SQLite还可以作为一个单纯的数据处理的工具。如果大小和复杂性合适,使用SQLite可以很容易地将应用程序所使用的数据结构转化为表,并保存在一个内在数据库中。用此方法,你可以操作互相关联的数据,可以完成很繁重的任务页不必写自己的算法来对数据结构操作和排序。

 

SQLite 的应用:

 

SQLite已经被多种软件和产品所使用。它被用在Apple的Mac OS X操作系统中,被用作其CoreData应用程序架构的一部分。它还应用于Safari的Web浏览器、Mail.app的电子邮件程序、RSS的管理、Apple的Aperture照片软件。

 

 

 

SQLite 的结构:

 


 


 

2. 操作数据库

Ø  打开数据库用sqlite3_open()函数,它们的声明如下

int sqlite3_open(

    const char *filename,           /* Database filename (UTF-8) */

    sqlite3 **ppDb                    /* OUT: SQLite db handle */

);

 

Ø  关闭连接使用sqlite3_close()函数,它的声明如下:

int sqlite3_close(sqlite3*);

 

为了sqlite3_close()能够成功执行,所有与连接所关联的已编译的查询必须被定案。如果仍然有查询没有定案,sqlite3_close()将返回SQLITE_BUSY和错误信息

Ø  函数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 */

);

说明: 通常, sqlite3_callback 和它后面的void * 这两个位置都可以填NULL。填NULL 表示你不需要回调。比如你做insert 操作,

做delete 操作, 就没有必要使用回调。而当你做select 时, 就要使用回调, 因为SQLite3 把数据查出来, 得通过回调告诉你查出了什么

数据。

( 4)

SQL命令由sql参数提供,它可以由多个SQL命令构成,sqlite3_exec()会对其中每个命令进行分析并执行,直到命令串结束或遇到一个错误。列表6-1(来自create.c)说明了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( id integer primary key,"

       "                  name text,cid 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 ('CinnamonBabka2',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;   

}

 

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.


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 */

);

 

Listing 6-4. Using sqlite3_get_table

void main(int argc, char **argv)

{

   sqlite3 *db;

   char *zErr;

   int rc,i;

   char *sql;

   char **result;

       intnrows, ncols;

 

   /* Connect to database, etc. */

   rc = sqlite3_open("test.db", &db);

 

   sql = "select * from episodes;";

   rc = sqlite3_get_table(db, sql, &result, &nrows, &ncols,&zErr);

 

   /* Do something with data */

       printf("rows=%d,cols=%d\n",nrows,ncols);

       for(i=0;i<=nrows;i++)

              printf("%-5s%-20s%-5s\n",result[3*i],result[3*i+1],result[3*i+2]);

 

   /* Free memory */

   sqlite3_free_table(result);

}

If, for example, the result set returned isof the form

 

rows=2,cols=3

id  name                cid

1   Cinnamon Babka2     (null)

2   Mackinaw Peaches    1

 

 

 

预处理的查询

As you’ll recall from Chapter 5, preparedqueries are performed in three basic steps: compilation, execution, andfinalization. This process is illustrated in Figure 6-1.

Figure 6-1. Prepared query processing

 

Now that you’ve seen the whole process, let’sgo through an example. A simple, complete program using a prepared query islisted in Listing 6-6. It is taken from select.c in the examples.

 

Listing 6-6. Using Prepared Queries

#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_column_name()来取得各字段的名称:

   const char *sqlite3_column_name( sqlite3_stmt*, /* statement handle */

       int iCol /* column ordinal */);

类似地,你可以使用sqlite3_column_type()取得各字段的存储类:

   int sqlite3_column_type( sqlite3_stmt*, /* statement handle */

       int iCol /* column ordinal */);

这个函数返回一个整数值,代表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( sqlite3_stmt*, /*statement handle */

   int iCol /* column ordinal */);

xxx表示你希望得到的数据类型。sqlite3_column_xxx()包括以下函数:

int sqlite3_column_int(sqlite3_stmt*, intiCol);

double sqlite3_column_double(sqlite3_stmt*,int iCol);

long long intsqlite3_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都会将字段值从存储类转化为函数指定的结果类型

3. 使用说明

SQLite 存储的数据类型

SQLite有5个原始的数据类型,被称为存储类。存储类这个词表明了一个值在磁盘上存储的格式,其实就是类型或数据类型的同义词。这5个存储类在表4-6中描述。

表 4-6 SQLite存储类

名称

说明

INTEGER

整数值是全数字(包括正和负)。整数可以是1, 2, 3, 4, 6或 8字节。整数的最大范围(8 bytes)是{-9223372036854775808, 0, +9223372036854775807}。SQLite根据数字的值自动控制整数所占的字节数。

空注:参可变长整数的概念。

REAL

实数是10进制的数值。SQLite使用8字节的符点数来存储实数。

TEXT

文本(TEXT)是字符数据。SQLite支持几种字符编码,包括UTF-8和UTF-16。字符串的大小没有限制。

BLOB

二进制大对象(BLOB)是任意类型的数据。BLOB的大小没有限制。

NULL

NULL表示没有值。SQLite具有对NULL的完全支持。

SQLite通过值的表示法来判断其类型,下面就是SQLite的推理方法:

l  SQL语句中用单引号或双引号括起来的文字被指派为TEXT。

l  如果文字是未用引号括起来的数据,并且没有小数点和指数,被指派为INTEGER。

l  如果文字是未用引号括起来的数据,并且带有小数点或指数,被指派为REAL。

l  用NULL说明的值被指派为NULL存储类。

l  如果一个值的格式为X'ABCD',其中ABCD为16进制数字,则该值被指派为BLOB。X前缀大小写皆可。

SQL函数typeof()根据值的表示法返回其存储类。使用这个函数,下面SQL语句返回的结果为:

sqlite> select typeof(3.14),typeof('3.14'), typeof(314), typeof(x'3142'), typeof(NULL);

typeof(3.14)   typeof('3.14')  typeof(314)    typeof(x'3142')      typeof(NULL)

real                text               integer           blob                      null

 

再看下面例子:

CREATE TABLE foo(x, y, z);

现在执行同样的插入语句:

INSERT INTO foo VALUES ('1', '1', '1');

现在,x、y和z中存储的是什么类型呢?答案是TEXT、TEXT和TEXT。

那么,是否SQLite的字段类型默认为TEXT呢?再看,还是第2个表,执行如下插入语句:

INSERT INTO foo VALUES (1, 1.0, x'10');

现在,x、y和z中存储的是什么类型呢?答案是INTEGER、REAL和BLOB。

 

如果你愿意,可以为SQLite的字段定义类型,这看起来跟其它数据库一样。但这不是必须的,你可以尽管违反类型定义。这是因为在任何情况下,SQLite都可以接受一个值并推断它的类型。

SQLite 最大的特点在于其数据类型为无数据类型(typelessness)。这意味着可以保存任何类型的数据到所想要保存的任何表的任

何列中, 无论这列声明的数据类型是什么。虽然在生成表结构的时候, 要声明每个域的数据类型, 但SQLite 并不做任何检查。开

发人员要靠自己的程序来控制输入与读出数据的类型。这里有一个例外, 就是当主键为整型值时, 如果要插入一个非整型值时会

产生异常。

数据库锁

在SQLite中,锁和事务是紧密联系的。为了有效地使用事务,需要了解一些关于如何加锁的知识。

SQLite采用粗放型的锁。当一个连接要写数据库,所有其它的连接被锁住,直到写连接结束了它的事务。SQLite有一个加锁表,来帮助不同的写数据库都能够在最后一刻再加锁,以保证最大的并发性。

SQLite使用锁逐步上升机制,为了写数据库,连接需要逐级地获得排它锁。SQLite有5个不同的锁状态:未加锁(UNLOCKED)、共享(SHARED)、保留(RESERVED)、未决(PENDING)和排它(EXCLUSIVE)。每个数据库连接在同一时刻只能处于其中一个状态。每种状态(未加锁状态除外)都有一种锁与之对应。

最初的状态是未加锁状态,在此状态下,连接还没有存取数据库。当连接到了一个数据库,甚至已经用BEGIN开始了一个事务时,连接都还处于未加锁状态。

未加锁状态的下一个状态是共享状态。为了能够从数据库中读(不写)数据,连接必须首先进入共享状态,也就是说首先要获得一个共享锁。多个连接可以同时获得并保持共享锁,也就是说多个连接可以同时从同一个数据库中读数据。但哪怕只有一个共享锁还没有释放,也不允许任何连接写数据库。

如果一个连接想要写数据库,它必须首先获得一个保留锁。一个数据库上同时只能有一个保留锁。保留锁可以与共享锁共存,保留锁是写数据库的第1阶段。保留锁即不阻止其它拥有共享锁的连接继续读数据库,也不阻止其它连接获得新的共享锁。

一旦一个连接获得了保留锁,它就可以开始处理数据库修改操作了,尽管这些修改只能在缓冲区中进行,而不是实际地写到磁盘。对读出内容所做的修改保存在内存缓冲区中。

当连接想要提交修改(或事务)时,需要将保留锁提升为排它锁。为了得到排它锁,还必须首先将保留锁提升为未决锁。获得未决锁之后,其它连接就不能再获得新的共享锁了,但已经拥有共享锁的连接仍然可以继续正常读数据库。此时,拥有未决锁的连接等待其它拥有共享锁的连接完成工作并释放其共享锁。

一旦所有其它共享锁都被释放,拥有未决锁的连接就可以将其锁提升至排它锁,此时就可以自由地对数据库进行修改了。所有以前对缓冲区所做的修改都会被写到数据库文件。

 

还可能存在死锁,SQLite对死锁有一套预防机制。


 

 

4. 注意事项

1. 多个应用程序或一个应用程序的多个实例可以同时访问同一个数据库文件吗?

多个进程可同时打开同一个数据库。多个进程可以同时进行SELECT 操作,但在任一时刻,只能有一个进程对数据库进行更改。

SQLite使用读、写锁控制对数据库的访问。

 

多个应用程序或者同一个应用程序的多个例程能同时存取同一个数据库文件吗?

多进程可以同时打开同一个数据库,也可以同时 SELECT 。但只有一个进程可以立即改数据库。

SQLite使用读/写锁定来控制数据库访问。

 

我们知道没有其他的嵌入式 SQL数据库引擎比SQLite支持更多的并发性。 SQLite允许多进程 同时打开和读取数据库。任何一个进程需要写入时,整个数据库将在这一过程中被锁定。但这一般仅耗时 几毫秒。其他进程只需等待然后继续其他事务。其他嵌入式SQL数据库引擎往往只允许单进程访问数据库。

 

当 SQLite 尝试操作一个被另一个进程锁定的文件时,缺省的行为是返回 SQLITE_BUSY。你可以用 C代码更改这一行为。 使用 sqlite3_busy_handler() 或sqlite3_busy_timeout() API函数。如果两个或更多进程同时打开同一个数据库,其中一个进程创建了新的表或索引,则其它进程可能不能立即看见新的表。其它进程可能需要关闭并重新连结数据库。

(8) SQLite是线程安全的吗?

有时候是的。为了线程安全,SQLite 必须在编译时把 THREADSAFE 预处理宏设为1。在缺省的发行的已编译版本中 Windows 版的是线程安全的,而 Linux 版的不是。如果要求线程安全,Linux 版的要重新编译。

线程安全”是指二个或三个线程可以同时调用独立的不同的sqlite3_open() 返回的"sqlite3"结构。而不是在多线程中同时使用同一个 sqlite3 结构指针。

一个sqlite3结构只能在调用 sqlite3_open创建它的那个进程中使用。你不能在一个线程中打开一个数据库然后把指针传递给另一个线程使用。这是因为大多数多线程系统的限制(或 Bugs?)例如RedHat9上。在这些有问题的系统上,一个 线程创建的fcntl()锁不能由另一个线程删除或修改。由于SQLite依赖fcntl()锁来进行并发控制,当在线程间传递数据库连接时会出现严重的问题。

也许在Linux下有办法解决fcntl()锁的问题,但那十分复杂并且对于正确性的测试将是极度困难的。因此,SQLite目前不允许在线程间共享句柄。

 

如果两个或更多进程同时打开同一个数据库,其中一个进程创建了新的表或索引,则其它进程可能不能立即看见新的表。其它进程可能需要关闭并重新连结数据库。 

 

sqlite毕竟是面向嵌入式系统的小型数据库,我们 无法要求它拥有很高的并发能力

不要在一个进程或者线程里面开两个或者更多的连接

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值