我正在使用SQLite数据库开发多线程应用程序。我已经进行了一些研究,似乎SqliteQueueDatabase可以提供所需的并发处理。我已经仔细阅读了文档,但似乎还没有完全了解如何启动和启动数据库。
from peewee import *
from playhouse.sqliteq import SqliteQueueDatabase
db = SqliteQueueDatabase(':memory:')
class Prime(Model):
num = IntegerField()
class Meta:
database = db
db.start()
db.connect()
db.create_tables([Prime])
print db.get_tables() # prints []
db.stop()
在上述示例中启动数据库后,我尝试为我的模型创建表,但未创建该表。我想念什么?我试图找到一个涵盖整个生命周期但无法实现的peewee&SqliteQueueDatabase示例。
答案 0 :(得分:0)
您正在使用内存数据库。内存数据库使用每个线程的连接。因此,除非您专门使用共享内存模式(必须查阅sqlite文档),否则您将很不走运。
使用基于文件的数据库,您的示例将运行正常。
答案 1 :(得分:0)
我遇到了这个问题,似乎读查询已在create_tables()
之前完成。
我的解决方法是引发背对背db.stop()
db.start()
的呼叫。强制执行代码,直到所有数据库写查询完成。
from peewee import *
from playhouse.sqliteq import SqliteQueueDatabase
db = SqliteQueueDatabase('db.sqlite3')
class Prime(Model):
num = IntegerField()
class Meta:
database = db
db.start()
db.connect()
db.create_tables([Prime])
db.stop() # Wait for create_tables() to complete
db.start() # Continue execution
print db.get_tables()
db.stop()
答案 2 :(得分:0)
我最终要做的是,除了sqlite3和线程之外,我没有使用peewee,SqliteQueueDatabase或任何ORM。
使用一种单例技巧,我基本上拥有一个具有连接属性的对象的实例,因此所有线程都共享一个连接实例。连接到sqlite时必须设置check_same_thread=False
,否则不同的线程无法共享相同的连接。
以下是简化版本:
import sqlite3
import threading
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
class Dao(object, metaclass=Singleton):
def __init__(self, conf=None):
self.lock = threading.Lock()
self.conn = sqlite3.connect(
conf.db,
check_same_thread=False
)
我第一次在主线程中实例化Dao
类并将配置传递给__init__
。后来,无论哪个线程需要使用数据库,它都只会创建一个Dao
对象。但是,由于使用Singleton技巧,呼叫者仅获得了对已存在实例的引用,该实例还包括已建立的连接。
我将所有数据库操作作为方法添加到此Dao
类中。为了避免重复锁定,我使用wraps
创建了装饰器。
from functools import wraps
def transaction_read_write(fn):
@wraps(fn)
def wrapper(self, *args, **kwargs):
self.lock.acquire()
# execute wrapped method and perform commit
try:
ret = fn(self, *args, **kwargs)
self.conn.commit()
except Exception as e:
# perform rollback in case of an error
# also in real world application do some logging here
self.conn.rollback()
raise e
finally:
# release acquired lock
self.lock.release()
return ret
return wrapper
我对只读操作有一个类似的包装,但是没有提交/回滚。我将其配置为在只读操作期间是否执行锁定-以便能够在生产中发生数据库并发问题的情况下无需新版本就调整锁定行为。
现在我要做的就是将自定义事务装饰器添加到Dao
方法中。 (当然,在现实生活中,事务可以由多个SQL命令组成。因此,我有一些没有事务注释的原子方法,它们从来没有从外部直接调用过。它们仅由执行一些任务的其他Dao
方法调用在单个事务中调用,因此这些复杂的方法具有事务注释。我非常小心这些事务的大小和速度,因为在我的情况下,锁定机制基本上阻止了其他线程同时与DB一起工作。) / p>
因此Dao
方法可能类似于:
@transaction_read_write
def set_processed_files(self, id, num_files):
cur = self.conn.cursor()
cur.execute("UPDATE jobs SET num_files = ? WHERE job_id = ?", (num_files, id))
最后,我决定不使用peewee,但我希望示例中有用。