如何处理SQLAlchemy的数据库表锁定策略?
有人能解释一下吗?我该如何避免应用程序在处理时卡住,比如说我有一个实体列表,并且可以跳转到详细页面。
我打开列表时,启动了一个sqlalchemy会话,然后我打开一个详细页面,再打开另一个,接着又一个,结果应用程序就卡住了,因为一个会话在阻塞另一个会话。
我不能在整个应用程序中只用一个会话,因为那样我就无法通过检查session.dirty、新建和删除属性来判断表单是否被编辑,这样应用程序的状态处理就会变得非常复杂,代码也会变得难以理解。
我需要实现其他类型的会话处理策略吗?
我需要调整sqlalchemy的映射或sql服务器吗?
这里是一个最小的工作示例:
from sqlalchemy import MetaData, Table, Column, FetchedValue, ForeignKey, create_engine
from sqlalchemy.types import BigInteger, String
from sqlalchemy.orm import mapper, relationship, sessionmaker, Session
class Ref(object):
id = None
name = None
id_parent = None
class TableMapper(object):
def __init__(self, metadata, mapped_type):
self._table = None
self._mapped_type = mapped_type
def get_table(self):
return self._table
def set_table(self, table):
assert isinstance(table, Table)
self._table = table
class RefTableMapper(TableMapper):
def __init__(self, metadata):
TableMapper.__init__(self, metadata, Ref)
self.set_table(Table('Ref', metadata,
Column('id', BigInteger,
primary_key = True, nullable = False),
Column('name', String),
Column('id_parent', BigInteger,
ForeignKey('Ref.id'))
))
def map_table(self):
r_parent = relationship(Ref,
uselist = False,
remote_side = [self._table.c.id],
primaryjoin = (
self._table.c.id_parent == self._table.c.id))
mapper(Ref, self._table,
properties = {'parent': r_parent})
return self._table
class Mapper(object):
def __init__(self, url, echo = False):
self._engine = create_engine(url, echo = echo)
self._metadata = MetaData(self._engine)
self._Session = sessionmaker(bind = self._engine, autoflush = False)
ref_t = RefTableMapper(self._metadata).map_table()
def create_session(self):
return self._Session()
if __name__ == '__main__':
mapp = Mapper(r'mssql://username:pwd@Server\SQLEXPRESS/DBName', True)
s = mapp.create_session()
rr = s.query(Ref).all()
s1 = mapp.create_session()
merged = s1.merge(rr)
merged.flush()
s2 = mapp.create_session()
rr1 = s2.query(Ref).all() #application freezes!
1 个回答
2
SQL Server默认的隔离模式会非常严格地锁定整个表。这意味着在进行某些操作时,整个表都会被锁住,其他操作就不能进行。比如,上面的例子可能是你在一个事务中执行了更新操作,然后在另一个事务中执行了查询操作,而第一个事务还没有完成。不过,session.merge()不接受列表,而且上面没有说明表的内容,所以很难判断具体情况。
总的来说,通常的做法是启用多版本并发控制(SQL Server称之为“行版本控制”),这样就可以合理地锁定单独的行,而不是整个表,这样可以提高效率:
ALTER DATABASE MyDatabase SET ALLOW_SNAPSHOT_ISOLATION ON
ALTER DATABASE MyDatabase SET READ_COMMITTED_SNAPSHOT ON
关于这个的详细信息可以在这里找到:http://msdn.microsoft.com/en-us/library/ms175095.aspx。