簡體   English   中英

SQLAlchemy 池不關閉數據庫連接

[英]SQLAlchemy with pooling not closing database connections

我發現 SQLAlchemy 沒有釋放數據庫連接(在我的情況下),所以這堆積到可能導致服務器崩潰的地步。 連接由不同的線程制成。

這是簡化的代碼

"""
Test to see DB connection allocation size while making call from multiple threads
"""

from time import sleep
from threading import Thread, current_thread
import uuid

from sqlalchemy import func, or_, desc
from sqlalchemy import event
from sqlalchemy import ForeignKey, Column, Integer, String, DateTime, UniqueConstraint
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm import scoped_session
from sqlalchemy.orm import relationship
from sqlalchemy.orm import scoped_session, Session
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.types import Integer, DateTime, String, Boolean, Text, Float
from sqlalchemy.engine import Engine
from sqlalchemy.pool import NullPool

# MySQL
SQLALCHEMY_DATABASE = 'mysql'
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://amalgam:amalgam@localhost/amalgam?charset=utf8mb4' # https://stackoverflow.com/questions/47419943/pymysql-warning-1366-incorrect-string-value-xf0-x9f-x98-x8d-t
SQLALCHEMY_ECHO = False
SQLALCHEMY_ENGINE_OPTIONS = {'pool_size': 40, 'max_overflow': 0}
SQLALCHEMY_ISOLATION_LEVEL = "AUTOCOMMIT"

# DB Engine

# engine = create_engine(SQLALCHEMY_DATABASE_URI, echo=SQLALCHEMY_ECHO, pool_recycle=3600,
#                        isolation_level= SQLALCHEMY_ISOLATION_LEVEL,
#                        **SQLALCHEMY_ENGINE_OPTIONS
#                        ) #  Connect to server

engine = create_engine(SQLALCHEMY_DATABASE_URI, 
                        echo=SQLALCHEMY_ECHO, 
                        # poolclass=NullPool,
                        pool_recycle=3600,
                       isolation_level= SQLALCHEMY_ISOLATION_LEVEL,
                       **SQLALCHEMY_ENGINE_OPTIONS
                       ) #  Connect to server


session_factory = sessionmaker(bind=engine)
Base = declarative_base()

# ORM Entity
class User(Base):

    LEVEL_NORMAL = 'normal'
    LEVEL_ADMIN = 'admin'

    __tablename__ = "users"
    id = Column(Integer, primary_key=True)    
    name = Column(String(100), nullable=True)
    email = Column(String(100), nullable=True, unique=True)
    password = Column(String(100), nullable=True)
    level = Column(String(100), default=LEVEL_NORMAL)


# Workers
NO = 10
workers = []

_scoped_session_factory = scoped_session(session_factory)


def job(job_id):
    session = _scoped_session_factory()

    print("Job is {}".format(job_id))

    user = User(name='User {} {}'.format(job_id, uuid.uuid4()), email='who cares {} {}'.format(job_id, uuid.uuid4()))

    session.add(user)
    session.commit()
    session.close()

    print("Job {} done".format(job_id))
    sleep(10)
    
# Create worker threads
for i in range(NO):
    workers.append(Thread(target=job, kwargs={'job_id':i}))

# Start them
for worker in workers:
    worker.start()

# Join them
for worker in workers:
    worker.join()

# Allow some time to see MySQL's "show processlist;" command
sleep(10)

程序到達的那一刻

sleep(10)

我運行

show processlist;

它給出以下結果 - 這意味着與數據庫的所有連接仍然存在。

在此處輸入圖像描述

如何強制關閉這些連接?

注意:我可以利用

poolclass=NullPool

但我覺得該解決方案過於嚴格——我希望仍然可以訪問數據庫池,但能夠在需要時以某種方式關閉連接

以下來自QueuePool構造函數的簽名

pool_size – 要維護的池的大小,默認為 5。這是將永久保留在池中的最大連接數。 請注意,池開始時沒有連接; 一旦請求此數量的連接,該數量的連接將保持不變。 pool_size 可以設置為 0 表示沒有大小限制; 要禁用池,請改用NullPool

max_overflow – 池的最大溢出大小。 當簽出的連接數達到 pool_size 中設置的大小時,將返回額外的連接,直到此限制。 當這些額外的連接返回到池中時,它們將被斷開並丟棄。 因此,池將允許的同時連接總數為 pool_size + max_overflow,池將允許的“休眠”連接總數為 pool_size。 max_overflow 可以設置為 -1 表示沒有溢出限制; 不會限制並發連接的總數。 默認為 10。

SQLALCHEMY_ENGINE_OPTIONS = {'pool_size': 40, 'max_overflow': 0}

鑒於上述情況,此配置要求SQLAlchemy 保持最多 40 個連接打開。

如果你不喜歡這樣,但想保持一些可用的連接,你可以嘗試這樣的配置:

SQLALCHEMY_ENGINE_OPTIONS = {'pool_size': 10, 'max_overflow': 30}

這將在池中保留 10 個持久連接,並且如果同時請求,將突發多達 40 個連接。 任何超出配置的池大小的連接在被檢回池后立即關閉。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM