sqlite3是一个非常简单的本地数据库,以磁盘文件作为基础。正是因为简单,所以在多进程多线程情况下,需要用户自行决定相应的逻辑。有两种方式来操作数据库:同步、异步。

同步

多进程多线程情况下,如果多个进程或多个线程同时对数据库进行写操作,容易出现一个进程或线程写完数据库之后,另一个持有旧状态的进程或线程又对数据库进行了一次写操作,极易造成数据库损坏。

解决以上问题的方法主要有锁机制,通过对临界区域加锁,来避免多个进程或线程同时对数据库进行操作。

进程锁:python的multiprocess提供了Lock()方法,可以在同一个任务中多个进程之间进行同步。但是,如果涉及到不同任务对同一个文件进行读写,就需要其他的方式来对进程进行同步。这里介绍一种可以用于不同任务之间的文件锁:fcntl。

fcntl有两种不同的加锁方式flock和lockf,flock和fcntl都是系统调用,而lockf是库函数,也是fcntl的封装,因此lockf和fcntl在底层实现是一样的,对文件加锁的效果也是一样的。

几个概念:

文件锁:对整个文件进行加锁

记录锁:对整个文件以及文件的部分字节进行加锁,如fcntl和lockf

排它锁:也可以称为写锁、独占锁,同一时间只有一个进程可以加锁

共享锁:也可以称为读锁,支持多个进行并发的读文件内容,但是不可以写

睡眠锁:如果进程请求的锁被其他进程所持有,则该进行会进行休眠,直到满足条件被唤醒,如semaphore、mutex等。

自旋锁:如果进程请求的锁被其他进程所持有,则该进行不休眠,循环等待,直到满足条件。自旋锁不应被长时间持有。

劝告锁:不要求进程一定遵守规则,可以强制操作,如flock和fcntl

强制锁:是内核行为,在操作系统调用违反约束条件的时候,内核将直接阻拦,如fcntl。

flock

flock(fd, operation)

fd是文件描述符,operation的可选项有:

LOCK_SH: 共享锁

LOCK_EX: 排它锁

LOCK_UN: 解锁

LOCK_NB: 非阻塞(与上述三种操作一起使用)

flock和lockf的第一个区别是flock只能对整个文件进行加锁,而不能对文件的某一部分加锁,lockf可以对文件的某个区域进行上锁。

第二个区别是flock只能产生劝告锁,flock可以有共享锁和排它锁,而lockf只支持排他锁。

第三个区别是使用fork/dup时的情况(flock锁可以递归,通过dup或者fork产生两个fd,都可以进行加锁而不会死锁)

第四个区别是flock不能在NFS文件系统上使用,要在NFS上使用fcntl

lockf和fcntl

fcntl(fd, arg)

fcntl/lockf的特性有:

上锁可以递归

加读锁(共享锁)必须读是打开的,加写锁(排它锁)文件必须是写打开的

有fork产生的子进程不继承父进程所设置的锁

支持强制性锁:对一个特定文件打开其设置组ID位(S_ISGID),并关闭其组执行位(S_IXGRP),则对该文件开启了强制性锁机制,在linux中如果要使用强制性锁,则要在文件系统mount时,使用-o mand打开该机制。

线程锁:对于同一个进程中的多个线程来说,进行同步操作相对来说比较容易,因为这些线程共享进程内的所有资源,则可以使用一个统一的全局变量来当做锁,当然,也可以使用对应编程语言提供的锁,比如python的threading.Lock()

异步

同步操作虽然可以保证数据的一直性,但是却降低了效率,不适合高并发的场景。

以写文件为例,在同步模式下,每个进程都需要等待数据写入到磁盘上才能返回,而写磁盘的操作是比较耗时的,这就造成进程大部分时间都在等待写磁盘这个操作或者其他的I/O操作。而在异步模式下,通过事先定义一个缓冲区,进程并不直接和磁盘进行交互,而是将数据放到缓冲区,然后继续执行其他的指令,由操作系统来保证将缓冲区的数据写入到磁盘。这样的话,效率就会大大的提高。

异步场景下,对splite3数据库进行操作可以采用生产者消费者模式,只不过有多个生产者和一个消费者,通过一个消息队列或者缓冲区来连接生产者和消费者。在写数据库场景下,生产者为发送写指令的进程或者线程,消费者指的是实际对数据库进行写操作的进程或者线程。

当生产者进程或线程需要向数据库写数据的时候,并不直接操作数据库,而是把要写的数据放到消息队里或者缓冲区,消费者进程或线程读取消息队列或者缓冲区,如果队列或者缓冲区为空,则阻塞,否则将取出来的数据写入到数据库。

采用异步模式可以提高程序的执行效率,避免程序耗费大量的时间等待I/O。但是异步场景下,需要注意保证数据的一致性。比如在使用缓冲区的情况下,如果出现突发情况,缓冲区的数据就有可能会丢失。

 

相关阅读

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。