我有非常简单的代码导致我的MySQL数据库挂起:
import sqlalchemy as sa
from sqlalchemy import orm
# creating the engine, the base, etc
import utils
import config
utils.base_init(config)
Base = config.Base
class Parent(Base):
__tablename__ = 'Parents'
id = sa.Column(sa.Integer, primary_key=True)
children = orm.relationship('Child', backref='parent')
class Child(Base):
id = sa.Column(sa.Integer, primary_key=True)
parent_id = sa.Column(sa.Integer)
__tablename__ = 'Children'
__table_args__ = (sa.ForeignKeyConstraint(
['parent_id'],
['Parents.id'],
onupdate='CASCADE', ondelete='CASCADE'),{})
Base.metadata.create_all()
session = orm.sessionmaker(bind=config.Base.metadata.bind)()
p = Parent(id=1)
c1 = Child(id=1)
c2 = Child(id=2)
session.add(p)
session.add(c1)
session.add(c2)
session.commit()
# Works
# Base.metadata.drop_all()
c1.parent
# 2012-08-17 20:16:21,459 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
# 2012-08-17 20:16:21,460 INFO sqlalchemy.engine.base.Engine SELECT `Children`.id AS `Children_id`, `Children`.parent_id AS `Children_parent_id`
# FROM `Children`
# WHERE `Children`.id = %s
# 2012-08-17 20:16:21,460 INFO sqlalchemy.engine.base.Engine (1,)
Base.metadata.drop_all()
# hangs until i kill the connection above.
# server status: 'Waiting for table metadata lock'
看起来SQL Alchemy在发出加载关系属性所需的select查询后没有释放元数据锁?我怎么能让它发布呢?我甚至不明白为什么select语句需要首先锁定表!
当然,我可以通过关闭会话来获得这段特定的代码,但这在我的实际程序中是不实际的。
答案 0 :(得分:2)
您需要在.drop_all()
来电之前开始新的交易; MySQL看到你从这个事务中的表中读取,并锁定表而不被删除:
session.commit()
Base.metadata.drop_all()
提交交易会隐式开始新交易。
MySQL保证事务隔离;您的事务将读取一致的数据,并且在您开始新事务之前不会看到其他事务所提交的更改。然而,DROP TABLE
语句使MySQL无法保留这些保证,因此表被锁定。
或者,您可以更改事务隔离级别,告诉MySQL您不关心隔离保证。由于会话连接是合并的,因此只能对所有连接进行,或者根本不进行;使用isolation_level
参数create_engine()
:
engine = create_engine(
'mysql://username:passwd@localhost/databasename',
isolation_level='READ UNCOMMITTED')
有关每个隔离级别的详细信息,请参阅SET TRANSACTION
documentation。