Bir Discord botu kullanıcı verisi tutmaya başladığı anda discord.py SQLite ikilisi en pratik çözümlerden biri hâline gelir: kurulum gerektirmeyen, tek dosyalık, sunucu istemeyen bir veritabanı. Ancak burada kritik bir tuzak var; standart sqlite3 modülü eşzamanlıdır (blocking) ve her sorguda botun event loop'unu dondurarak tüm komutları yavaşlatır. Çözüm aiosqlite: SQLite'ı async/await dünyasına taşıyan, discord.py 2.x ile mükemmel uyumlu bir kütüphane. Bu yazıda ekonomi/seviye verisini asenkron ve güvenli biçimde saklayan bir altyapı kuruyoruz.
Neden aiosqlite, neden sqlite3 değil?
discord.py tamamen asenkron bir mimari üzerine kuruludur: tek bir thread içinde dönen event loop, gelen mesajları ve etkileşimleri sırayla işler. Standart sqlite3 ile yaptığınız her sorgu bu döngüyü bekletir; veritabanı diske yazarken botunuz ping'lere bile cevap veremez. aiosqlite ise sorguyu arka plandaki bir thread'e devreder ve sonucu await edilebilir bir coroutine olarak döndürür. Pratikteki kazanımlar:
- Bloklamayan G/Ç: Yavaş bir sorgu diğer komutları kilitlemez.
- Doğal söz dizimi:
async withveawaitile discord.py kodunuza birebir oturur. - Bağımsızlık: MySQL/PostgreSQL gibi ayrı bir sunucu kurmaya gerek yoktur; veritabanı tek bir
.dbdosyasıdır.
Kurulum tek satır: pip install aiosqlite. Altında yatan SQLite zaten Python ile birlikte gelir.
Veritabanını bota bağlamak
En temiz yöntem, bağlantıyı bot başlarken setup_hook içinde açıp bot nesnesine bir nitelik olarak iliştirmektir. Böylece tüm cog'lar self.bot.db üzerinden aynı bağlantıyı paylaşır. Bağlantıyı kapanışta da düzgünce kapatırız:
import discord
from discord.ext import commands
import aiosqlite
class MyBot(commands.Bot):
def __init__(self):
intents = discord.Intents.default()
super().__init__(command_prefix="!", intents=intents)
self.db: aiosqlite.Connection | None = None
async def setup_hook(self):
self.db = await aiosqlite.connect("data.db")
# Eşzamanlı okuma/yazmayı iyileştirir
await self.db.execute("PRAGMA journal_mode=WAL;")
await self.init_db()
async def init_db(self):
await self.db.execute("""
CREATE TABLE IF NOT EXISTS users (
user_id INTEGER PRIMARY KEY,
balance INTEGER NOT NULL DEFAULT 0,
xp INTEGER NOT NULL DEFAULT 0
)
""")
await self.db.commit()
async def close(self):
if self.db is not None:
await self.db.close()
await super().close()
PRAGMA journal_mode=WAL (Write-Ahead Logging) okuyucuların yazıcıyı bloklamamasını sağlar; çok kullanıcılı bir botta belirgin fark yaratır. CREATE TABLE IF NOT EXISTS sayesinde tablo her açılışta güvenle hazırlanır.
Veri yazmak: parametreli sorgular
Asla kullanıcı girdisini doğrudan SQL metnine yapıştırmayın; bu SQL injection kapısını açar. Bunun yerine ? yer tutucularını ve bir parametre demetini kullanın. SQLite'ın INSERT ... ON CONFLICT sözdizimi, kaydın olmadığı durumda ekleme, varsa güncelleme (upsert) işini tek sorguda yapar:
async def add_balance(db, user_id: int, amount: int):
await db.execute(
"""
INSERT INTO users (user_id, balance)
VALUES (?, ?)
ON CONFLICT(user_id)
DO UPDATE SET balance = balance + excluded.balance
""",
(user_id, amount),
)
await db.commit()
Kritik nokta: aiosqlite değişiklikleri otomatik kalıcı kılmaz. Her yazma işleminden sonra await db.commit() çağırmazsanız veriler diske işlenmez ve bot yeniden başladığında kaybolur.
Veri okumak: fetchone ve fetchall
Okuma tarafında imleç (cursor) üzerinden ilerlersiniz. async with bloğu imleci otomatik kapatır:
async def get_balance(db, user_id: int) -> int:
async with db.execute(
"SELECT balance FROM users WHERE user_id = ?",
(user_id,),
) as cursor:
row = await cursor.fetchone()
return row[0] if row else 0
Birden çok satır için fetchall kullanın; örneğin bir seviye sıralaması (leaderboard):
async def top_users(db, limit: int = 10):
async with db.execute(
"SELECT user_id, xp FROM users ORDER BY xp DESC LIMIT ?",
(limit,),
) as cursor:
return await cursor.fetchall()
Sonuçlara sütun adıyla erişmek isterseniz db.row_factory = aiosqlite.Row ayarını yapın; o zaman row["balance"] gibi okuyabilirsiniz.
Bir komut içinde kullanmak
Yardımcı fonksiyonları bir cog içindeki komuta bağlamak çok kısadır. self.bot.db her yerden erişilebilir olduğundan, komut yalnızca iş mantığına odaklanır:
from discord.ext import commands
class Economy(commands.Cog):
def __init__(self, bot):
self.bot = bot
@commands.command()
async def daily(self, ctx):
await add_balance(self.bot.db, ctx.author.id, 100)
bal = await get_balance(self.bot.db, ctx.author.id)
await ctx.send(f"Günlük 100 coin alındı. Bakiyen: {bal}")
async def setup(bot):
await bot.add_cog(Economy(bot))
Performans ve güvenlik ipuçları
- Tek bağlantı, paylaşımlı: Her komutta yeni bağlantı açıp kapatmak yerine bot ömrü boyunca tek bağlantı tutun.
- İndeks ekleyin: Sık sorguladığınız sütunlara (örn. sıralama için
xp)CREATE INDEXtanımlamak büyük tablolarda hız kazandırır. - Toplu işlemler: Çok sayıda satırı tek seferde işlerken
executemanykullanıp sonunda tekcommitatın. - Yedekleme: SQLite tek dosyadır; bota dokunmadan dosyayı kopyalamak için
VACUUM INTOya da çevrimiçi yedek API'sini değerlendirin.
SQLite tek sunuculu, orta ölçekli botlar için fazlasıyla yeterlidir. Yüz binlerce kullanıcıya ulaşıp ağır eşzamanlı yazma ihtiyacı doğduğunda PostgreSQL'e geçiş düşünülebilir; ama o noktaya kadar aiosqlite sade ve hızlı bir tercihtir.
Sık Sorulan Sorular
commit çağırmayı unutursam ne olur?
Yazdığınız veriler yalnızca o oturumdaki bağlantı içinde görünür kalır ama diske kalıcı olarak işlenmez. Bot yeniden başladığında son commit'ten sonraki tüm değişiklikler kaybolur. Her INSERT/UPDATE/DELETE sonrası await db.commit() çağırmayı alışkanlık hâline getirin.
Normal sqlite3 modülünü kullanamaz mıyım?
Teknik olarak çalışır, ama her sorgu botun event loop'unu bloklar ve tüm komutları geciktirir. Düşük trafikli bir bot için fark edilmeyebilir; gerçek kullanımda aiosqlite tek doğru tercihtir çünkü asenkron mimariye uyum sağlar.
Birden çok cog aynı veritabanını güvenle kullanabilir mi?
Evet. Bağlantıyı self.bot.db üzerinde paylaştığınız sürece tüm cog'lar aynı tek bağlantıyı kullanır. WAL modu eşzamanlı okumaları rahatlatır; yazmalar SQLite tarafından sıraya alınarak güvenle yürütülür.
Botunuza sağlam bir veri katmanı mı kurmak istiyorsunuz? aiosqlite altyapısı tasarlamak, mevcut sqlite3 kodunuzu asenkrona taşımak ya da ekonomi/seviye sistemi kurmak için benimle iletişime geçin.