Pertama-tama, Anda perlu memutuskan apakah Anda ingin mempertahankan koneksi persisten ke MySQL. Yang terakhir berkinerja lebih baik, tetapi perlu sedikit perawatan.
Default wait_timeout
di MySQL adalah 8 jam. Kapan pun koneksi tidak digunakan lebih lama dari wait_timeout
itu tertutup. Ketika server MySQL di-restart, itu juga menutup semua koneksi yang dibuat. Jadi, jika Anda menggunakan koneksi persisten, Anda perlu memeriksa sebelum menggunakan koneksi apakah koneksi masih hidup (dan jika tidak, sambungkan kembali). Jika Anda menggunakan koneksi per permintaan, Anda tidak perlu mempertahankan status koneksi, karena koneksi selalu baru.
Koneksi per permintaan
Koneksi database non-persisten memiliki overhead yang jelas untuk membuka koneksi, handshaking, dan sebagainya (untuk server database dan klien) per setiap permintaan HTTP yang masuk.
Berikut kutipan dari tutorial resmi Flask tentang koneksi database :
Namun, perhatikan bahwa konteks aplikasi diinisialisasi per permintaan (yang agak terselubung oleh masalah efisiensi dan istilah Flask). Dan dengan demikian, itu masih sangat tidak efisien. Namun itu harus menyelesaikan masalah Anda. Berikut cuplikan yang dilucuti dari apa yang disarankan seperti yang diterapkan ke 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)
Koneksi persisten
Untuk koneksi database koneksi persisten ada dua pilihan utama. Entah Anda memiliki kumpulan koneksi atau memetakan koneksi ke proses pekerja. Karena biasanya aplikasi Flask WSGI dilayani oleh server berulir dengan jumlah utas tetap (misalnya uWSGI), pemetaan utas lebih mudah dan efisien.
Ada sebuah paket, DBUtils
, yang mengimplementasikan keduanya, dan PersistentDB
untuk koneksi yang dipetakan dengan utas.
Satu peringatan penting dalam mempertahankan koneksi yang persisten adalah transaksi. API untuk koneksi ulang adalah ping
. Aman untuk membuat pernyataan tunggal secara otomatis, tetapi dapat mengganggu di antara transaksi (sedikit lebih detail di sini
). DBUtils menangani ini, dan hanya boleh terhubung kembali pada dbapi.OperationalError
dan dbapi.InternalError
(secara default, dikendalikan oleh failures
ke penginisialisasi PersistentDB
) dibangkitkan di luar transaksi.
Berikut tampilan cuplikan di atas dengan 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)
Tolok ukur mikro
Untuk memberikan sedikit petunjuk tentang implikasi kinerja dalam angka, berikut adalah tolok ukur mikro.
Saya berlari:
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
Dan mengujinya dengan konkurensi 1, 4, 8, 16 melalui:
siege -b -t 15s -c 16 http://localhost:5000/?city=london
Pengamatan (untuk konfigurasi lokal saya):
- Koneksi persisten ~30% lebih cepat,
- Pada konkurensi 4 dan yang lebih tinggi, proses pekerja uWSGI mencapai lebih dari 100% penggunaan CPU (
pymysql
harus mengurai protokol MySQL dengan Python murni, yang merupakan hambatannya), - Pada konkurensi 16,
mysqld
Utilisasi CPU adalah ~55% untuk per-permintaan dan ~45% untuk koneksi persisten.