YENİ 📢 Sınırlı lansman teklifi: Tüm oyunlarda ilk aya %70'e varan indirim!Paketleri görün

aiogram botunda MySQL veritabanı nasıl kullanılır (aiomysql)

Telegram botlarıUpdated Nov 6, 202610 min read

Kısa yanıt. aiogram 3 botunuza aiomysql ekleyin, polling başlamadan önce autocommit=True ile tek bir havuz açın ve dp.start_polling(bot, pool=pool) ile handler’larınıza verin: aiogram onu pool parametresi olan her handler’a iletir. %s yer tutucularını kullanın ve Telegram kimliklerini BIGINT olarak saklayın, INT olarak değil. PlayNHost’ta her bot planı bir MySQL veritabanı içerir: bunu Veritabanları sayfasında oluşturun, bilgilerini Ortam bölmesine yapıştırın, aşağıdaki kod olduğu gibi çalışır. Veritabanı, PlayNHost sunucularınızdan ve phpMyAdmin’den gelen bağlantıları kabul eder, kendi bilgisayarınızdan gelenleri değil. Bu kodu 6 Kasım 2026’da aiogram 3.31.0, aiomysql 0.3.2 ve Python 3.12 ile, bizimki gibi ayarlanmış bir veritabanı hesabıyla (20 bağlantı, 120 saniyelik sorgu sınırı) MariaDB 10.11 üzerinde ve Telegram’ın Bot API sunucusunun yerine geçen bir taklitle test ettik: 8 komut girdi, 8 yanıt çıktı, notlar veritabanına kaydedildi. Gerçek bir Telegram girişi testin parçası değildi.

İhtiyacınız olanlar

1. Veritabanını oluşturun

  1. Bot sunucunuzu panelde açın ve Veritabanları bölümüne tıklayın.
  2. Yeni veritabanı düğmesine tıklayın. Etiket isteğe bağlıdır; Şu adla oluşturulacak: tam adı gösterir. Veritabanı oluştur düğmesine tıklayın.
  3. Yeni veritabanının sayfası açılır (daha sonra Veritabanlarınız içinde ona tıklayın). Bağlantı kartında Sunucu adresi, Port, Veritabanı, Kullanıcı Adı ve Şifre görünür. Hazır yapılandırma altında, Şunun için yapılandırma içinde Python (aiomysql) kod parçası aynı değerler doldurulmuş olarak yer alır.

2. Bilgileri Ortam’a girin

  1. Bot sayfasında, Ortam bölmesinde .env dosyası yapıştır düğmesine tıklayın.
  2. Şu beş satırı Bağlantı kartındaki değerlerle yapıştırın:
    DB_HOST=your-host
    DB_PORT=3306
    DB_NAME=your-database
    DB_USER=your-username
    DB_PASSWORD=your-password
  3. DB_PASSWORD bir Gizli rozeti alır: yalnızca yazma amaçlı kaydedilir ve bir daha gösterilmez. 5 değişkeni içe aktar düğmesine tıklayın.

Gerçek portu Bağlantı kartından kullanın. Bot token’ı bu satırlardan biri değildir: Bot sayfasında, Telegram bölmesinde kaydettiğiniz token koda zaten BOT_TOKEN adıyla ulaşır, bu ad aiogram’ın kendi örneklerinde kullanılır (ayrıca TELEGRAM_BOT_TOKEN, TOKEN, TELEGRAM_TOKEN ve TELOXIDE_TOKEN). .env dosyanızda token satırı varsa, iletişim kutusu onu atlar. Bu bölme hakkında daha fazlası: Discord botunuz için ortam değişkenleri ve gizli değerler nasıl ayarlanır.

3. Kod

Küçük bir not botu: /note buy milk bir not kaydeder, /notes son beş notunuzu gösterir.

requirements.txt

aiogram>=3,<4
aiomysql

bot.py

import asyncio
import logging
import os
import sys

import aiomysql
from aiogram import Bot, Dispatcher
from aiogram.filters import Command, CommandObject
from aiogram.types import Message

dp = Dispatcher()


