SQlite数据库的C编程接口七 数据库锁定Database Locking ——《Using SQlite》读书笔记

SQlite数据库的C编程接口(七)数据库锁定(Database Locking) by斜风细雨QQ:253786989 2012-02-09

对于《UsingSQLite》的这一节内容,理解的不是很清楚。有时间要仔细看看SQLite的文档:http://www.sqlite.org/lockingv3.html(FileLockingAndConcurrencyInSQLiteVersion3

SQLite使用一些不同的锁来保护数据库,以允许多个数据库连接同时访问一个相同的数据库文件,而不会出现数据库损坏。不管是在“自动提交事务(autocommittransaction)”模式,还是“显示事务(explicittransaction)”模式,这些锁都工作良好。

SQLite锁系统(lockingsystem)涉及几个不同层次的锁,用来减少竞争、避免死锁等等。以使SQlite允许多个数据库连接并行读取同一个数据库文件,不过任何写操作都需要完整的,整个数据库文件的独占访问。

大部分时间锁系统(lockingsystem)工作良好,允许不同的应用程序之间方便和安全的分享同一个数据库文件。如果编码得当,大部分写操作仅仅需要几分之一秒。然而,如果多个数据库连接试图在同一时间访问同一个数据库文件,那这些操作迟早会相遇。通常情况下,如果一个数据库操作需要一个暂时无法得到的锁,那么SQLite会返回SQLITE_BUSY,或者在更极端的情况下,返回SQLITE_IOERR(或者扩展码SQLITE_IOERR_BLOCKED)。函数sqlite3_prepare_xxx、sqlite3_step、sqlite3_reset、sqlite3_finalize会返回SQLITE_BUSY。函数sqlite3_backup_step、sqlite3_blob_open也会返回SQLITE_BUSY,因为在这两个函数的内部都是通过调用sqlite3_prepare_xxx、sqlite3_step函数来完成工作的。如果调用sqlite3_close函数时,所连接的数据库存在没有销毁(unfinalize)的语句,则该函数也会返回SQLITE_BUSY

如果想访问某个锁,就需要等待其所有者完成并释放对它的使用,通常不会等待太长时间。等待(waiting)状态可以由应用程序处理,比如接收到SQLITE_BUSY应答,则再次尝试处理该语句。或者也可以由一个忙处理程序(busyhandler)来处理。

