SQLAlchemy的Db会话在并行调用时出错
我创建了一个数据库会话,代码如下:
db_engine = create_engine(DB_URL, pool_pre_ping=True, echo=False, pool_size=POOL_SIZE, max_overflow=MAX_OVERFLOW)
Base.metadata.create_all(db_engine)
db_session = sessionmaker(bind=db_engine)()
我在一个Flask路由中使用了这个数据库会话,代码是:
from db_settings import db_session
@application.route('/'):
def my_home():
try:
my_obj = MyObj("dummy_fname", "dummy_lname")
db_session.merge(my_obj)
db_session.commit()
except exc.IntegrityError:
db_session.rollback()
finally:
db_session.close()
我原本以为每次访问这个路由时,数据库会话都是独立的。我尝试连续访问这个路由5次,结果成功在数据库中创建了5条记录,没有任何问题。但是,当我尝试使用线程同时访问这个路由时,只创建了一条记录,其他四次调用都出错了。有人能帮我理解一下这个情况吗?为什么会这样?是因为db_session.close()
影响了其他API调用吗?
import requests
import threading
no_parallel_hits = no_of_sequential_hits = 5
def send_request():
url = 'http://127.0.0.1:5000/'
response = requests.get(url)
print(response.text)
def main_parallel():
threads = []
for _ in range(no_parallel_hits):
thread = threading.Thread(target=send_request)
threads.append(thread)
thread.start()
for thread in threads:
thread.join()
def main_sequential():
for _ in range(no_of_sequential_hits):
send_request()
if __name__ == "__main__":
main_parallel()
# main_sequential()
这是我尝试的结果。main_sequential()调用正常,而main_parallel()却没有按预期工作。
1 个回答
0
在这里,你创建了一个会话生成器,然后生成了一个会话实例。这个实例不应该被同时使用,也不应该放在全局范围内。
# Usually NOT what you want.
db_session = sessionmaker(bind=db_engine)()
更好的做法是这样:
db_session_maker = sessionmaker(bind=db_engine)
然后可以这样创建一个会话:
from db_settings import db_session_maker
@application.route('/'):
def my_home():
db_session = db_session_maker()
try:
my_obj = MyObj("dummy_fname", "dummy_lname")
db_session.add(my_obj)
db_session.commit()
except exc.IntegrityError:
db_session.rollback()
finally:
db_session.close()
更理想的方式是使用某种 Flask 会话管理器,这样可以帮你处理会话的创建和关闭。SQLAlchemy 提到要把会话管理和你的应用代码分开。session-faq-whentocreate
另外,你应该使用 db_session.add
来添加新对象,而不是 db_session.merge
,后者是用来处理不同情况的。