`
javachs
  • 浏览: 120620 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

sqlite的事务和锁

阅读更多
事务
事务定义了一组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执行第2、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开始你的事务。
分享到:
评论

相关推荐

    C语言SQLite3事务和锁的操作实例

    主要介绍了C语言SQLite3事务和锁的操作,结合完整实例形式分析了C语言针对SQLite3数据库的事务与锁相关操作技巧,需要的朋友可以参考下

    SQLite教程(十二):锁和并发控制详解

    主要介绍了SQLite教程(十二):锁和并发控制详解,本文讲解了锁和并发控制机制概述、文件锁、回滚日志、数据写入、SQL级别的事务控制等内容,需要的朋友可以参考下

    SQLite3数据库中的文件锁和同步机制

    PagerModue负责SQLite事务的ACID,也提供缓存功能。PagerModue不需要知道BTree,字符编码, 索引的结构, Pager Module用来管理Page, 一个Page对应一个DiskBlock, 大小一般是1024Byte。  1. SQLite3 数据库的锁...

    sqlite3.30.1 数据库模块+支持库

    要解决多线程问题,主要是对sqlite3的锁机制进行了解,和事务有密切的关系。本次更新的版本在开始事务时,增加了一个 事务锁状态 的参数。拿简单的话来描述就是 开始事务 时如果。设置了 事务锁状态_ 立即 参数,...

    C# SQLite开发包及实例源码.zip

    1.轻量级 使用 SQLite 只需要带一个动态库,就可以享受它的全部...SQLite 数据库通过数据库级上的独占性和共享锁来实现独立事务处理。这意味着多个进程可以在同一时间从同一数据库读取数据,但只能有一个可以写入数据。

    sqlite3易语言支持库和模块,相对于1.0的更新

    开始事务() 增加了 事务锁状态 参数,此参数在多线程中非常重要。5、记录集必须手动关闭,任何内部方法都不再自动关闭。6、增加 数据库.附加数据库() 的密码参数;7、增加 数据库.取记录集多个() 支持分号分割的...

    易语言-sqlite3.30.1 数据库模块+支持库

    要解决多线程问题,主要是对sqlite3的锁机制进行了解,和事务有密切的关系。本次更新的版本在开始事务时,增加了一个 事务锁状态 的参数。 拿简单的话来描述就是 开始事务 时如果 设置了 事务锁状态_ 立即 参数,...

    SQLite这么娇小可爱,不多了解点都不行啊

    在我眼里,MySQL和Oracle是这样的而SQLite在是这样的所以这么萌的...SQLite的事务和锁是很重要的概念。SQLite有5个不同的锁状态SQLite有一个加锁表,记录数据库连接的锁状态。每个数据库连接在同一时刻只能处于其中一个

    SQLite入门分析

    SQLite入门与分析(四)---Page Cache之事务处理.doc SQLite入门与分析(五)---Page Cache之并发控制.doc SQLite入门与分析(六)---再谈SQLite的锁.doc

    sqlite:转到SQLite3驱动程序

    连接被,如果启用了SQLite模式,则该软件包将利用来最大程度地减少处理数据库锁争用所需的用户代码处理量。 。 它具有一些SQLite更有趣的扩展的接口,例如和。 实用程序包提供了一些高级工具,使使用SQLite执行常见...

    transaction_retry:重试有关死锁和事务序列化错误的数据库事务。 支持MySQL,PostgreSQL和SQLite

    支持MySQL,PostgreSQL和SQLite。 例子 通过拯救ActiveRecord :: TransactionIsolationConflict并重试事务,gem自动工作。 安装 将此添加到您的Gemfile中: gem 'transaction_retry' 然后运行: bundle 它可以与...

    APP开发教程 Java Android移动端开发 12、Android数据存储和共享(一) 共21页.pptx

    Linux用户权限管理 存储一:Share Prefences 存储二:FILE 存储三:Sqlite数据库 SQLite3数据库的操作SQLiteDatabase/SQLiteDatabaseHelper SQLite数据库事务和锁

    sqlite3.30.1 数据库模块+支持库(彻底解决多线程死锁问题)-易语言

    要解决多线程问题,主要是对sqlite3的锁机制进行了解,和事务有密切的关系。本次更新的版本在开始事务时,增加了一个 事务锁状态 的参数。 拿简单的话来描述就是 开始事务 时如果 设置了 事务锁状态_ 立即 参数,...

    史上最强全面解析SQLite

    测试2:在事务处理程序中的 25000 INSERTs........................................... 25 测试3:在编入索引表格中的25000 INSERTs ........................................... 26 测试4:没有索引的 100 SELECTs.....

    BigData:SQLite单表4亿订单,大数据测试

    写入向前日志模式,避免多线程访问时锁定数据库,写入时不必使用排它锁影响其它线程读取,而是把事务操作写入到WAL文件中,延迟合并 加大缓存,Cache Size=5000,提升性能。操作系统通过文件映射MapFile把整个数据库...

    LevenBlog2.0.7版源码

    2.针对SQLite修改SQLite版本的事务策略,由于SQLite在本身和Windows系统本身的原因,SQLite在大量并发的情况下可能造出死锁,修改后的事务策略将有助于一次请求更快的执行,但是,由于SQLite锁的因素,在大量并发的情况下...

Global site tag (gtag.js) - Google Analytics