SQLite3写数据库时的锁机制
SQLite3 写数据库
为了写Sqlite3数据库,进程必须先获取SHARED锁。当获取SHARED锁之后,进程需要进⼀步申请RESERVED锁。RESERVED锁表⽰该进程会在不远的将来执⾏写数据库操作。同⼀时刻只有⼀个进程能够获取RESERVED锁。但是其他进程此时还是可以获取SHARED锁来读取数据库中的内容最美的老师
专业技能怎么写
当写数据库的进程试图RESERVED锁未遂,意味着此时有另⼀个进程已经获取了RESERVERD锁。在这种情况下,写数据库将会失败,并返回SQLITE_BUSY错误。
获取RESERVERED锁之后,写数据库进程将先创建⼀个rollback journal。journal的头部被初始化成Sqlite数据库⽂件的原始⼤⼩。同时该头部也为master journal保留了空间,尽管该master-journal初始值为空。
我熟悉的人300字作文在对DB的任何⼀个page(数据是以page的形式组织的)执⾏更新之前,进程需要将该page的原始内容写到rollback journal中。被改变的page起初被保存在内存中,并没有写到disk上。原始的数据库还是处于未被修改的状态,换句话说,其它进程还是能继续读取原始的数据。
最后,写数据的进程准备真正更新数据库了。更新的时机在于memory cache满了需要换出,或者是进程
已经准备好commit这次update。在更新发⽣之前,写数据库的进程必须确保没有其他进程正在读数据库,同时rollback journal的数据也已经安全保存到磁盘上,以确保在更新失败时,rollback能够正确进⾏。以下将是更新数据库的具体步骤:
1. 确保所有的rollback journal都被写到disk上(⽽不是OS的FS中或者disk controller的cache中),以防⽌突然掉电重启后,数据依然可以恢复。
2. 获取PENDING锁,再进⼀步获取该数据库⽂件上的EXCLUSIVE(排他)锁。如果此时该数据库⽂件上有SHARED锁(被其他进程所占⽤),写进程必须等带直到获取EXCLUSIVE(排它)锁。
3. 将Memory中所有修改过的page写到数据库⽂件中。
防诈骗宣传标语如果由于memory cache满了导致的写数据库操作,写进程⾃⼰并不知道,也不会⽴即commit。相反,写进程可能会继续对内存中
的page作更新操作。在后续的更新被写到数据库⽂件之前,rollback journal必须先被flush到disk上。同时也要注意,先前因为memory cache满⽽写数据库时获取的EXCLUSIVE(排他)锁,将会⼀直保持到所有的更新都被commit。换句话说,⼀旦memory cache满了,并且第⼀次成功写⼊数据库后,没有任何其他进程可以访问数据库。(排它锁⼀旦获取,就不会降级,直到所有的更新被提交给数据库)
竹子的英语
当⼀个写进程准备commit更新时,它将执⾏⼀下操作:
4. 获取该数据库⽂件上的排他锁,并确保所有memory中的更新都正确写到disk上。其逻辑由上述1-3步骤描述
5. 将所有该数据库⽂件上的更新flush到disk上。(同步过程,数据真正写到磁盘表⾯才会返回)
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 databa file(怎么确定主数据⽂件,没有交代)"名称后加⼀个随机数后缀,知道该⽂件名没有被使⽤过为⽌)该master-journal中记录的是各个DB的journal⽂件名称。(同样,此时要保证master-journal被正确flush到disk上)
6. 将master-journal的名称写到相关的各个journal⽂件中(在创建rollback journal的时候,为master journal预留了空间)。同时flush各
春联对联大全
个journal⽂件到disk上(是指真正的磁盘表⾯)。
7. 将数据库⽂件们的更新flush到磁盘表⾯。
灰色裤子搭配8. 提交transaction同时,删除master-journal⽂件。同理如果在删除之前,出现掉电,crash等情况,参考上个ssion的第6步骤
9. 删除各个DB⽂件相关的journal⽂件
10. 释放相关数据库⽂件上的EXCLUSIVE锁和PENDING锁
写饥饿
在Sqlite2中,如果有很多进程读数据库,很有可能每时每刻都有进程在读DB。也就是说每时每刻都有进程占据DB的SHARED锁,写进程将没有机会获取EXCLUSIVE锁,即导致所谓的写饥饿。
金门战役Sqlite 3通过使⽤PENDING锁来解决写饥饿的问题。PENDING锁允许当前正在读DB的进程完成读操作,但是不允许新的读操作。因此,当⼀个进程准备写DB时,它将先申请PENDING锁,阻断后续的读操作。⽽当当前正在读的所有进程完成读取操作后,SHARED锁都被释放,从⽽写进程就可以获取EXCLUSIVE锁,进⽽可以对数据库进⾏写操作。