数据库连接池SQLAlchemy中多线程安全的问题
1、数据库模块model.py
from sqlalchemy.orm import scoped_session from sqlalchemy.orm import sessionmaker session_factory = sessionmaker(bind=some_engine) Session = scoped_session(session_factory)
2、业务模块thread.py
import threading from model import Session class User(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True) name = Column(String(20)) fullname = Column(String(20)) password = Column(String(20)) age = Column(Integer) class MyThread(threading.Thread): def __init__(self, threadName): super(MyThread, self).__init__() self.name = threading.current_thread().name def run(self): session = Session() #每个线程都可以直接使用数据库模块定义的Session,执行时,每一个session都相当于一个connection session.query(User).all() user = User(name="hawk-%s"%self.name, fullname="xxxx",password="xxxx",age=10) session.add(user) time.sleep(1) if self.name == "thread-9": session.commit() Session.remove() if __name__ == "__main__": arr = [] for i in xrange(10): arr.append(MyThread('thread-%s' % i)) for i in arr: i.start() for i in arr: i.join()
错误示范:
class MyThread(threading.Thread): def __init__(self, threadName): super(MyThread, self).__init__() self.session = Session() #错误! self.name = threading.current_thread().name def run(self): self.session.query(User).all() user = User(name="hawk-%s"%self.name, fullname="xxxx",password="xxxx",age=10) self.session.add(user) time.sleep(1) if self.name == "thread-9": self.session.commit() Session.remove()
错误解析:
看了SQLAlchemy之后源码发现,Session() 返回的是一个threading.local()对象的成员变量,threading.local()对象只有在线程内部才能实现线程隔离,因此只能放在run()函数里,而不能作为类成员变量。
如果按照错误示例来运行,所有线程其实公用了一个session,没有做到线程隔离,session.commit()操作会互相影响,我们原本只想将thread-9中的数据插入,结果会发现,所有线程中的数据全部被插入。
如何避免使用SQLAlchemy使用连接池
在使用 create_engine创建引擎时,如果默认不指定连接池设置的话,一般情况下,SQLAlchemy会使用一个 QueuePool绑定在新创建的引擎上。并附上合适的连接池参数。
在以默认的方法create_engine时(如下),就会创建一个带连接池的引擎。
engine = create_engine('postgresql://postgres@127.0.0.1/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('postgresql://postgres@127.0.0.1/dbname', poolclass=NullPool) Session = sessionmaker(bind=engine) session = Session() usr_obj_list = session.query(UsrObj).all() print usr_obj_list[0].id session.close()
https://blog.csdn.net/daijiguo/article/details/79486294
https://blog.csdn.net/weiwangchao_/article/details/80185009
参考:
https://www.cnblogs.com/1a2a/p/8278698.html
http://blog.csdn.net/kikaylee/article/details/53232920