@dp.message(Command("note"))
async def note(message: Message, command: CommandObject, pool: aiomysql.Pool) -> None:
    text = (command.args or "").strip()
    if not text:
        await message.answer("Write your note after the command, for example: /note buy milk")
        return
    if len(text) > 500:
        await message.answer("A note can be up to 500 characters.")
        return
    async with pool.acquire() as conn:
        async with conn.cursor() as cur:
            await cur.execute(
                "INSERT INTO notes (user_id, text) VALUES (%s, %s)",
                (message.from_user.id, text),
            )
    await message.answer("Saved. Send /notes to see your last five.")


@dp.message(Command("notes"))
async def notes(message: Message, pool: aiomysql.Pool) -> None:
    async with pool.acquire() as conn:
        async with conn.cursor() as cur:
            await cur.execute(
                "SELECT text FROM notes WHERE user_id = %s ORDER BY id DESC LIMIT 5",
                (message.from_user.id,),
            )
            rows = await cur.fetchall()
    if not rows:
        await message.answer("You have no notes yet.")
        return
    await message.answer("\n".join(f"- {text}" for (text,) in rows))


async def main() -> None:
    # One pool for the whole bot. The values come from the Environment pane.
    pool = await aiomysql.create_pool(
        host=os.environ["DB_HOST"],
        port=int(os.environ["DB_PORT"]),
        user=os.environ["DB_USER"],
        password=os.environ["DB_PASSWORD"],
        db=os.environ["DB_NAME"],
        autocommit=True,
        maxsize=5,
    )
    async with pool.acquire() as conn:
        async with conn.cursor() as cur:
            await cur.execute(
                "CREATE TABLE IF NOT EXISTS notes ("
                "id INT AUTO_INCREMENT PRIMARY KEY, "
                "user_id BIGINT NOT NULL, "
                "text VARCHAR(500) NOT NULL, "
                "created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "
                "KEY user_id (user_id))"
            )
    print("Database ready")

    bot = Bot(os.environ["BOT_TOKEN"])
    try:
        # Every keyword passed here reaches the handlers that ask for it by name.
        await dp.start_polling(bot, pool=pool)
    finally:
        pool.close()
        await pool.wait_closed()


if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO, stream=sys.stdout)
    asyncio.run(main())

Ne yapar: main() havuzu açar, notes tablosu yoksa oluşturur ve bot Telegram’dan mesaj istemeden önce Database ready yazdırır. dp.start_polling(bot, pool=pool) aiogram’ın kendi nesne paylaşma yöntemidir: ilettiğiniz her ek anahtar sözcük, o adda parametresi olan her handler’a aiogram’ın verdiği veri olur, bu yüzden note() ve notes() aynı havuzu alır. Bot durduğunda finally bloğu havuzu kapatır.

%s yer tutucularını sürücü doldurur, bu yüzden '); DROP TABLE notes; -- gibi bir not düz metin olarak saklanır. Testimizde 2. satır olarak kaydedildi ve tablo yerinde kaldı. SQL’i asla f-string ile oluşturmayın.

Telegram kimliklerini BIGINT olarak saklayın

Telegram’ın Bot API belgeleri, bir kullanıcı veya sohbet kimliğinin “32’den fazla anlamlı bite sahip olabileceğini” ama en fazla 52 bit olduğunu söylüyor; bu yüzden 64 bitlik bir tam sayı güvenlidir. Kimlikler sayfası aralıkları verir: kullanıcı kimlikleri 1.099.511.627.775’e kadar çıkar, grup, süpergrup ve kanal kimlikleri ise negatiftir. Süpergruplar ve kanallar -1.000.000.000.001 ile -1.997.852.516.352 arasındadır (örneğin -1001234567890). MySQL’in INT türü 2.147.483.647’de durur. Bizim testimizde, veritabanı sunucumuzun kullandığı aynı katı SQL modunda, 7.000.000.001 kullanıcı kimliğini bir INT sütununa kaydetmek başarısız oldu. aiomysql şunu verdi:

pymysql.err.DataError: (1264, "Out of range value for column 'user_id' at row 1")

Bir BIGINT sütunu onu sakladı, -1001234567890’ı da. Düz BIGINT kullanın, BIGINT UNSIGNED değil; böylece sohbet kimlikleri de sığar.

