由于是初创公司,追求快速开发,后台选用python,采用Tornado+SQLAlchemy,数据库是mysql。python之前没接触过,都是现学的,前一周差不多都在边看边学边写的状态,好在python还是特别简单,有其他语言基础很容易上手,只是经常会犯排版对齐、行末忘记“:”的错误。
项目还没上线,只可能写代码的两人去访问数据库,而且频度很低,居然发生了2次数据库连接错误。经过日志分析,都是SQLAlchemy连接池的配置问题。
错误1:
TimeoutError: QueuePool limit of size 5 overflow 10 reached, connection timed out, timeout 30
错误2:
[ERROR] (OperationalError) (2006, 'MySQL server has gone away')

对于错误1,首先确认数据库的最大连接数是足够的,至少比log里达到的10个连接不应该出错。

mysql> show variables like 'max_connections';
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| max_connections | 100   |
+-----------------+-------+
1 row in set (0.01 sec)
再查show global status like '%connect%';也是正常的。可以肯定是SQLAlchemy连接池的配置问题。原来的代码:
engine = create_engine('mysql://'+etc.mysql_user+':'+etc.mysql_passwd+'@'+etc.mysql_host+':'+str(etc.mysql_port)+'/'+etc.host_name+'?charset=utf8',encoding="utf-8", echo=False)
没有设置pool_size的大小,默认为5。加上pool_size=100后,此问题不再出现。

另外在初版代码里图省事,写了个循环调用sql的查询语句,正是这个语句的调用直接导致该错误。把循环去掉,改为只调用SQL一次,增加一个方法获取多件,在之后的代码里循环处理业务逻辑;在调用sql的结束地方,增加finally代码块,明确调用session.close(),让连接资源尽快回收到连接池。之后打算加memcached或者redis,减少DB的IO。代码片段如下:
metadata = MetaData()
Session = sessionmaker() #使用sessionmaker()不需要显示调用session.close()
Session.configure(bind=engine)
'''
...省略
'''
def get_img_url_by_imgid_list( p_img_list ) :
    try :
        session = Session()
        items = session.query( ImgBaseItem ).filter( ImgBaseItem.imgid.in_(p_img_list)).all()
        if not items :
            return None
        res = {}
        for item in items :
            res[ item.imgid ] = item.to_url()
        return res
    except Exception as e :
        log.exp(e)
        return None
    finally :
        session.close()

对于错误2,刚开始没在意,因为发生的时机都是在早上来上班后的第一次访问,后面的sql请求都正常了。到第三天,意识到问题不是偶然的,查看mysql配置。

mysql> show global variables like '%timeout%';
+-----------------------------+----------+
| Variable_name               | Value    |
+-----------------------------+----------+
| connect_timeout             | 10       |
| interactive_timeout         | 28800    |
| wait_timeout                | 28800    |
+-----------------------------+----------+
是默认设置,mysql建立的连接,在8小时内都没有访问请求的话,mysql server将主动断开这条连接,后续在该连接上进行的查询操作都将失败,出现:error 2006 (MySQL server has gone away),到这里问题就清楚了,SQLAlchemy连接池中的连接资源一直没有释放,第二天来上班时,超过了8小时,SQLAlchemy连接池中获取的失效连接去访问数据库服务器导致出错。
修复后代码:
engine = create_engine('mysql://'+etc.mysql_user+':'+etc.mysql_passwd+'@'+etc.mysql_host+':'+str(etc.mysql_port)+'/'+etc.host_name+'?charset=utf8',encoding="utf-8",pool_size=100, pool_recycle=3600, echo=False)
pool_recycle设置为3600(该值必须小于数据库服务器的interactive_timeout),连接池中的空闲连接超过1小时候,自动释放。

在解决问题的过程中,发现还是官方文档最好用,有空还是多上去看看:http://docs.sqlalchemy.org/en/rel_1_0/core/engines.html
关于sqlalchemy数据库的连接配置参数摘要:

