NOWOŚĆ 📢 Ograniczona oferta na start: do 70% zniżki na pierwszy miesiąc, na każdą grę!Zobacz plany

Jak używać bazy danych MySQL z botem aiogram (aiomysql)

Hosting botów TelegramUpdated Nov 6, 202611 min read

Krótka odpowiedź. Dodaj aiomysql do swojego bota aiogram 3, otwórz jedną pulę z autocommit=True zanim ruszy polling, i przekaż ją handlerom przez dp.start_polling(bot, pool=pool): aiogram poda ją każdemu handlerowi, który ma parametr pool. Używaj symboli zastępczych %s i przechowuj ID Telegrama jako BIGINT, a nie jako INT. W PlayNHost każdy plan bota zawiera jedną bazę MySQL: utwórz ją na stronie Bazy danych, wklej jej dane w sekcji Środowisko a kod poniżej zadziała bez zmian. Baza przyjmuje połączenia z twoich serwerów PlayNHost i z phpMyAdmin, a nie z twojego własnego komputera. Przetestowaliśmy ten kod 6 listopada 2026 z aiogram 3.31.0, aiomysql 0.3.2 i Python 3.12, na MariaDB 10.11, z kontem bazy skonfigurowanym jak nasze (20 połączeń, limit zapytania 120 sekund) oraz atrapą serwera Bot API Telegrama: 8 poleceń na wejściu, 8 odpowiedzi na wyjściu, notatki zapisane w bazie. Prawdziwe logowanie do Telegrama nie było częścią testu.

Czego potrzebujesz

1. Utwórz bazę danych

  1. Otwórz serwer bota w panelu i kliknij Bazy danych.
  2. Kliknij Nowa baza danych. Pole Etykieta jest opcjonalne; pole Zostanie utworzona jako pokazuje pełną nazwę. Kliknij Utwórz bazę danych.
  3. Otworzy się strona nowej bazy (później kliknij ją w Twoje bazy danych). Jej karta Połączenie pokazuje Adres hosta, Port, Baza danych, Nazwa użytkownika i Hasło. W sekcji Gotowa konfiguracja, pole Konfiguracja dla ma fragment Python (aiomysql) z tymi samymi, już wpisanymi wartościami.

2. Wpisz dane w sekcji Środowisko

  1. Otwórz stronę Bot, sekcja Środowisko, i kliknij Wklej plik .env.
  2. Wklej te pięć linii z wartościami z karty Połączenie:
    DB_HOST=your-host
    DB_PORT=3306
    DB_NAME=your-database
    DB_USER=your-username
    DB_PASSWORD=your-password
  3. DB_PASSWORD dostaje plakietkę Sekret: jest zapisywana tylko do zapisu i nigdy więcej nie jest pokazywana. Kliknij Zaimportuj 5 zmiennych.

Użyj prawdziwego portu z karty Połączenie. Tokenu bota nie ma wśród tych linii: token zapisany na stronie Bot, sekcja Telegram, trafia już do twojego kodu jako BOT_TOKEN, czyli nazwa używana w przykładach samego aiogram (oraz jako TELEGRAM_BOT_TOKEN, TOKEN, TELEGRAM_TOKEN i TELOXIDE_TOKEN). Jeśli twój plik .env zawiera linię z tokenem, okno ją pominie. Więcej o tej sekcji: Jak ustawić zmienne środowiskowe i sekrety dla bota Discord.

3. Kod

Mały bot z notatkami: /note buy milk zapisuje notatkę, /notes pokazuje twoje ostatnie pięć.

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())

Co robi: main() otwiera pulę, tworzy tabelę notes jeśli jej brakuje, i wypisuje Database ready zanim bot zacznie pytać Telegram o wiadomości. dp.start_polling(bot, pool=pool) to własny sposób aiogram na współdzielenie obiektu: każde dodatkowe słowo kluczowe, które przekażesz, staje się danymi, które aiogram podaje każdemu handlerowi z parametrem o tej nazwie, więc note() i notes() dostają tę samą pulę. Gdy bot się zatrzymuje, blok finally zamyka pulę.

Symbole zastępcze %s wypełnia sterownik, więc notatka taka jak '); DROP TABLE notes; -- zostanie zapisana jako zwykły tekst. W naszym teście zapisała się jako wiersz 2, a tabela została. Nigdy nie buduj SQL za pomocą f-stringa.

