专栏首页工作笔记精华SQLAlchemy session 使用问题

SQLAlchemy session 使用问题

SQLAlchemy session 使用问题

在更改 SQLAlchemy Session 从每次请求都创建到共享同一个 Session 之后遇到了如下问题:

StatementError: (sqlalchemy.exc.InvalidRequestError) Can’t reconnect until invalid transaction is rolled back [SQL: ]

或者是

raised unexpected: OperationalError(“(_mysql_exceptions.OperationalError) (2006, ‘MySQL server has gone away’)”,)

错误是 SQLAlchemy 抛出。原因是你从 pool 拿的 connection 没有以 session.commit 或 session.rollback 或者 session.close 放回 pool 里。这时 connection 的 transaction 没有完结(rollback or commit)。 而不知什么原因(recyle 了,timeout 了)你的 connection 又死掉了,你的 sqlalchemy 尝试重新连接。由于 transaction 还没完结,无法重连。

正确用法是确保 session 在使用完成后用 session.close, session.commit 或者 session.rollback 把连接还回 pool。

SQLAlchemy 数据库连接池使用

sessions 和 connections 不是相同的东西, session 使用连接来操作数据库,一旦任务完成 session 会将数据库 connection 交还给 pool。

在使用 create_engine 创建引擎时,如果默认不指定连接池设置的话,一般情况下,SQLAlchemy 会使用一个 QueuePool 绑定在新创建的引擎上。并附上合适的连接池参数。

在以默认的方法 create_engine 时(如下),就会创建一个带连接池的引擎。

engine = create_engine('mysql+mysqldb://root:password@127.0.0.1:3306/dbname')

在这种情况下,当你使用了 session 后就算显式地调用 session.close(),也不能把连接关闭。连接会由 QueuePool 连接池进行管理并复用。

这种特性在一般情况下并不会有问题,不过当数据库服务器因为一些原因进行了重启的话。最初保持的数据库连接就失效了。随后进行的 session.query() 等方法就会抛出异常导致程序出错。

如果想禁用 SQLAlchemy 提供的数据库连接池,只需要在调用 create_engine 是指定连接池为 NullPool,SQLAlchemy 就会在执行 session.close() 后立刻断开数据库连接。当然,如果 session 对象被析构但是没有被调用 session.close(),则数据库连接不会被断开,直到程序终止。

下面的代码就可以避免 SQLAlchemy 使用连接池:

#!/usr/bin/env python
#-*- coding: utf-8 -*-

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.pool import NullPool

engine = create_engine('mysql+mysqldb://root:password@127.0.0.1:3306/dbname', poolclass=NullPool)
Session = sessionmaker(bind=engine)
session = Session()
usr_obj_list = session.query(UsrObj).all()
print usr_obj_list[0].id
session.close()

create_engine() 函数和连接池相关的参数有:

  • -pool_recycle, 默认为 -1, 推荐设置为 7200, 即如果 connection 空闲了 7200 秒,自动重新获取,以防止 connection 被 db server 关闭。
  • -pool_size=5, 连接数大小,默认为 5,正式环境该数值太小,需根据实际情况调大
  • -max_overflow=10, 超出 pool_size 后可允许的最大连接数,默认为 10, 这 10 个连接在使用过后,不放在 pool 中,而是被真正关闭的。
  • -pool_timeout=30, 获取连接的超时阈值,默认为 30 秒

直接只用 create_engine 时,就会创建一个带连接池的引擎

engine = create_engine('postgresql://postgres@127.0.0.1/dbname')

当使用 session 后就显示地调用 session.close(),也不能把连接关闭,连接由 QueuePool 连接池管理并复用。

引发问题

当数据库重启,最初保持的连接就会失败,随后进行 session.query() 就会失败抛出异常 mysql 数据 ,interactive_timeout 等参数处理连接的空闲时间超过(配置时间),断开

何时定义 session,何时提交,何时关闭