Parameters:
  • case_sensitive=True –

    if False, result column names will match in a case-insensitive fashion, that is,row['SomeColumn'].

    Changed in version 0.8: By default, result row names match case-sensitively. In version 0.7 and prior, all matches were case-insensitive.

  • connect_args – a dictionary of options which will be passed directly to the DBAPI’s connect() method as additional keyword arguments. See the example at Custom DBAPI connect() arguments.
  • convert_unicode=False –

    if set to True, sets the default behavior of convert_unicode on theString type to True, regardless of a setting of False on an individualString type, thus causing all String -based columns to accommodate Python unicode objects. This flag is useful as an engine-wide setting when using a DBAPI that does not natively support Python unicode objects and raises an error when one is received (such as pyodbc with FreeTDS).

    See String for further details on what this flag indicates.

  • creator – a callable which returns a DBAPI connection. This creation function will be passed to the underlying connection pool and will be used to create all new database connections. Usage of this function causes connection parameters specified in the URL argument to be bypassed.
  • echo=False – if True, the Engine will log all statements as well as a repr() of their parameter lists to the engines logger, which defaults to sys.stdout. Theecho attribute of Engine can be modified at any time to turn logging on and off. If set to the string "debug", result rows will be printed to the standard output as well. This flag ultimately controls a Python logger; see Configuring Logging for information on how to configure logging directly.
  • echo_pool=False – if True, the connection pool will log all checkouts/checkins to the logging stream, which defaults to sys.stdout. This flag ultimately controls a Python logger; see Configuring Logging for information on how to configure logging directly.
  • encoding –

    Defaults to utf-8. This is the string encoding used by SQLAlchemy for string encode/decode operations which occur within SQLAlchemy, outside of the DBAPI. Most modern DBAPIs feature some degree of direct support for Pythonunicode objects, what you see in Python 2 as a string of the form u'somestring'. For those scenarios where the DBAPI is detected as not supporting a Python unicode object, this encoding is used to determine the source/destination encoding. It is not used for those cases where the DBAPI handles unicode directly.

    To properly configure a system to accommodate Python unicode objects, the DBAPI should be configured to handle unicode to the greatest degree as is appropriate - see the notes on unicode pertaining to the specific target database in use at Dialects.

    Areas where string encoding may need to be accommodated outside of the DBAPI include zero or more of:

    • the values passed to bound parameters, corresponding to the Unicodetype or the String type when convert_unicode is True;
    • the values returned in result set columns corresponding to the Unicodetype or the String type when convert_unicode is True;
    • the string SQL statement passed to the DBAPI’s cursor.execute()method;
    • the string names of the keys in the bound parameter dictionary passed to the DBAPI’s cursor.execute() as well ascursor.setinputsizes() methods;
    • the string column names retrieved from the DBAPI’scursor.description attribute.

    When using Python 3, the DBAPI is required to support all of the above values as Python unicode objects, which in Python 3 are just known as str. In Python 2, the DBAPI does not specify unicode behavior at all, so SQLAlchemy must make decisions for each of the above values on a per-DBAPI basis - implementations are completely inconsistent in their behavior.

  • execution_options – Dictionary execution options which will be applied to all connections. See execution_options()
  • implicit_returning=True – When True, a RETURNING- compatible construct, if available, will be used to fetch newly generated primary key values when a single row INSERT statement is emitted with no existing returning() clause. This applies to those backends which support RETURNING or a compatible construct, including Postgresql, Firebird, Oracle, Microsoft SQL Server. Set this to False to disable the automatic usage of RETURNING.
  • isolation_level –

    this string parameter is interpreted by various dialects in order to affect the transaction isolation level of the database connection. The parameter essentially accepts some subset of these string arguments: "SERIALIZABLE","REPEATABLE_READ""READ_COMMITTED""READ_UNCOMMITTED"and "AUTOCOMMIT". Behavior here varies per backend, and individual dialects should be consulted directly.

    Note that the isolation level can also be set on a per-Connection basis as well, using the Connection.execution_options.isolation_levelfeature.

  • label_length=None – optional integer value which limits the size of dynamically generated column labels to that many characters. If less than 6, labels are generated as “_(counter)”. If None, the value ofdialect.max_identifier_length is used instead.
  • listeners – A list of one or more PoolListener objects which will receive connection pool events.
  • logging_name – String identifier which will be used within the “name” field of logging records generated within the “sqlalchemy.engine” logger. Defaults to a hexstring of the object’s id.
  • max_overflow=10 – the number of connections to allow in connection pool “overflow”, that is connections that can be opened above and beyond the pool_size setting, which defaults to five. this is only used with QueuePool.
  • module=None – reference to a Python module object (the module itself, not its string name). Specifies an alternate DBAPI module to be used by the engine’s dialect. Each sub-dialect references a specific DBAPI which will be imported before first connect. This parameter causes the import to be bypassed, and the given module to be used instead. Can be used for testing of DBAPIs as well as to inject “mock” DBAPI implementations into the Engine.
  • paramstyle=None – The paramstyle to use when rendering bound parameters. This style defaults to the one recommended by the DBAPI itself, which is retrieved from the .paramstyle attribute of the DBAPI. However, most DBAPIs accept more than one paramstyle, and in particular it may be desirable to change a “named” paramstyle into a “positional” one, or vice versa. When this attribute is passed, it should be one of the values "qmark","numeric""named""format" or "pyformat", and should correspond to a parameter style known to be supported by the DBAPI in use.
  • pool=None – an already-constructed instance of Pool, such as aQueuePool instance. If non-None, this pool will be used directly as the underlying connection pool for the engine, bypassing whatever connection parameters are present in the URL argument. For information on constructing connection pools manually, see Connection Pooling.
  • poolclass=None – a Pool subclass, which will be used to create a connection pool instance using the connection parameters given in the URL. Note this differs from pool in that you don’t actually instantiate the pool in this case, you just indicate what type of pool to be used.
  • pool_logging_name – String identifier which will be used within the “name” field of logging records generated within the “sqlalchemy.pool” logger. Defaults to a hexstring of the object’s id.
  • pool_size=5 – the number of connections to keep open inside the connection pool. This used with QueuePool as well as SingletonThreadPool. WithQueuePool, a pool_size setting of 0 indicates no limit; to disable pooling, set poolclass to NullPool instead.
  • pool_recycle=-1 – this setting causes the pool to recycle connections after the given number of seconds has passed. It defaults to -1, or no timeout. For example, setting to 3600 means connections will be recycled after one hour. Note that MySQL in particular will disconnect automatically if no activity is detected on a connection for eight hours (although this is configurable with the MySQLDB connection itself and the server configuration as well).
  • pool_reset_on_return='rollback' –

    set the “reset on return” behavior of the pool, which is whether rollback(),commit(), or nothing is called upon connections being returned to the pool. See the docstring for reset_on_return at Pool.

    New in version 0.7.6.

  • pool_timeout=30 – number of seconds to wait before giving up on getting a connection from the pool. This is only used with QueuePool.
  • strategy='plain' –

    selects alternate engine implementations. Currently available are:

  • executor=None – a function taking arguments (sql, *multiparams,**params), to which the mock strategy will dispatch all statement execution. Used only by strategy='mock'.

Logo

更多推荐