SQLite3 写数据库时的锁机制

zz/2024/7/17 20:20:04

转载:http://blog.csdn.net/woshinia/article/details/9063411

SQLite3 写数据库

 

    为了写Sqlite3数据库,进程必须先获取SHARED锁。当获取SHARED锁之后,进程需要进一步申请RESERVED锁。RESERVED锁表示该进程会在不远的将来执行写数据库操作。同一时刻只有一个进程能够获取RESERVED锁。但是其他进程此时还是可以获取SHARED锁来读取数据库中的内容

 

    当写数据库的进程试图RESERVED锁未遂,意味着此时有另一个进程已经获取了RESERVERD锁。在这种情况下,写数据库将会失败,并返回SQLITE_BUSY错误。

 

    获取RESERVERED锁之后,写数据库进程将先创建一个rollback journaljournal的头部被初始化成Sqlite数据库文件的原始大小。同时该头部也为master journal保留了空间,尽管该master-journal初始值为空。

 

    在对DB的任何一个page(数据是以page的形式组织的)执行更新之前,进程需要将该page的原始内容写到rollback journal中。被改变的page起初被保存在内存中,并没有写到disk上。原始的数据库还是处于未被修改的状态,换句话说,其它进程还是能继续读取原始的数据。

 

    最后,写数据的进程准备真正更新数据库了。更新的时机在于memory cache满了需要换出,或者是进程已经准备好commit这次update。在更新发生之前,写数据库的进程必须确保没有其他进程正在读数据库,同时rollback journal的数据也已经安全保存到磁盘上,以确保在更新失败时,rollback能够正确进行。以下将是更新数据库的具体步骤:

    1. 确保所有的rollback journal都被写到disk上(而不是OSFS中或者disk controllercache中),以防止突然掉电重启后,数据依然可以恢复。

    2. 获取PENDING锁,再进一步获取该数据库文件上的EXCLUSIVE(排他)锁。如果此时该数据库文件上有SHARED锁(被其他进程所占用),写进程必须等带直到获取EXCLUSIVE(排它)锁。

    3. Memory中所有修改过的page写到数据库文件中。

   

    如果由于memory cache满了导致的写数据库操作,写进程自己并不知道,也不会立即commit。相反,写进程可能会继续对内存中的page作更新操作。在后续的更新被写到数据库文件之前,rollback journal必须先被flushdisk上。同时也要注意,先前因为memory cache满而写数据库时获取的EXCLUSIVE(排他)锁,将会一直保持到所有的更新都被commit。换句话说,一旦memory cache满了,并且第一次成功写入数据库后,没有任何其他进程可以访问数据库。(排它锁一旦获取,就不会降级,直到所有的更新被提交给数据库)

 

    当一个写进程准备commit更新时,它将执行一下操作:   

    4. 获取该数据库文件上的排他锁,并确保所有memory中的更新都正确写到disk上。其逻辑由上述1-3步骤描述

    5. 将所有该数据库文件上的更新flushdisk上。(同步过程,数据真正写到磁盘表面才会返回)

    6. 删除journal文件(如果PRAGMA journal mode被设置成TRUNCATE或者PERSIST时,则相应地Truncate journal文件或者清空journal文件的头部)。这些操作是在更新被提交时立即执行的。在删除journal文件之前,如果出现掉电或者crash的情况,当下一个进程打开数据库文件时,将会发现该数据库文件有一个hot journal文件残留,并会根据该journal文件回滚所有的更新。当journal文件被删除后,将不会存在所谓的hot journal文件,所有的更新即被持久化下来。

    7. 释放该数据库文件上的EXCLUSIVE锁和PENDING

   

    一旦PENDING锁被释放,其他进程就可以读取该数据库文件中的内容。在当前(Sqlite3)的实现中,RESERVED锁也会一同被释放,虽然无伤大雅。将来的Sqlite可能会

提供"CHECH POINT" SQL命令,可以用来在一个transaction内,提交到当前位置的所有变化,同时保持RESERVED锁。这样后续的更新就能确保不会被其他的写进程抢占。

 

    如果一个transaction涉及到多个DB文件,提交的逻辑将会更加复杂(1-3步骤是一样的,从第4步开始有所变化):

    4. 确保所有的数据库文件都获取到了排它锁以及正确的journal文件。

    5. 创建一个master-journal文件,该master-journal文件的名称为arbitray。(目前的实现是在"主数据库文件——main database file(怎么确定主数据文件,没有交代)"名称后加一个随机数后缀,知道该文件名没有被使用过为止)该master-journal中记录的是各个DBjournal文件名称。(同样,此时要保证master-journal被正确flushdisk上)

    6. master-journal的名称写到相关的各个journal文件中(在创建rollback journal的时候,为master journal预留了空间)。同时flush各个journal文件到disk上(是指真正的磁盘表面)。

    7. 将数据库文件们的更新flush到磁盘表面。

    8. 提交transaction同时,删除master-journal文件。同理如果在删除之前,出现掉电,crash等情况,参考上个session的第6步骤

    9. 删除各个DB文件相关的journal文件

    10. 释放相关数据库文件上的EXCLUSIVE锁和PENDING

   

