Sqlite3 事务和锁机制

事务 
事务定义了一组SQL命令的边界,这组命令或者做为一个总体被所有执行,或者都不执行。事务的典型实例是转账。 
事务的范围 
事务由3个命令控制:BEGIN、COMMIT和ROLLBACK。BEGIN开始一个事务,以后的全部操做均可以取消。COMMIT使BEGIN后的全部命令获得确认;而ROLLBACK还原BEGIN以后的全部操做。如: 
sqlite> BEGIN; 
sqlite> DELETE FROM foods; 
sqlite> ROLLBACK; 
sqlite> SELECT COUNT(*) FROM foods; 
COUNT(*) 
412 
上面开始了一个事务,先删除了foods表的全部行,可是又用ROLLBACK进行了回卷。再执行SELECT时发现表中没发生任何改变。 
SQLite默认状况下,每条SQL语句自成事务(自动提交模式)。 
冲突解决 
如前所述,违反约束会致使事务的非法结束。大多数数据库(管理系统)都是简单地将前面所作的修改所有取消。 
SQLite有其独特的方法来处理约束违反(或说从约束违反中恢复),被称为冲突解决。 
如: 
sqlite> UPDATE foods SET id=800-id; 
SQL error: PRIMARY KEY must be unique 
SQLite提供5种冲突解决方案:REPLACE、IGNORE、FAIL、ABORT和ROLLBACK。 
REPLACE: 当发违反了惟一完整性,SQLite将形成这种违反的记录删除,替代以新插入或修改的新记录,SQL继续执行,不报错。 
IGNORE 
FAIL 
ABORT 
ROLLBACK 
数据库锁 
在SQLite中,锁和事务是紧密联系的。为了有效地使用事务,须要了解一些关于如何加锁的知识。 
SQLite采用粗放型的锁。当一个链接要写数据库,全部其它的链接被锁住,直到写链接结束了它的事务。SQLite有一个加锁表,来帮助不一样的写数据库都可以在最后一刻再加锁,以保证最大的并发性。 
SQLite使用锁逐步上升机制,为了写数据库,链接须要逐级地得到排它锁。SQLite有5个不一样的锁状态:未加锁(UNLOCKED)、共享 (SHARED)、保留(RESERVED)、未决(PENDING)和排它(EXCLUSIVE)。每一个数据库链接在同一时刻只能处于其中一个状态。每 种状态(未加锁状态除外)都有一种锁与之对应。 
最初的状态是未加锁状态,在此状态下,链接尚未存取数据库。当链接到了一个数据库,甚至已经用BEGIN开始了一个事务时,链接都还处于未加锁状态。 
未加锁状态的下一个状态是共享状态。为了可以从数据库中读(不写)数据,链接必须首先进入共享状态,也就是说首先要得到一个共享锁。多个链接能够 同时得到并保持共享锁,也就是说多个链接能够同时从同一个数据库中读数据。但哪怕只有一个共享锁尚未释放,也不容许任何链接写数据库。 
若是一个链接想要写数据库,它必须首先得到一个保留锁。一个数据库上同时只能有一个保留锁。保留锁能够与共享锁共存,保留锁是写数据库的第1阶段。保留锁即不阻止其它拥有共享锁的链接继续读数据库,也不阻止其它链接得到新的共享锁。 
一旦一个链接得到了保留锁,它就能够开始处理数据库修改操做了,尽管这些修改只能在缓冲区中进行,而不是实际地写到磁盘。对读出内容所作的修改保存在内存缓冲区中。 
当链接想要提交修改(或事务)时,须要将保留锁提高为排它锁。为了获得排它锁,还必须首先将保留锁提高为未决锁。得到未决锁以后,其它链接就不能 再得到新的共享锁了,但已经拥有共享锁的链接仍然能够继续正常读数据库。此时,拥有未决锁的链接等待其它拥有共享锁的链接完成工做并释放其共享锁。 
一旦全部其它共享锁都被释放,拥有未决锁的链接就能够将其锁提高至排它锁,此时就能够自由地对数据库进行修改了。全部之前对缓冲区所作的修改都会被写到数据库文件。 
死锁 
为何须要了解锁的机制呢?为了不死锁。 
考虑下面表4-7所假设的状况。两个链接——A和B——同时但彻底独立地工做于同一个数据库。A执行第1条命令,B执行第二、3条,等等。 
表4-7 一个死锁的假设状况 

