MySQL Connector/Python'u Üretim İçin Yapılandırmak
Selamlar, bu yazımda MySQL Connector/Python'u local'de oyuncak gibi çalıştırmaktan çıkarıp, üretimde gece 3'te uyandırmayacak hale nasıl getiriyoruz, ona bakacağız. Konu kuru görünüyor ama açıkçası bir bağlantının nasıl açıldığı, nasıl kapandığı ve charset'in ne olduğu - işin %80'i burada bitiyor. Hadi başlayalım.
Connector/Python neden bu kadar konuşuluyor?
mysql-connector-python, Oracle'ın resmi sürücüsü. İki ana alternatifi var: C üzerine kurulu mysqlclient (en hızlısı) ve saf Python yazılmış PyMySQL (en taşınabilir olanı). Bence resmi sürücüyü tercih etmenin en büyük sebebi MySQL'in yeni özelliklerine ilk önce burada destek gelmesi. Performans canınızı sıkıyorsa mysqlclient'a geçersiniz, ama 'hangisi daha hızlı?' tartışmasına girmeden önce çoğu projede darboğazın sürücü olmadığını söyleyeyim.
Kurulum kısmı klasik:
# Resmi MySQL surucusu
pip install mysql-connector-python
# C uzantili, daha hizli alternatif
pip install mysqlclient
# Saf Python alternatif
pip install PyMySQL
Temel bağlantı: charset'i unutmayın
İlk kuralım: utf8 yazan her yere ikna olmadan bakın. MySQL'in efsane tuzaklarından biri, utf8 aliasının aslında 3-byte'lık eski bir kodlama olmasıdır. Emoji'ler, bazı CJK karakterler, hatta düz Türkçe metnin bazı kombinasyonları orada patlar. utf8mb4 istiyorsunuz, başka bir şey değil.
import mysql.connector
conn = mysql.connector.connect(
host='db.example.com',
port=3306,
database='myapp',
user='app_user',
password='app_password',
charset='utf8mb4',
collation='utf8mb4_unicode_ci',
use_unicode=True,
time_zone='+00:00',
connection_timeout=10,
autocommit=False,
)
time_zone='+00:00' da önemli. Sunucunuz UTC olsun, uygulamada lokalize edin; aksi halde 'cron 03:00'da çalışıyor ama veritabanı 06:00 yazıyor' tarzı saatlik debug tuzaklarına düşersiniz.
Bağlantı havuzu - üretimde tek başına bağlantı açmayın
Her isteğin TCP el sıkışmasını, kimlik doğrulamasını ve charset müzakeresini baştan yapması üretimde kabul edilemez. Havuz şart:
import mysql.connector.pooling
pool = mysql.connector.pooling.MySQLConnectionPool(
pool_name='myapp_pool',
pool_size=20,
pool_reset_session=True,
host='db.example.com',
port=3306,
database='myapp',
user='app_user',
password='app_password',
charset='utf8mb4',
collation='utf8mb4_unicode_ci',
use_unicode=True,
time_zone='+00:00',
connection_timeout=10,
autocommit=False,
)
def get_orders(user_id: int):
conn = pool.get_connection()
try:
cursor = conn.cursor(dictionary=True)
cursor.execute(
'SELECT id, total, status FROM orders WHERE user_id = %s',
(user_id,),
)
return cursor.fetchall()
finally:
cursor.close()
conn.close() # Baglantiyi havuza geri verir
Buradaki tek kritik nokta: conn.close() aslında bağlantıyı kapatmıyor, havuza geri veriyor. try/finally bloğunu atlamayın; aksi halde havuzunuz birkaç dakika içinde tükenir ve uygulamanız kilitlenir.
SSL: opsiyonel değil
Veritabanınız aynı sunucuda olmadıkça SSL pazarlık konusu değil. Sertifikalarla:
conn = mysql.connector.connect(
host='db.example.com',
database='myapp',
user='app_user',
password='app_password',
ssl_ca='/etc/ssl/mysql/ca.pem',
ssl_cert='/etc/ssl/mysql/client-cert.pem',
ssl_key='/etc/ssl/mysql/client-key.pem',
ssl_verify_cert=True,
ssl_verify_identity=True,
)
ssl_verify_identity=True'yu açmazsanız ortadaki adam saldırısına karşı kapı yarı açık kalır. Açın.
SQLAlchemy ile daha temiz bir yol
Birden fazla servis, birden fazla query, birden fazla migration - elle havuz yönetmek bir yerden sonra yorucu. SQLAlchemy'nin QueuePool'u tam olarak bu iş için var:
from sqlalchemy import create_engine, text
from sqlalchemy.pool import QueuePool
import os
engine = create_engine(
f'mysql+mysqlconnector://{os.environ["DB_USER"]}:{os.environ["DB_PASSWORD"]}'
f'@{os.environ["DB_HOST"]}:3306/{os.environ["DB_NAME"]}',
poolclass=QueuePool,
pool_size=20,
max_overflow=10,
pool_timeout=30,
pool_recycle=1800,
pool_pre_ping=True,
connect_args={
'charset': 'utf8mb4',
'time_zone': '+00:00',
'connection_timeout': 10,
},
)
pool_pre_ping=True benim için pazarlıksız. MySQL sunucusu bağlantıyı wait_timeout sonrası sessizce düşürür; ping olmadan bunu ancak ilk sorgunuz Lost connection ile patladığında öğrenirsiniz. pool_recycle=1800 ise 30 dakikayı geçen bağlantıları proaktif olarak yeniler.
Sık karşılaşılan hatalar
utf8yazıp bitti sanmak:utf8mb4olmazsa emoji ve bazı diakritikler kaybolur. Hem bağlantıda hem tablo seviyesinde tutarlı olmalı.- Havuzdan aldığın bağlantıyı
closeetmemek: Havuz tükenir,PoolError: Failed getting connectiongörürsünüz.finallybloğu zorunlu. autocommit=Trueile transaction beklemek: Birden fazlaINSERT'i atomik sanırsanız, biri patladığında geriye dönüş yok. AçıkçaFalseyapın,commit/rollback'i siz yönetin.- Deadlock'u retry'lamamak: MySQL
1213koduyla normal işleyişin parçası olarak deadlock döndürür. Yakalayın,rollbackyapın, kısa bir bekleyişle tekrar deneyin.
Kapanış
Üretimdeki MySQL bağlantınız aslında üç şeyden ibaret: doğru charset, sağlıklı bir havuz ve şifreli bir kanal. Bence buradaki hiçbir ayarı 'sonra bakarım' diye ertelememek lazım, çünkü hepsi production'da bir kere ısırınca ders veren konular. Umarım faydalı olur, görüşmek üzere.