忙处理程序(Busyhandlers

busyhandler(忙处理程序)是一个回调函数,当SQLitelibrary无法获取一个锁时调用。在busyhandler中,可以继续尝试获取锁的操作,或者放弃并返回SQLITE_BUSY错误码。

SQLite有一个内置的基于定时器的busyhandler,可以给这个busyhandler设置一个以毫秒为单位的超时时间。在超时时间范围内,busyhandler将继续重复尝试获取锁的操作。

int sqlite3_busy_timeout(sqlite3*,int ms);

用来设置内置busyhandler的超时时间,以毫秒为单位。如果给ms参数传递0或者负值,则内置的busyhandler被清除。

程序员也可以自己写busyhandler,然后通过aqlite3_busy_handler函数进行设置。

int sqlite3_busy_handler(sqlite3*,int(*)(void*,int),void*);

给指定的数据库连接设置自定义的busyhandler,把自己写的busyhandler函数传递给该函数的第2个参数。如果给第2个参数传递NULL,则移除自定义的busyhandler。第3个参数是传递给busyhandler回调函数的用户数据指针。

自定义的busyhandler回调函数原型如下:

int user_defined_busy_handler_callback( void *udp,int incr )

第1个参数是通过sqlite3_busy_handler函数传递过来的用户数据指针。第2个参数是一个计数器,每次busyhandler被调用时该计数器的计数递增。如果该回调函数返回0,则SQLite将放弃获取锁的操作,并返回SQLITE_BUSY。如果该函数返回一个非0值,则SQLite将继续尝试获取锁的操作。

需要注意的是:一个数据库连接仅仅可以拥有一个busyhandler,不能同时设置自定义的busyhandler并配置内置的基于定时器的busyhandler。每次设置其中一种busyhandler都将删除另一个busyhandler

死锁(Deadlocks

为一个数据库连接设置busyhandler不能够解决所有问题。有的时候在多个数据库连接之间会出现死锁现象。比如两个数据库连接各自持有一些锁,但是它们现在都在等待对方持有的锁被释放,这样就会造成死锁现象。唯一的解决办法,就是其中一个数据库连接放弃获取锁的操作,并释放自己所持有的锁。

如果SQLite检测到了一个潜在的死锁情况,它将跳过busyhandler,并将有一个数据库连接立刻返回SQLITE_BUSY。这样做是为了鼓励应用程序释放他们自己的锁,以打破死锁现象。

避免SQLITE_BUSYAvoidingSQLITE_BUSY

如果我们在开发某个项目时需要充分考虑数据库的并发性能,最简单的方法是使用sqlite3_busy_timeout设置内置的busyhandler,并且超时时间在250-2000毫秒范围内进行调整,这样可以减少SQLITE_BUSY的产生。

如果想完全避免SQLITE_BUSY,唯一的办法就是确保对于同一个数据库某一个时刻仅仅存在一个数据库连接。这需要设置PRAGMAlocking-modeEXCLUSIVE(独占的)。

另外,应用程序可以使用独占模式(EXCLUSIVE)的事务,这样对于SQLITE_BUSY返回值的处理就更容易些。可以通过BEGINEXCLUSIVETRANSACTION命令开启一个独占式的事务,如果成功则在事务的执行过程中不会返回SQLITE_BUSY。不过BEGIN命令本身可能会执行失败并返回SQLITE_BUSY,但这种情况处理起来很容易,应用程序可以通过sqlite3_reset函数重置BEGIN语句,然后重试。BEGINEXCLUSIVE的缺点是只有目前没有其他的数据库连接(包括只读事务)正在访问该数据库,它才会执行成功。而且一旦独占式的事务开始执行,它同样会锁住数据库,使其他的数据库连接(包括只读事务)无法访问该数据库。

为了允许更多的并发访问,还有一种类型的事务——IMMEDIATETRANSACTION(即时事务)。可以通过BEGINIMMEDIATETRANSACTION命令启动一个即时事务,如果成功则在事务执行的过程中通常只有执行到COMMIT语句时才会返回SQLITE_BUSY。不管是事务中的哪些命令(包括COMMIT),一旦遇到了SQLITE_BUSY,应用程序可以简单的重置语句然后等待并重试。BEGINIMMEDIATE语句本身也有可能会遭遇SQLITE_BUSY,这时应用程序也是可以简单的重置BEGIN语句然后重试。与EXCLUSIVETRANSACTION不同的是,如果存在其它的数据库连接正在读取(非写)数据库,这时候IMMEDIATE事务是可以启动的。一旦IMMEDIATE事务成功启动,则不允许其它数据库连接进行写入操作,但只读的数据库连接仍然可以访问数据库,除非IMMEDIATE事务正在强制修改数据库文件(通常是事务正在执行COMMIT操作)。使用IMMEDIATE事务不会发生死锁现象,所有的SQLITE_BUSY可以通过重试操作进行处理。

避免死锁(Avoidingdeadlocks

避免死锁的原则比较简单,不过遵循这些原则会使应用程序变得复杂一些。

首先,sqlite3_prepare_xxx、sqlite3_backup_step、sqlite3_blob_open函数调用不会产生死锁现象。任何时候如果这些函数返回SQLITE_BUSY,简单的等待一下然后重试即可。

如果在一个(deferred)事务中,sqlite3_step、sqlite3_reset、sqlite3_finalize函数返回SQLITE_BUSY,则应用程序必须回退然后重试。如果这些语句不在一个显示的(explicit)事务中,prepared语句可以简单的重置然后重新执行。如果这些语句在一个显示的(explicit)事务中,那么整个事务就必须回退,然后从头开始执行。致使回退的整个原因就是其他一些数据库连接需要修改数据库。另外需要注意的是,如果应用程序完成了一些读取操作并准备进行写操作,那么在新的事务中最好重新读取这些信息以确定这些数据仍然有效。

不管你做什么,不要忽视SQLITE_BUSY。它可能很少发生,但如果处理不当它就可能成为大麻烦的源头。

当“繁忙”转换为“被阻塞”(WhenBUSYbecomesBLOCKED

当一个连接需要修改数据库中的数据,数据库就要被加锁使其对于其他连接处于只读状态。事实上,事务中对于数据库的某个修改并没有马上写入数据库文件,而是保存在数据库的页缓存中。因为如果将这个修改直接写入数据库,那么这个修改对于其他的读连接就可见了,这就打破了事务的隔离性原则。

当所有需要的修改操作全部做完,事务开始准备提交。这时数据库文件会被进一步锁定,不允许新的只读事务启动。允许已经存在的读操作(reader)完成,并释放他们持有的数据库锁。当所有的读操作完成,写操作(writer)就要独占式的访问数据库,最终将页缓存中的修改刷新到数据库中。

这一过程允许写事务正在执行的同时,只读事务依然可以继续运行。当写事务确实在提交数据时,读事务被锁定。然而,一个关键的假设是在这个过程中,所有修改被放入页缓存中,直到事务提交的时候才被写入数据库。如果缓存(cache)中装满了(包含处于悬挂状态的修改的)数据页,那么写事务没有其它选择,只能给数据库加一个独占锁,并且在提交(commit)阶段之前刷新缓存。该事务仍然可以在任何时候回滚,但是写入操作必须获得独占锁的即时访问,以刷新缓存。

如果这个锁不是立即可用,写入操作就会被强制终止整个事务。写入事务将回退,然后返回扩展码SQLITE_IOERR_BLOCKED。因为事务是自动回退的,所以应用程序没有更多选择,只能重新启动事务。

为了避免这种情况,最好是用显示的BEGINEXCLUSIVE语句启动一个可以修改多行数据的大事务。BEGINEXCLUSIVE也许会失败,并返回SQLITE_BUSY,但是应用程序可以简单的重新尝试直到成功。一旦一个独占式的事务成功启动,它就可以完整的访问数据库,消除SQLITE_IOERR_BLOCKED的出现,甚至事务在提交之前就造成了缓冲区溢出(增加数据库缓存会有所帮助)。

SQlite数据库的C编程接口(七)数据库锁定(Database Locking) by斜风细雨QQ:253786989 2012-02-09

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


SQLite架构简单,又有Json计算能力,有时会承担Json文件/RESTful的计算功能,但SQLite不能直接解析Json文件/RESTful,需要用Java代码硬写,或借助第三方类库,最后再拼成insert语句插入数据表,代码非常繁琐,这里就不展示了。参考前面的代码可知,入库的过程比较麻烦,不能只用SQL,还要借助Java或命令行。SPL是现代的数据计算语言,属于简化的面向对象的语言风格,有对象的概念,可以用点号访问属性并进行多步骤计算,但没有继承重载这些内容,不算彻底的面向对象语言。...
使用Python操作内置数据库SQLite以及MySQL数据库。
破解微信数据库密码,用python导出微信聊天记录
(Unity)SQLite 是一个软件库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。SQLite 是在世界上最广泛部署的 SQL 数据库引擎。SQLite 源代码不受版权限制。本教程将告诉您如何使用 SQLite 编程,并让你迅速上手。.................................
安卓开发,利用SQLite实现登陆注册功能
相比大多数数据库而言,具有等优势,广泛应用于、等领域。
有时候,一个项目只有一个数据库,比如只有SQLite,或者MySQL数据库,那么我们只需要使用一个固定的数据库即可。但是一个项目如果写好了,有多个用户使用,但是多个用户使用不同的数据库,这个时候,我们就需要把软件设计成可以连接多个数据库的模式,用什么数据库,就配置什么数据库即可。4.Users实体类,这个实体类要和数据库一样的,形成一一对应的关系。11.Sqlite数据库,需要在代码里面创建数据库,建立表,再建立数据。8.我们开启MySQL数据库,然后进行调试,看程序的结果。2.安装SqlSugar。
基于Android的背单词软件,功能强大完整。
SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统。说白了就是使用起来轻便简单,
Android的简单购物车案例
SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库中。它是D.RichardHipp建立的公有领域项目。它的设计目标是嵌入式的,而且已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了。它能够支持Windows/Linux/Unix等等主流的操作系统,同时能够跟很多程序语言相结合,比如 Tcl、C#、PHP、Java等,还有ODBC接口,同样比起Mysql、PostgreSQL这两款开源的世界著名数据库...
Qt设计较为美观好看的登录注册界面(包含SQLite数据库以及TCP通信的应用)
SQLite是用C语言开发的跨平台小型数据库,可嵌入其他开发语言,也可在单机执行。SPL是用Java开发的跨平台的数据计算语言,可嵌入Java,可在单机执行,可以数据计算服务的形式被远程调用。两者的代码都是解释执行的。...
新建库.openDATA_BASE;新建表createtableLIST_NAME(DATA);语法:NAME关键字...<用逗号分割>删除表droptableNAME;查看表.schema查看表信息新建数据insertintoLIST_NAMEvalues();语法:CLASS,PARAMETER...,CLASS是类别,PARAMETER是参数<用逗号分割新建的
importsqlite3classDemo01:def__init__(self):self.conn=sqlite3.connect("sql_demo_001.db")self.cursor1=self.conn.cursor()self.cursor1.execute("select*fromtable_001wherename=?andid=?",('ssss&#0
 在客户端配置文件<configuration>节点下,添加:<connectionStrings>      <add name="localdb" connectionString="Data Source=config/local.db;Version=3;UseUTF16Encoding=True;" providerName="System.Data.SQLite.SQLiteFactory"/&g
提到锁就不得不说到死锁的问题,而SQLite也可能出现死锁。下面举个例子:连接1:BEGIN(UNLOCKED)连接1:SELECT...(SHARED)连接1:INSERT...(RESERVED)连接2:BEGIN(UNLOCKED)连接2:SELECT...(SHARED)连接1:COMMIT(PENDING,尝试获取EXCLUSIVE锁,但还有SHARED锁未释放,返回SQLITE_BUSY)连接2:INSERT...
SQLite是一种嵌入式数据库,它的数据库就是一个文件。由于SQLite本身是C写的,而且体积很小,所以,经常被集成到各种应用程序中,甚至在iOS和Android的App中都可以集成。Python就内置了SQLite3,所以,在Python中使用SQLite,不需要安装任何东西,直接使用。在使用SQLite前,我们先要搞清楚几个概念:表
设计思想————首先要确定有几个页面、和每个页面的大致布局由于是入门,我也是学习了不是很长的时间,所以项目比较low。。。。第一个页面,也就是打开APP的首页面:今天这个博客,先实现添加功能!:首先对主界面进行布局:其中activity_main.xml的代码为<?xmlversion="1.0"encoding="