• 数据库连接池SQLAlchemy中多线程安全问题


    数据库连接池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

     

  • 相关阅读:
    PyQt(Python+Qt)学习随笔:窗口的布局设置及访问
    PyQt(Python+Qt)学习随笔:QAbstractItemView的showDropIndicator属性
    PyQt学习随笔:QStandardItemModel使用注意事项
    Windows 2008 R2 防火墙允许Serv-U通过的方法
    实例化php类的时候如何传参
    密码强度检测
    php和c++socket通讯(基于字节流,二进制)
    PHP 魔术方法__set() __get() 方法详解
    type='button'和'submit'的区别
    jQuery实现CheckBox全选、全不选
  • 原文地址:https://www.cnblogs.com/leijiangtao/p/4454126.html
Copyright © 2020-2023  润新知