A链接                                     B链接 
sqlite> BEGIN; 
                                    sqlite> BEGIN; 
                                    sqlite> INSERT INTO foo VALUES('x'); 
sqlite> SELECT * FROM foo; 
                                    sqlite> COMMIT; 
                                    SQL error: database is locked 
sqlite> INSERT INTO foo VALUES ('x'); 
SQL error: database is locked 

两个链接都在死锁中结束。B首先尝试写数据库,也就拥有了一个未决锁。A再试图写,但当其INSERT语句试图将共享锁提高为保留锁时失败。 
为了讨论的方便,假设链接A和B都一直等待数据库可写。那么此时,其它的链接甚至都不可以再读数据库了,由于B拥有未决锁(它能阻止其它链接得到共享锁)。那么时此,不只A和B不能工做,其它全部进程都不能再操做此数据库了。 
若是避免此状况呢?固然不能让A和B经过谈判解决,由于它们甚至不知道彼此的存在。答案是采用正确的事务类型来完成工做。 
事务的种类 
SQLite有三种不一样的事务,使用不一样的锁状态。事务能够开始于:DEFERRED、MMEDIATE或EXCLUSIVE。事务类型在BEGIN命令中指定: 
BEGIN [ DEFERRED | IMMEDIATE | EXCLUSIVE ] TRANSACTION; 
一个DEFERRED事务不获取任何锁(直到它须要锁的时候),BEGIN语句自己也不会作什么事情——它开始于UNLOCK状态。默认状况下就 是这样的,若是仅仅用BEGIN开始一个事务,那么事务就是DEFERRED的,同时它不会获取任何锁;当对数据库进行第一次读操做时,它会获取 SHARED锁;一样,当进行第一次写操做时,它会获取RESERVED锁。 
由BEGIN开始的IMMEDIATE事务会尝试获取RESERVED锁。若是成功,BEGIN IMMEDIATE保证没有别的链接能够写数据库。可是,别的链接能够对数据库进行读操做;可是,RESERVED锁会阻止其它链接的BEGIN IMMEDIATE或者BEGIN EXCLUSIVE命令,当其它链接执行上述命令时,会返回SQLITE_BUSY错误。这时你就能够对数据库进行修改操做了,可是你还不能提交,当你 COMMIT时,会返回SQLITE_BUSY错误,这意味着还有其它的读事务没有完成,得等它们执行完后才能提交事务。 
EXCLUSIVE事务会试着获取对数据库的EXCLUSIVE锁。这与IMMEDIATE相似,可是一旦成功,EXCLUSIVE事务保证没有其它的链接,因此就可对数据库进行读写操做了。 
上节那个例子的问题在于两个链接最终都想写数据库,可是它们都没有放弃各自原来的锁,最终,SHARED锁致使了问题的出现。若是两个链接都以 BEGIN IMMEDIATE开始事务,那么死锁就不会发生。在这种状况下,在同一时刻只能有一个链接进入BEGIN IMMEDIATE,其它的链接就得等待。BEGIN IMMEDIATE和BEGIN EXCLUSIVE一般被写事务使用。就像同步机制同样,它防止了死锁的产生。 
基本的准则是:若是你正在使用的数据库没有其它的链接,用BEGIN就足够了。可是,若是你使用的数据库有其它的链接也会对数据库进行写操做,就得使用BEGIN IMMEDIATE或BEGIN EXCLUSIVE开始你的事务
相关文章
相关标签/搜索