4. Çalıştırın

  1. bot.py ve requirements.txt dosyalarını Dosyalar bölümünden yükleyin.
  2. Başlat düğmesine basın ve Konsol bölümünü açın. Başlatma, requirements.txt içindeki paketleri kurar, ardından [PlayNHost] Starting your bot, Database ready ve Run polling for bot @your_bot satırlarını gösterir. Bu son satır, Genel bakış altındaki durumu Telegram’da çevrimiçi olarak değiştirir.
  3. Telegram’da botunuza /note buy milk gönderin, sonra /notes. Satırlara bakmak için veritabanının sayfasını açın ve phpMyAdmin’de aç düğmesine tıklayın.

İkinci başlatmadan itibaren konsol ayrıca Warning: Table 'notes' already exists satırını da gösterir; önünde aiomysql’in cursors.py dosyasının yolu bulunur. Bu, MariaDB’nin CREATE TABLE IF NOT EXISTS için verdiği ve sürücünün ilettiği nottur. Sorun yok.

Testimizde Telegram taklidi şu komutları gönderdi ve şu yanıtları aldı:

GönderilenYanıt
/note buy milkSaved. Send /notes to see your last five.
/noteWrite your note after the command, for example: /note buy milk
/note ve 501 karakterA note can be up to 500 characters.
/notesKaydedilen notlar, en yeniden başlayarak
/notes ikinci bir kullanıcıdanYou have no notes yet.

Aynı anda çok mesaj

aiogram her gelen güncellemeyi ayrı bir görev olarak işler, bu yüzden birkaç komut sorgularını aynı anda çalıştırabilir. Testimizde birlikte gelen 4 komutun her biri yaklaşık 50 ms içinde işlendi. maxsize=5 havuzu 5 bağlantıyla sınırlar; altıncı sorgu yenisini açmak yerine boş bir bağlantıyı bekler. Testten sonra bot 2 açık bağlantı tutuyordu.

Autocommit tuzağı

aiomysql bağlantıları autocommit kapalı olarak açar. Bu durumda bir INSERT açık bir işlemin içinde kalır. Bağlantı o işlem hâlâ açıkken havuza döndüğünde aiomysql onu kapatır ve MariaDB onaylanmamış satırı atar. Testimizde autocommit=True olmayan bir havuza eklenen satırı diğer 0 bağlantı gördü. Ya autocommit=True değerini create_pool’a yukarıdaki gibi verin ya da yazma işlemlerinizden sonra await conn.commit() çağırın.

Başarısız bir sorgu botu durdurmaz

Testimizde bot çalışırken tabloyu sildik ve /notes gönderdik. aiogram hatayı, şununla biten bir traceback ile kaydetti: pymysql.err.ProgrammingError: (1146, "Table '...notes' doesn't exist"). Bu komut için yanıt göndermedi ve çalışmaya devam etti: tablo geri geldiğinde sonraki /note kaydedildi. Komutu gönderen kişi hiçbir şey görmez, bu yüzden bir sorgu başarısız olabiliyorsa hatayı handler içinde yakalayın ve kısa bir mesajla yanıt verin.

Sessiz bir gece havuzu bozmaz

Veritabanı sunucumuz 8 saat boşta kalan bir bağlantıyı kapatır. O kadar uzun süre mesaj almayan bir bot, örneğin gece boyunca, havuzdaki bağlantılarını kapalı bulur. aiomysql bir handler bağlantı istediğinde bunu kontrol eder ve kapalı olanın yerine yenisini açar. Boşta kalma sınırını 5 saniyeye düşürerek test ettik: 7 sessiz saniyeden sonra sonraki /note hatasız kaydedildi.