基本

  • 通常来说,将 session 的生命周期和访问操作数据库的方法对象隔离和独立。
  • 确保 transaction 有非常清晰的开始和结束,保持 transaction 简短,也就意味着让 transaction 能在一系列操作之后终止,而不是一直开放着。 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()

是否线程安全

Session 不是为了线程安全而设计的,因此确保只在同一个线程中使用。

如果实际上有多个线程参与同一任务,那么您考虑在这些线程之间共享 Session 及其对象;但是在这种极不寻常的情况下,应用程序需要确保实现正确的 locking scheme,以便不会同时访问 Session 或其状态。处理这种情况的一种更常见的方法是为每个并发线程维护一个 Session,而是将对象从一个 Session 复制到另一个 Session,通常使用 Session.merge() 方法将对象的状态复制到本地的新对象中。

scoped session

想要线程安全时使用 scoped_session() ,文档解释

the scoped_session() function is provided which produces a thread-managed registry of Session objects. It is commonly used in web applications so that a single global variable can be used to safely represent transactional sessions with sets of objects, localized to a single thread.

using transactional=False is one solution, but a better one is to simply rollback(), commit(), or close() the Session when operations are complete - transactional mode (which is called “autocommit=False” in 0.5) has the advantage that a series of select operations will all share the same isolated transactional context..this can be more or less important depending on the isolation mode in effect and the kind of application.

DBAPI has no implicit “autocommit” mode so there is always a transaction implicitly in progress when queries are made.

This would be a fairly late answer. This is what happens: While using the session, a sqlalchemy Error is raised (anything which would also throw an error when be used as pure SQL: syntax errors, unique constraints, key collisions etc.).

You would have to find this error, wrap it into a try/except-block and perform a session.rollback().

After this you can reinstate your session.

flush 和 commit 区别

  • flush 预提交,等于提交到数据库内存,还未写入数据库文件;
  • commit 就是把内存里面的东西直接写入,可以提供查询了;

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • spring-data-redis中JedisCluster不支持pipelined问题解决

    摘要: 引言 了解Jedis的童鞋可能清楚,Jedis中JedisCluster是不支持pipeline操作的,如果使用了redis集群,在spring-boo...

    stys35
  • Vue Cli 3 搭建一个可按需引入组件的组件库架子

    babel-plugin-component 就是 Element UI 用来实现组件按需加载的一个 babel 插件。我们把它用到我们的组件库上,就不需要重新...

    stys35
  • Call exception, tries=10, retries=35, started=38348 ms ago, cancelled=false, msg=pc-node1 row

    stys35
  • JSP Session管理

      本篇讲述JSP中session的相关知识和管理方法;   先说说cookie与session   在web中常用的两种用户信息管理方式:cookie 和 s...

    用户1154259
  • XSS利用之延长Session生命周期

    1.0 话题准备 —— session 和 cookie 的原理 —— ▌1.1 session介绍 ---- 简单介绍:PHP session 变量用于存储...

    FB客服
  • php session_decode函数用法讲解

    session_decode() 对 data 参数中的已经序列化的会话数据进行解码, 并且使用解码后的数据填充 _SESSION 超级全局变量。

    砸漏
  • PHP实现提高SESSION响应速度的几种方法详解

    本文实例讲述了PHP实现提高SESSION响应速度的几种方法。分享给大家供大家参考,具体如下:

    砸漏
  • spring-boot-2.0.3不一样系列之番外篇 - 自定义session管理,绝对有值得你看的地方

      还记得当初写spring-session实现分布式集群session的共享的时候,里面有说到利用filter和HttpServletRequestWrapp...

  • 对laravel的session获取与存取方法详解

    以上这篇对laravel的session获取与存取方法详解就是小编分享给大家的全部内容了,希望能给大家一个参考。

    砸漏
  • PHP7创建销毁session的实例方法

    session可以存储用户会话中的变量,用来更改用户的会话设置,并且可以在应用程序中的所有页面使用。下面我们就来介绍下PHP7中创建与销毁session的方法,...

    砸漏

扫码关注云+社区

领取腾讯云代金券