Trước hết, bạn cần quyết định xem bạn có muốn duy trì kết nối liên tục với MySQL hay không. Cái sau hoạt động tốt hơn, nhưng cần bảo trì một chút.
Mặc định wait_timeout
trong MySQL là 8 giờ. Bất cứ khi nào kết nối không hoạt động lâu hơn wait_timeout
đóng cửa rồi. Khi máy chủ MySQL được khởi động lại, nó cũng đóng tất cả các kết nối đã thiết lập. Vì vậy, nếu bạn sử dụng một kết nối liên tục, bạn cần kiểm tra trước khi sử dụng kết nối xem nó còn sống hay không (và nếu không, hãy kết nối lại). Nếu bạn sử dụng kết nối theo yêu cầu, bạn không cần phải duy trì trạng thái kết nối vì kết nối luôn mới.
Theo yêu cầu kết nối
Một kết nối cơ sở dữ liệu không liên tục hiển nhiên có chi phí mở kết nối, bắt tay, v.v. (cho cả máy chủ cơ sở dữ liệu và máy khách) cho mỗi yêu cầu HTTP đến.
Đây là trích dẫn từ hướng dẫn chính thức của Flask về kết nối cơ sở dữ liệu :
Tuy nhiên, lưu ý rằng ngữ cảnh ứng dụng được khởi tạo theo yêu cầu (được che giấu bởi mối quan tâm về hiệu quả và biệt ngữ của Flask). Và do đó, nó vẫn rất kém hiệu quả. Tuy nhiên, nó sẽ giải quyết vấn đề của bạn. Đây là đoạn mã được rút gọn về những gì nó gợi ý được áp dụng cho 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)
Kết nối liên tục
Đối với một kết nối cơ sở dữ liệu kết nối liên tục, có hai tùy chọn chính. Bạn có một nhóm các kết nối hoặc bản đồ kết nối với các quy trình của nhân viên. Bởi vì thông thường các ứng dụng WSGI của Flask được cung cấp bởi các máy chủ luồng với số luồng cố định (ví dụ:uWSGI), nên việc ánh xạ luồng dễ dàng hơn và hiệu quả hơn.
Có một gói, DBUtils
, triển khai cả hai và PersistentDB
cho các kết nối được ánh xạ luồng.
Một lưu ý quan trọng trong việc duy trì một kết nối bền vững là các giao dịch. API để kết nối lại là ping
. Nó an toàn cho việc tự động cam kết các câu lệnh đơn, nhưng nó có thể làm gián đoạn giữa một giao dịch (thêm một chút chi tiết tại đây
). DBUtils sẽ xử lý vấn đề này và chỉ nên kết nối lại trên dbapi.OperationalError
và dbapi.InternalError
(theo mặc định, được kiểm soát bởi failures
để khởi tạo PersistentDB
) được huy động bên ngoài giao dịch.
Đây là cách đoạn mã trên sẽ trông như thế nào với 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)
Điểm chuẩn vi mô
Để cung cấp một chút manh mối về tác động của hiệu suất trong các con số, đây là điểm chuẩn vi mô.
Tôi đã chạy:
-
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
Và thử tải chúng với đồng thời 1, 4, 8, 16 thông qua:
siege -b -t 15s -c 16 http://localhost:5000/?city=london
Quan sát (đối với cấu hình cục bộ của tôi):
- Kết nối liên tục nhanh hơn ~ 30%,
- Trên đồng thời 4 trở lên, quy trình công nhân uWSGI đạt đỉnh hơn 100% mức sử dụng CPU (
pymysql
phải phân tích cú pháp giao thức MySQL bằng Python thuần túy, đây là điểm nghẽn), - Trên đồng thời 16,
mysqld
Hiệu suất sử dụng CPU của là ~ 55% cho mỗi yêu cầu và ~ 45% cho kết nối liên tục.