Havuzunuz için önemli sınırlar

  • Veritabanı kullanıcısı başına aynı anda 20 bağlantı. Aynı girişi kullanan her işlem sayılır. 21. bağlantı şununla reddedilir: (1226, "User '...' has exceeded the 'max_user_connections' resource (current value: 20)"). maxsize=5 botu bu sınırın çok altında tutar.
  • 120 saniyeden uzun süren bir sorgu durdurulur. MariaDB onu 1969 numaralı hatayla sonlandırır: Query execution was interrupted (max_statement_time exceeded). Bizim daha önceki bir testimizde, aynı şekilde ayarlanmış bir hesapta bu, 120,03 saniye sonra oldu. Bir botun sorguları normalde milisaniye sürer, bu yüzden bu yalnızca kontrolden çıkmış bir sorguyu etkiler.
  • Veritabanı başına 500 MB. Ne kadarının kullanıldığını Veritabanları sayfası gösterir. 500 MB’a ulaşınca, siz satır silene kadar yazma işlemleri durdurulur.
  • Sunucu yedekleri veritabanını içermez. Ara sıra phpMyAdmin’den dışa aktarın.

Hatalar ve anlamları

Bunlar aiomysql’in testimizde verdiği hatalardır; konsolda bir traceback’i bitirdikleri biçimde yazılmıştır (aiomysql’in altta kullandığı PyMySQL’den gelirler):

  • pymysql.err.OperationalError: (1045, "Access denied for user ..."): yanlış DB_USER veya DB_PASSWORD. Şifreyi sıfırla işleminden sonra (Bağlantı kartında) eski şifre hemen geçersiz olur: DB_PASSWORD değerini değiştirin ve botu yeniden başlatın.
  • pymysql.err.OperationalError: (2003, "Can't connect to MySQL server on ..."): yanlış sunucu adresi veya port ya da PlayNHost dışından bir bağlantı. Veritabanı yalnızca PlayNHost sunucularından ve phpMyAdmin’den gelen bağlantıları kabul eder, bu yüzden kendi bilgisayarınızda da bunu alırsınız.
  • pymysql.err.OperationalError: (1226, ... 'max_user_connections' ...): çok fazla açık bağlantı, yukarıdaki sınırlara bakın. Örneğin botun ikinci bir kopyası ya da her mesaj için açılan bir havuz.
  • pymysql.err.DataError: (1264, "Out of range value for column 'user_id' at row 1"): bir kimlik sütunu INT. Onu BIGINT yapın.
  • pymysql.err.ProgrammingError: (1146, "Table '...' doesn't exist"): tablo yok. Yukarıdaki kod onu başlangıçta oluşturur; elle oluşturduysanız adını kontrol edin.

Ortam içinde eksik bir değişken, botu bağlanmadan önce şu hatayla durdurur: KeyError: 'DB_HOST' ya da eksik olanın adıyla. Çünkü kod her değeri os.environ[...] ile okur. Değişkeni ekleyin, sonra botu yeniden başlatın: değişkenler bir sonraki başlatmada uygulanır.

Yanlış token, veritabanı kısmından sonra ortaya çıkar. Sahte token’ımızla gerçek Telegram’a bağlandığımızda bot Database ready yazdırdı, sonra şununla durdu: aiogram.exceptions.TelegramUnauthorizedError: Telegram server says - Unauthorized. Token’ı @BotFather’dan yeniden kopyalayın ve Bot sayfasında, Telegram bölmesine yapıştırın. Daha fazla Telegram hatası: Telegram botu 409 Conflict hatasını düzeltin.

Ne kadar bellek kullanır

Testimizde çalışan bot, 8 komutu yanıtladıktan sonra, havuzu açıkken, en küçük bot planımız gibi 512 MB ile sınırlı bir konteynerde 174 MB bellek (resident) kullandı. Bunun çoğu aiogram’ın kendisi: önceki barındırma testimizde yalnızca aiogram 173 MB ölçüldü (o bot, hiçbir mesajı işlemeden önce reddedilen bir token’da durdu, yani iki sayı birbirine yakın ama tam bir karşılaştırma değil). aiogram ve aiomysql’i pip ile sıfırdan kurmak, dosya önbelleği dahil, 163 MB’ta zirve yaptı. Bu, en küçük planda kendi kodunuza yer bırakır. Barındırma kılavuzu kütüphaneleri karşılaştırır: PlayNHost’ta python-telegram-bot ve aiogram karşılaştırması.

Başka bir kütüphaneyle aynısı

Telegram botunuz için veritabanı dahil bir sunucu mu arıyorsunuz? Telegram bot planlarına göz atın.

Botun başka bir yerde mi? Bize taşı

EN YENİ BLOG YAZILARIMIZ