Przechowuj ID Telegrama jako BIGINT

Dokumentacja Bot API Telegrama mówi, że ID użytkownika lub czatu „może mieć więcej niż 32 znaczące bity”, ale najwyżej 52, więc 64-bitowa liczba całkowita jest bezpieczna. Jej strona o ID podaje zakresy: ID użytkowników sięgają 1.099.511.627.775, a ID grup, supergrup i kanałów są ujemne. Supergrupy i kanały biegną od -1.000.000.000.001 do -1.997.852.516.352 (na przykład -1001234567890). Typ INT w MySQL kończy się na 2.147.483.647. W naszym teście, przy tym samym ścisłym trybie SQL, którego używa nasz serwer bazy danych, zapis ID użytkownika 7.000.000.001 w kolumnie INT nie powiódł się. aiomysql zgłosił:

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

Kolumna BIGINT zapisała go, tak samo jak -1001234567890. Używaj zwykłego BIGINT, a nie BIGINT UNSIGNED, żeby mieściły się też ID czatów.

4. Uruchom

  1. Prześlij bot.py i requirements.txt w Files.
  2. Naciśnij Uruchom i otwórz Console. Start instaluje pakiety z requirements.txt, a potem pokazuje [PlayNHost] Starting your bot, Database ready i Run polling for bot @your_bot. Ta ostatnia linia zmienia status w Przegląd na Online w Telegramie.
  3. W Telegramie wyślij swojemu botowi /note buy milk, a potem /notes. Aby zobaczyć wiersze, otwórz stronę bazy i kliknij Otwórz w phpMyAdmin.

Od drugiego startu konsola pokazuje też Warning: Table 'notes' already exists, poprzedzone ścieżką do pliku cursors.py z aiomysql. To uwaga MariaDB dla CREATE TABLE IF NOT EXISTS, przekazana przez sterownik. Nic nie jest zepsute.

W naszym teście atrapa Telegrama wysłała te polecenia i dostała te odpowiedzi:

WysłanoOdpowiedź
/note buy milkSaved. Send /notes to see your last five.
/noteWrite your note after the command, for example: /note buy milk
/note i 501 znakówA note can be up to 500 characters.
/notesZapisane notatki, najnowsze pierwsze
/notes od drugiego użytkownikaYou have no notes yet.

Wiele wiadomości naraz

aiogram obsługuje każdą przychodzącą aktualizację jako osobne zadanie, więc kilka poleceń może wykonywać zapytania w tym samym czasie. W naszym teście 4 polecenia, które przyszły razem, zostały obsłużone w około 50 ms każde. maxsize=5 ogranicza pulę do 5 połączeń; szóste zapytanie czeka na wolne zamiast otwierać kolejne. Po teście bot trzymał 2 otwarte połączenia.

Pułapka autocommit

aiomysql otwiera połączenia z wyłączonym autocommit. Wtedy INSERT zostaje w otwartej transakcji. Gdy połączenie wraca do puli z wciąż otwartą transakcją, aiomysql je zamyka, a MariaDB odrzuca niezatwierdzony wiersz. W naszym teście wiersz wstawiony w puli bez autocommit=True został zobaczony przez 0 innych połączeń. Albo przekaż autocommit=True do create_pool, jak wyżej, albo wywołaj await conn.commit() po swoich zapisach.

Nieudane zapytanie nie zatrzymuje bota

W naszym teście usunęliśmy tabelę, gdy bot działał, i wysłaliśmy /notes. aiogram zapisał błąd w logu z tracebackiem kończącym się na pymysql.err.ProgrammingError: (1146, "Table '...notes' doesn't exist"), nie wysłał odpowiedzi na to polecenie i działał dalej: gdy tabela wróciła, kolejne /note zostało zapisane. Osoba, która wysłała polecenie, nic nie widzi, więc jeśli zapytanie może się nie udać, złap błąd w handlerze i odpowiedz krótką wiadomością.

Spokojna noc nie psuje puli

Nasz serwer bazy danych zamyka połączenie, które było bezczynne przez 8 godzin. Bot, który tak długo nie dostaje wiadomości, na przykład w nocy, zastaje swoje połączenia z puli zamknięte. aiomysql sprawdza to, gdy handler prosi o połączenie, i otwiera nowe zamiast zamkniętego. Przetestowaliśmy to z limitem bezczynności obniżonym do 5 sekund: po 7 sekundach ciszy kolejne /note zostało zapisane bez błędu.

