我有一个SQLite3表,其中有很多行会尝试使用很多进程。每行只能由一个进程使用。为了确保互斥,我为每个进程分配了一个id,每个DB行都有一列consumed_by
,表示使用它的进程。
因此,我的锁定机制包括启动事务,获取consumed_by
为NULL的行,将列更新为进程的id,以及提交事务。如果两个进程尝试获取同一行,则serializable原则将确保只有其中一个进程可以提交事务。
使用纯sqlite,我设法应用了这个锁定概念,但是对于SQLAlchemy,一些行被多个进程占用。这就是我实施测试的方式:
我有以下课程:
class ToConsume(Base):
__tablename__ = 'to_consume'
id = Column(Integer, primary_key=True)
consumed_by = Column(Integer,nullable=True)
我有以下函数,由100个不同的进程调用:
def consume(id):
log = open("log/"+str(id),"w")
while True:
session = None
try:
session = Session()
consume = session.query(ToConsume).filter_by(consumed_by=None).first()
if consume is None:
break
consume.consumed_by = id
session.commit()
log.write(str(consume.id)+"\n")
log.flush()
except Exception:
if session is not None:
session.rollback()
log.close()
在文件的开头,我通过以下方式初始化SQLAlchemy:
engine = sqlalchemy.create_engine('sqlite:///multithread.db', echo=False,connect_args={'timeout': 30})
def _sqlite_pragmas(dbapi_con, con_record):
dbapi_con.execute('PRAGMA synchronous = 0;')
sqlalchemy.event.listen(engine, 'connect', _sqlite_pragmas)
Base = sqlalchemy.ext.declarative.declarative_base()
Session = sqlalchemy.orm.sessionmaker(bind=engine)
我的数据库以1000行开头。运行100个独立的python进程(我不使用线程),我预计所有日志文件的串联将导致1000行,但我得到超过3000行。
我很难理解为什么在使用SQLAlchemy时我的互斥不起作用。请注意,当我使用纯sqlite时,问题不会发生。
答案 0 :(得分:2)
默认情况下,SQlite未设置为可序列化。此外,Python sqlite3驱动程序中存在错误(有关背景,请参阅http://bugs.python.org/issue9924)。要使用sqlite3 / sqlalchemy进行可序列化隔离,请参阅this document。例如:
from sqlalchemy import create_engine, event
engine = create_engine("sqlite:///myfile.db", isolation_level='SERIALIZABLE')
@event.listens_for(engine, "begin")
def do_begin(conn):
conn.execute("BEGIN")