sql >> Databáze >  >> RDS >> Mysql

Chyba při používání pymysql v baňce

Nejprve se musíte rozhodnout, zda chcete udržovat trvalé připojení k MySQL. Ten funguje lépe, ale potřebuje trochu údržby.

Výchozí wait_timeout v MySQL je 8 hodin. Kdykoli je připojení nečinné déle než wait_timeout je zavřeno. Při restartování serveru MySQL se také zavře všechna navázaná připojení. Pokud tedy používáte trvalé připojení, musíte před použitím připojení zkontrolovat, zda je aktivní (a pokud ne, znovu se připojte). Pokud používáte připojení na žádost, nemusíte udržovat stav připojení, protože připojení jsou vždy nová.

Připojení na žádost

Netrvalé připojení k databázi má zjevnou režii na otevření připojení, handshaking atd. (pro databázový server i klienta) na každý příchozí požadavek HTTP.

Zde je citát z oficiálního tutoriálu Flask ohledně připojení k databázi :

Všimněte si však, že kontext aplikace je inicializováno na žádost (což je trochu zahaleno obavami o efektivitu a Flaskovým žarlem). A tak je to stále velmi neefektivní. Mělo by to však vyřešit váš problém. Zde je zkrácený úryvek toho, co navrhuje při použití na pymysql :

import pymysql
from flask import Flask, g, request    

app = Flask(__name__)    

def connect_db():
    return pymysql.connect(
        user = 'guest', password = '', database = 'sakila', 
        autocommit = True, charset = 'utf8mb4', 
        cursorclass = pymysql.cursors.DictCursor)

def get_db():
    '''Opens a new database connection per request.'''        
    if not hasattr(g, 'db'):
        g.db = connect_db()
    return g.db    

@app.teardown_appcontext
def close_db(error):
    '''Closes the database connection at the end of request.'''    
    if hasattr(g, 'db'):
        g.db.close()    

@app.route('/')
def hello_world():
    city = request.args.get('city')

    cursor = get_db().cursor()
    cursor.execute('SELECT city_id FROM city WHERE city = %s', city)
    row = cursor.fetchone()

    if row:
      return 'City "{}" is #{:d}'.format(city, row['city_id'])
    else:
      return 'City "{}" not found'.format(city)

Trvalé připojení

Pro trvalé připojení k databázi existují dvě hlavní možnosti. Buď máte fond připojení, nebo mapujete připojení k pracovním procesům. Vzhledem k tomu, že aplikace Flask WSGI jsou normálně obsluhovány vláknovými servery s pevným počtem vláken (např. uWSGI), je mapování vláken jednodušší a stejně efektivní.

Existuje balíček, DBUtils , která implementuje obojí, a PersistentDB pro připojení s mapou vláken.

Jednou z důležitých výhrad při udržování trvalého připojení jsou transakce. API pro opětovné připojení je ping . Je to bezpečné pro automatické zadávání jednotlivých příkazů, ale mezi transakcemi to může rušit (trochu více podrobností zde ). DBUtils se o to postará a měl by se znovu připojit pouze na dbapi.OperationalError a dbapi.InternalError (ve výchozím nastavení řízeno failures k inicializátoru PersistentDB ) vzniklé mimo transakci.

Zde je návod, jak bude výše uvedený úryvek vypadat s PersistentDB .

import pymysql
from flask import Flask, g, request
from DBUtils.PersistentDB import PersistentDB    

app = Flask(__name__)    

def connect_db():
    return PersistentDB(
        creator = pymysql, # the rest keyword arguments belong to pymysql
        user = 'guest', password = '', database = 'sakila', 
        autocommit = True, charset = 'utf8mb4', 
        cursorclass = pymysql.cursors.DictCursor)

def get_db():
    '''Opens a new database connection per app.'''

    if not hasattr(app, 'db'):
        app.db = connect_db()
    return app.db.connection()    

@app.route('/')
def hello_world():
    city = request.args.get('city')

    cursor = get_db().cursor()
    cursor.execute('SELECT city_id FROM city WHERE city = %s', city)
    row = cursor.fetchone()

    if row:
      return 'City "{}" is #{:d}'.format(city, row['city_id'])
    else:
      return 'City "{}" not found'.format(city)

Mikro-benchmark

Abychom vám trochu napověděli, jaké dopady na výkon jsou v číslech, zde je mikro-benchmark.

Běžel jsem:

  • uwsgi --http :5000 --wsgi-file app_persistent.py --callable app --master --processes 1 --threads 16
  • uwsgi --http :5000 --wsgi-file app_per_req.py --callable app --master --processes 1 --threads 16

A otestoval je se souběžností 1, 4, 8, 16 přes:

siege -b -t 15s -c 16 http://localhost:5000/?city=london

Pozorování (pro moji místní konfiguraci):

  1. Trvalé připojení je o ~30 % rychlejší
  2. Na souběžnosti 4 a vyšší má pracovní proces uWSGI vrchol s využitím více než 100 % CPU (pymysql musí analyzovat protokol MySQL v čistém Pythonu, což je úzké hrdlo),
  3. U souběžnosti 16 mysqld Využití CPU je ~55 % pro každý požadavek a ~45 % pro trvalé připojení.


  1. Zařazení do fronty ve zprávách OneWay WCF pomocí Windows Service a SQL Server

  2. jak dotazovat (vyhledat) sql na Amazon Athena, který má hodnotu JSON?

  3. Použití MySQL Galera Cluster Replication k vytvoření geograficky distribuovaného clusteru:Část první

  4. Nejlepší způsob, jak udržet pole TEXT jedinečné v databázi MySQL