Limity ważne dla twojej puli

  • 20 połączeń naraz na użytkownika bazy. Liczy się każdy proces używający tego samego loginu. Połączenie 21 zostanie odrzucone z (1226, "User '...' has exceeded the 'max_user_connections' resource (current value: 20)"). maxsize=5 trzyma bota znacznie poniżej tego.
  • Zapytanie trwające dłużej niż 120 sekund zostaje zatrzymane. MariaDB kończy je błędem 1969, Query execution was interrupted (max_statement_time exceeded). W naszym wcześniejszym teście, na koncie ustawionym tak samo, stało się to po 120,03 sekundy. Zapytania bota zwykle trwają milisekundy, więc dotyczy to tylko zapytania, które wymknęło się spod kontroli.
  • 500 MB na bazę. Strona Bazy danych pokazuje, ile jest zajęte. Przy 500 MB zapisy są zatrzymywane, dopóki nie usuniesz wierszy.
  • Kopie zapasowe serwera nie obejmują bazy danych. Od czasu do czasu eksportuj ją z phpMyAdmin.

Błędy i co oznaczają

To błędy, które aiomysql zgłosił w naszym teście, zapisane tak, jak kończą traceback w konsoli (pochodzą z PyMySQL, którego aiomysql używa pod spodem):

  • pymysql.err.OperationalError: (1045, "Access denied for user ..."): zły DB_USER lub zły DB_PASSWORD. Po Zresetuj hasło na karcie Połączenie, stare hasło przestaje działać od razu: zamień DB_PASSWORD i zrestartuj bota.
  • pymysql.err.OperationalError: (2003, "Can't connect to MySQL server on ..."): zły host lub port albo połączenie spoza PlayNHost. Baza przyjmuje połączenia tylko z serwerów PlayNHost i phpMyAdmin, więc to samo dostaniesz na własnym komputerze.
  • pymysql.err.OperationalError: (1226, ... 'max_user_connections' ...): za dużo otwartych połączeń, zobacz limity powyżej. Na przykład druga kopia bota albo pula otwierana dla każdej wiadomości.
  • pymysql.err.DataError: (1264, "Out of range value for column 'user_id' at row 1"): kolumna ID ma typ INT. Zmień ją na BIGINT.
  • pymysql.err.ProgrammingError: (1146, "Table '...' doesn't exist"): brakuje tabeli. Kod powyżej tworzy ją przy starcie; jeśli została utworzona ręcznie, sprawdź jej nazwę.

Zmienna brakująca w Środowisko zatrzymuje bota przed połączeniem, z komunikatem KeyError: 'DB_HOST' lub nazwą brakującej, bo kod czyta każdą wartość przez os.environ[...]. Dodaj ją, potem zrestartuj bota: zmienne działają od następnego startu.

Zły token wychodzi na jaw po części z bazą. Z naszym fałszywym tokenem wobec prawdziwego Telegrama bot wypisał Database ready, a potem zatrzymał się z aiogram.exceptions.TelegramUnauthorizedError: Telegram server says - Unauthorized. Skopiuj token jeszcze raz od @BotFather i wklej go na stronie Bot, sekcja Telegram. Więcej błędów Telegrama: Jak naprawić błąd 409 Conflict bota Telegram.

Ile pamięci zajmuje

W naszym teście działający bot zajął 174 MB pamięci (rezydentnej) po odpowiedzi na 8 poleceń, z otwartą pulą, w kontenerze ograniczonym do 512 MB jak nasz najmniejszy plan bota. Większość to sam aiogram: w naszym wcześniejszym teście hostingu sam aiogram wyniósł 173 MB (ten bot zatrzymał się na odrzuconym tokenie, zanim obsłużył jakąkolwiek wiadomość, więc obie liczby są bliskie, ale to nie jest dokładne porównanie). Instalacja aiogram i aiomysql przez pip od zera osiągnęła szczyt 163 MB, razem z pamięcią podręczną plików. To zostawia miejsce na twój własny kod w najmniejszym planie. Przewodnik po hostingu porównuje biblioteki: python-telegram-bot kontra aiogram w PlayNHost.

To samo z inną biblioteką

Potrzebujesz serwera dla swojego bota Telegram z wliczoną bazą danych? Zobacz plany botów Telegram.

Twój bot działa już gdzie indziej? Przenieś go do nas

NASZE OSTATNIE WPISY