सबसे पहले, आपको यह तय करने की आवश्यकता है कि क्या आप MySQL से लगातार कनेक्शन बनाए रखना चाहते हैं। बाद वाला बेहतर प्रदर्शन करता है, लेकिन थोड़े रखरखाव की जरूरत है।
डिफ़ॉल्ट wait_timeout
MySQL में 8 घंटे है। जब भी कोई कनेक्शन wait_timeout
. से अधिक समय तक निष्क्रिय रहता है यह बंद है। जब MySQL सर्वर को पुनरारंभ किया जाता है, तो यह सभी स्थापित कनेक्शनों को भी बंद कर देता है। इस प्रकार यदि आप एक सतत कनेक्शन का उपयोग करते हैं, तो आपको कनेक्शन का उपयोग करने से पहले जांचना होगा कि क्या यह जीवित है (और यदि नहीं, तो पुनः कनेक्ट करें)। यदि आप प्रति अनुरोध कनेक्शन का उपयोग करते हैं, तो आपको कनेक्शन की स्थिति बनाए रखने की आवश्यकता नहीं है, क्योंकि कनेक्शन हमेशा ताजा होता है।
प्रति अनुरोध कनेक्शन
एक गैर-निरंतर डेटाबेस कनेक्शन में प्रत्येक आने वाले HTTP अनुरोध के अनुसार कनेक्शन, हैंडशेकिंग, और इसी तरह (डेटाबेस सर्वर और क्लाइंट दोनों के लिए) खोलने का स्पष्ट ओवरहेड होता है।
यहां फ्लास्क के आधिकारिक ट्यूटोरियल डेटाबेस कनेक्शन के संबंध में का एक उद्धरण दिया गया है। :
हालांकि, ध्यान दें कि आवेदन संदर्भ प्रति अनुरोध आरंभ किया गया है (जो कि दक्षता संबंधी चिंताओं और फ्लास्क के लिंगो द्वारा छिपा हुआ है)। और इस प्रकार, यह अभी भी बहुत अक्षम है। हालांकि इसे आपकी समस्या का समाधान करना चाहिए। 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)
लगातार कनेक्शन
लगातार कनेक्शन डेटाबेस कनेक्शन के लिए दो प्रमुख विकल्प हैं। या तो आपके पास कार्यकर्ता प्रक्रियाओं के लिए कनेक्शन या मानचित्र कनेक्शन का पूल है। चूँकि सामान्य रूप से फ्लास्क WSGI अनुप्रयोगों को थ्रेडेड सर्वर द्वारा निश्चित संख्या में थ्रेड्स (जैसे uWSGI) के साथ परोसा जाता है, थ्रेड-मैपिंग आसान और कुशल होती है।
एक पैकेज है, DBUtils
, जो दोनों को लागू करता है, और PersistentDB
थ्रेड-मैप किए गए कनेक्शन के लिए।
लगातार कनेक्शन बनाए रखने में एक महत्वपूर्ण चेतावनी लेनदेन है। पुन:कनेक्शन के लिए एपीआई है ping
. यह ऑटो-कमिटिंग सिंगल-स्टेटमेंट के लिए सुरक्षित है, लेकिन लेन-देन के बीच में यह बाधित हो सकता है (थोड़ा और विवरण यहां
) DBUtils इसका ध्यान रखता है, और केवल dbapi.OperationalError
पर फिर से कनेक्ट होना चाहिए और dbapi.InternalError
(डिफ़ॉल्ट रूप से, failures
. द्वारा नियंत्रित किया जाता है PersistentDB
. के प्रारंभकर्ता के लिए ) लेन-देन के बाहर उठाया गया।
यहां बताया गया है कि उपरोक्त स्निपेट 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)
माइक्रो-बेंचमार्क
संख्याओं में प्रदर्शन के निहितार्थ क्या हैं, इसका एक छोटा सा सुराग देने के लिए, यहां माइक्रो-बेंचमार्क है।
मैं भागा:
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
और उन्हें संगामिति 1, 4, 8, 16 के माध्यम से लोड-परीक्षण किया:
siege -b -t 15s -c 16 http://localhost:5000/?city=london
अवलोकन (मेरे स्थानीय विन्यास के लिए):
- एक सतत कनेक्शन ~30% तेज है,
- संगामी 4 और उच्चतर पर, uWSGI कार्यकर्ता प्रक्रिया CPU उपयोग के 100% से अधिक पर पहुंचती है (
pymysql
MySQL प्रोटोकॉल को शुद्ध पायथन में पार्स करना है, जो कि अड़चन है), - संगामी 16 पर,
mysqld
प्रति अनुरोध के लिए का CPU उपयोग ~55% और लगातार कनेक्शन के लिए ~45% है।