V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
Raul7
V2EX  ›  Python

害 关于 Flask-SQLAlchemy"数据库连接不可用的“问题困扰了好久,再详细发一下,求教如何解决

  •  
  •   Raul7 · 2019-11-24 17:46:32 +08:00 · 3820 次点击
    这是一个创建于 1822 天前的主题,其中的信息可能已经有所发展或是发生改变。

    首先,介绍下我的项目:Flask+Flask-SQLAlchemy,相关配置如下:

    # config.py
    MYSQL_URL = 'mysql+mysqlconnector://xxx/db_name'
    DEBUG = True
    SQLALCHEMY_ECHO = True
    SQLALCHEMY_DATABASE_URI = MYSQL_URL
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    SQLALCHEMY_POOL_RECYCLE = 3000
    SQLALCHEMY_ENCODING = "utf8mb4"
    
    # application.py
    from flask_sqlalchemy import SQLAlchemy
    db = SQLAlchemy()
    ......
    self.config.from_pyfile('config.py')
    db.init_app(self)
    
    

    我现在的功能是:前面的功能一切正常,数据库增删改查也正常,随后进入一个扫描过程,是一个第三方的扫描工具,扫描完成后生成一串数据量很大的扫描结果( json 串),然后我循环这个 json 串,拿出里面的 ip ( ip 数量很多),然后在循环里根据 ip 查询其 id,就这个过程报异常:

    (mysql.connector.errors.OperationalError) MySQL Connection not available
    

    大概代码如下:

     for result in nminfo['scan'].values():
     	if result['status']['state'] == "up":
     		ip = result['addresses']['ipv4']
            host_os = "xxx"
    	# 位置 1
            host_assets_id = db.session.query(HostAsset.host_id).filter(HostAsset.host_ip == ip).first()
            if host_assets_id:
                query_host_id = host_assets_id[0]
                # 位置 2
                HostAsset.query.filter_by(host_id=query_host_id).update({'host_os': host_os})
                HostAssetsHistory.query.filter_by(host_id=query_host_id,                     task_stream_id=self.task_stream_id).update({'host_os': host_os})
                db.session.commit()
            else:
                pass
    

    这个扫描过程,当这个扫描数据比较少的时候,扫描完成的时间就比较短。经过测试,扫描时间短的时候,不会报异常,程序正常跑完。但是当这个扫描的数据量大的时候,待扫描完成,查询数据库就报如上异常。

    很奇怪,感觉是不是:扫描时间长,扫描过程与数据库没有交互,自动与数据库断开连接了?

    我的 mysql 相关配置如下图:

    MOwU1S.png

    15 条回复    2019-11-29 11:15:10 +08:00
    CallMeReznov
        1
    CallMeReznov  
       2019-11-24 21:48:56 +08:00
    你的 close 呢?
    Raul7
        2
    Raul7  
    OP
       2019-11-24 22:57:52 +08:00
    @CallMeReznov 没有 close Flask-SQLAlchemy 不是自动 close 的吗?
    vZexc0m
        3
    vZexc0m  
       2019-11-25 09:07:30 +08:00
    试试把 ip 全部循环之后用 IN 进行查询呢!条件允许的话可以添加上索引。
    Eds1995
        4
    Eds1995  
       2019-11-25 09:43:54 +08:00
    加这个参数
    SQLALCHEMY_ENGINE_OPTIONS = {
    "pool_pre_ping": True,
    "pool_recycle": 300,
    }
    Eds1995
        5
    Eds1995  
       2019-11-25 09:45:04 +08:00
    增加 idle_timeout 时间
    lbfeng
        6
    lbfeng  
       2019-11-25 09:55:09 +08:00
    neoblackcap
        7
    neoblackcap  
       2019-11-25 10:03:32 +08:00
    request 对象泄露了被,没有被正确析构,所以导致数据库连接没有正确返回连接池
    Raul7
        8
    Raul7  
    OP
       2019-11-25 10:48:24 +08:00
    @neoblackcap 老哥 你说的这个是什么意思?如何解决呢?
    Raul7
        9
    Raul7  
    OP
       2019-11-25 10:49:16 +08:00
    @lbfeng 是的 问题类似 但是网上的方法我都试了下 都不行
    Latin
        10
    Latin  
       2019-11-25 11:29:51 +08:00
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    SQLALCHEMY_RECORD_QUERIES = True
    SQLALCHEMY_POOL_SIZE = 1024
    SQLALCHEMY_POOL_TIMEOUT = 90
    SQLALCHEMY_POOL_RECYCLE = 3
    SQLALCHEMY_MAX_OVERFLOW = 1024
    这个配置服务跑了一年也没挂过 自己看下文档调下配置
    neoblackcap
        12
    neoblackcap  
       2019-11-25 14:35:56 +08:00
    @Raul7 你资源泄露了,Flask-SQLAlchemy 将 scoped_session 绑在 request 对象上,request 对象的生命周期结束了,那么就会调用对应的钩子函数,对 scoped_session 对象进行析构。那么数据库连接自然就会返回连接池。
    你会出现这样的情况,要不就是 request 对象没有被正确结束,比如抛异常了,没有处理。或者你进行的任务实在是太长了,你的 request 对象一直没有析构(比如你在 celery 里面用了,但是 session 却又是用 request 绑定的)

    你可以自己先排查一遍看看,反正绝大多数情况都应该是资源泄露引起的。
    lolizeppelin
        13
    lolizeppelin  
       2019-11-25 17:33:30 +08:00
    在 pool 代码里加日志

    从 pool 里取出 connection 的时候打印一条
    还回去的时候打印一条

    这不就知道哪次没还回去了么?

    打日志打日志打日志
    Raul7
        14
    Raul7  
    OP
       2019-11-25 18:09:11 +08:00
    @neoblackcap @lolizeppelin get 谢谢老哥们
    我今天在扫描过程结束以后,db.session.close()了一下,然后程序正常了。哭了
    linlance
        15
    linlance  
       2019-11-29 11:15:10 +08:00
    哎,我都放弃 Flask 了,太多小插件需要自己弄了,简洁优美,但是弄完了,最后也快变成个 Django 了,耦合度还高。。
    我转 Django 了,有个个人项目,开发了两年了,还没第一版出来。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5165 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 09:30 · PVG 17:30 · LAX 01:30 · JFK 04:30
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.