本文主要用来探讨如何合理的使用 sqlalchemy
的 session
的一些基础知识
session 可能遇到的问题
- 占用连接未释放,导致数据库连接满
- 已查询的数据使用时返回
None
- 已变更过数据,但是查询时并没有得到最新的更新
session 是什么
它管理与数据库相关的所有操作,包括数据连接、释放、事物相关等操作。
它作为程序访问数据库的一个
entrypoint
它把从数据中取回的数据映射为
ORM
中的一个对象,并且存储在一个名为Identity Map
的数据结构中它在存入数据时把一个ORM对应到一个数据表
session生命周期管理
使用session
的一个基本准则,一个应用应该去管理session
的生命周期,对于处理特殊数据的函数session
应该作为一个参数被传递进来并且使用。
- 对于一个后台任务,建议每个子线程分配一个session,生命周期为子线程生命周期
- 对于命令行任务,建议应用建立一个单独的全局session
- GUI驱动的应用,建议一个用户事件作为session的生命周期
- 对于WEB应用,建议一个request作为生命周期
创建session
- 对于
sessionmaker
一般是作为一个全局变量仅定义一次 - 对于
session
一般是在有数据逻辑操作开始的时候创建
使用session
在session中存储了一个对象的弱引用
- 每条数据只保留一个副本,使用主键识别一条数据。如果查询时该数据已经存在session中,查询后将不会替换该数据,除非该数据已过期。
- 查一条数据的最新状态时先commit再query
- session的细节,事物和异常管理尽量与程序的细节分开处理
commit&&close
- 在
commit
或者rollback
前每个session
代表一个事物, 在commit
后session
中的数据会被标记为过期,但是不会被删除。应保证每个事物尽量短,避免持有数据锁时间过长影响数据库并发量。 - 关闭
session
后所有的资源都会被释放,前面说了session中的ORM
都是弱引用,查询所得的数据都可能消失。 - 保证任务结束后关闭
session
防止资源泄漏,毕竟一个应用一般也就分配几百个连接,使用完后就么有了。
session使用例子
from contextlib import contextmanager
@contextmanager
def session_scope():
"""Provide a transactional scope around a series of operations."""
session = Session()
try:
yield session
session.commit()
except:
session.rollback()
raise
finally:
session.close()
def run_my_program():
with session_scope() as session:
ThingOne().go(session)
ThingTwo().go(session)