写饥饿

 

    Sqlite2中,如果有很多进程读数据库,很有可能每时每刻都有进程在读DB。也就是说每时每刻都有进程占据DBSHARED锁,写进程将没有机会获取EXCLUSIVE锁,即导致所谓的写饥饿。

 

    Sqlite 3通过使用PENDING锁来解决写饥饿的问题。PENDING锁允许当前正在读DB的进程完成读操作,但是不允许新的读操作。因此,当一个进程准备写DB时,它将先申请PENDING锁,阻断后续的读操作。而当当前正在读的所有进程完成读取操作后,SHARED锁都被释放,从而写进程就可以获取EXCLUSIVE锁,进而可以对数据库进行写操作。


http://www.ngui.cc/zz/2762675.html

相关文章

tar 命令参数

tar -c: 建立压缩档案 -x:解压 -t:查看内容 -r:向压缩归档文件末尾追加文件 -u:更新原压缩包中的文件 这五个是独立的命令,压缩解压都要用到其中一个,可以和别的命令连用但只能用其中一个。下面的参数是根…

jenkins 阿里云托管集群创建

参考文档 - https://help.aliyun.com/document_detail/106712.html?spm5176.console_devops2020.help.dexternal.756d84778tw1gJ 部署 阿里云应用里就我们选择ack 安装,具体操作验证阿里云参考文档里都有,但是通过此方式安装安装完成后则是通过loadbal…

2005英语一text1

Everybody loves a fat pay rise. Yet pleasure at your own can vanish if you learn that a colleague has been given a bigger one. Indeed, if he has a reputation for slacking, you might even be outraged. Such behaviour is regarded as “all too human,” with th…

利用/*+Ordered*/提高查询性能

消耗在准备利用Oracle执行计划机制提高查询性能新的SQL语句的时间是Oracle SQL语句执行时间的最重要的组成部分。但是通过理解Oracle内部产生执行计划的机制,你能够控制Oracle花费在评估连接顺序的时间数量,并且能在大体上提高查询性能。 准备执行SQL语句…

正则表达式 学习手册 二

2.5. 修饰匹配次数的特殊符号 前面章节中讲到的表达式,无论是只能匹配一种字符的表达式,还是可以匹配多种字符其中任意一个的表达式,都只能匹配一次。如果使用表达式再加上修饰匹配次数的特殊符号,那么不用重复书写表达式就可以…

在Eclipse中使用TODO来记录待办任务

在开发过程中,可能会有一些任务需要做,但不能马上做,所以需要记录下来,以防忘记。为了解决这个问题,很多IDE都提供了“To do list“的功能。 比如,在Eclipse的java /javaWeb工程中的任意文件任意处&#x…

页面加载时就运行javascript函数

页面加载时就运行javascript函数 function Resolution() { Xscreen.width;Yscreen.height;Resolution X/Y; } 我要在页面加载时就运行上面这个javascript函数&#xff0c;有什么办法么? 在<body>内这样写:<body οnlοad"Resolution();">.

百度地图--根据经纬度定位

根据经纬度定位 <html> <head> <meta http-equiv"Content-Type" content"text/html; charsetutf-8" /> <meta name"viewport" content"initial-scale1.0, user-scalableno" /> <style type"text/cs…

在jquery的ajax中添加自定义的header信息

因为种种原因&#xff0c;自己的框架中要传系统约定好的header信息&#xff0c;页面使用的jquery的ajax请求&#xff0c;找了好久&#xff0c;突然发现我们可以在beforeSend方法中设置&#xff0c;这个方法接受一个参数&#xff0c;就代表了发起异步请求的XMLHttpRequest对象&a…

IntelliJ IDEA的使用(二)

在使用InelliJ IDEA的过程中&#xff0c;通过查找资料以及一些自己的摸索&#xff0c;发现这个众多Java程序员喜欢的IDE里有许多值得一提的小窍门&#xff0c;如果能熟练的将它们应用于实际开发过程中&#xff0c;相信它会大大节省你的开发时间&#xff0c;而且随之而来的还会有…