1. 程式人生 > >MariaDB的線程及連接

MariaDB的線程及連接

pin any 超時 inter not found sla errors format word

簡介:

相對於最新的MySQL5.6,MariaDB在性能、功能、管理、NoSQL擴展方面包含了更豐富的特性。比如微秒的支持、線程池、子查詢優化、組提交、進度報告等。

本文就主要探索MariaDB當中連接池的一些特性,配置。來配合我們的sqlalchemy。

一:起因

本來是不會寫這個東西的,但是,寫好了python--flask程序,使用sqlalchemy+mariadb,部署以後總是出問題,500錯誤之類的。

錯誤提示是:

raise errors.OperationalError("MySQL Connection not available.")
sqlalchemy.exc.OperationalError: (mysql.connector.errors.OperationalError)

MySQL Connection not available.
[SQL: ‘SELECT ******* \nFROM user‘]
[parameters: [{}]]
(Background on this error at: http://sqlalche.me/e/e3q8)

http://sqlalche.me/e/e3q8:

OperationalError:

Exception raised for errors that are related to the database’s operation andnot necessarily under the control of the programmer, e.g. an unexpecteddisconnect occurs, the data source name is not found, a transaction could notbe processed, a memory allocation error occurred during processing, etc.

This error is aDBAPI Errorand originates fromthe database driver (DBAPI), not SQLAlchemy itself.

TheOperationalErroris the most common (but not the only) error class usedby drivers in the context of the database connection being dropped, or notbeing able to connect to the database. For tips on how to deal with this, seethe sectionDealing with Disconnects

.

意思是沒有正確斷開和數據庫的連接。

二:處理斷開

http://docs.sqlalchemy.org/en/latest/core/pooling.html#pool-disconnects

官方給了三種方案來解決這個問題:

1.悲觀處理

engine = create_engine("mysql+pymysql://user:pw@host/db", pool_pre_ping=True)

pool_pre_ping=True

表示每次連接從池中檢查,如果有錯誤,監測為斷開的狀態,連接將被立即回收。

2.自定義悲觀的ping

from sqlalchemy import exc
from sqlalchemy import event
from sqlalchemy import select

some_engine = create_engine(...)

@event.listens_for(some_engine, "engine_connect")
def ping_connection(connection, branch):
    if branch:
        # "branch" refers to a sub-connection of a connection,
        # we don‘t want to bother pinging on these.
        return

    # turn off "close with result".  This flag is only used with
    # "connectionless" execution, otherwise will be False in any case
    save_should_close_with_result = connection.should_close_with_result
    connection.should_close_with_result = False

    try:
        # run a SELECT 1.   use a core select() so that
        # the SELECT of a scalar value without a table is
        # appropriately formatted for the backend
        connection.scalar(select([1]))
    except exc.DBAPIError as err:
        # catch SQLAlchemy‘s DBAPIError, which is a wrapper
        # for the DBAPI‘s exception.  It includes a .connection_invalidated
        # attribute which specifies if this connection is a "disconnect"
        # condition, which is based on inspection of the original exception
        # by the dialect in use.
        if err.connection_invalidated:
            # run the same SELECT again - the connection will re-validate
            # itself and establish a new connection.  The disconnect detection
            # here also causes the whole connection pool to be invalidated
            # so that all stale connections are discarded.
            connection.scalar(select([1]))
        else:
            raise
    finally:
        # restore "close with result"
        connection.should_close_with_result = save_should_close_with_result

說實話,沒怎麽看明白。

像是try一個select 語句,如果沒問題就關閉。

3.樂觀處理

from sqlalchemy import create_engine, exc
e = create_engine(...)
c = e.connect()

try:
    # suppose the database has been restarted.
    c.execute("SELECT * FROM table")
    c.close()
except exc.DBAPIError, e:
    # an exception is raised, Connection is invalidated.
    if e.connection_invalidated:
        print("Connection was invalidated!")

# after the invalidate event, a new connection
# starts with a new Pool
c = e.connect()
c.execute("SELECT * FROM table")

這個看懂了,try一個select語句,如果無效,就返回Connection was invalidated!,然後開一個新的連接,再去執行select。這個應該寫個裝飾器,放在每個查詢前面。

4.使用連接池回收

from sqlalchemy import create_engine
e = create_engine("mysql://scott:tiger@localhost/test", pool_recycle=3600)

這種方式就比較簡單了,在連接參數中寫上連接超時時間即可。

5.這是自己看文檔找到的方法

from sqlalchemy.pool import QueuePool,NullPool,AssertionPool,StaticPool,SingletonThreadPool,Pool

在sqlalchemy.pool下有已經配置好的連接池,直接使用這些連接池也應該可以。

三:測試

docker run --restart=always --privileged --name My_mariadb_01 -p 3301:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mariadb:10.2.13
docker run --restart=always --privileged --name My_mariadb_02 -p 3302:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mariadb:10.2.13
docker run --restart=always --privileged --name My_mariadb_03 -p 3303:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mariadb:10.2.13
docker run --restart=always --privileged --name My_mariadb_04 -p 3304:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mariadb:10.2.13
docker run --restart=always --privileged --name My_mariadb_05 -p 3305:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mariadb:10.2.13

為避免因數據庫交叉連接,首先開啟5個MARIADB

Flask_Plan_01 8801 engine = create_engine(‘mysql+mysqlconnector://plan:plan@mysql/plan‘,)
Flask_Plan_02 8802 engine = create_engine(‘mysql+mysqlconnector://plan:plan@mysql/plan‘, pool_pre_ping=True)
Flask_Plan_03 8803 engine = create_engine(‘mysql+mysqlconnector://plan:plan@mysql/plan‘, poolclass=QueuePool)
Flask_Plan_04 8804 engine = create_engine(‘mysql+mysqlconnector://plan:plan@mysql/plan‘, poolclass=NullPool)
Flask_Plan_05 8805 engine = create_engine(‘mysql+mysqlconnector://plan:plan@mysql/plan‘, pool_recycle=3600)

用這5種連接參數進行連接測試。

如果你願意,也可以繼續開,QueuePool,NullPool,AssertionPool,StaticPool,SingletonThreadPool,Pool,把這幾種都測試一下。

四:

五:

六:

七:

八:

九:

十:

MariaDB的線程及連接