NUOVO 📢 Offerta di lancio limitata: fino al 70% di sconto sul tuo primo mese, per tutti i giochi!Vedi i piani

Come usare un database MySQL con un bot aiogram (aiomysql)

Hosting di bot TelegramUpdated Nov 6, 202612 min read

Risposta breve. Aggiungi aiomysql al tuo bot aiogram 3, apri un solo pool con autocommit=True prima che parta il polling e passalo ai tuoi handler con dp.start_polling(bot, pool=pool): aiogram lo consegna a ogni handler che ha un parametro pool. Usa i segnaposto %s e salva gli ID Telegram come BIGINT, non come INT. Su PlayNHost ogni piano bot include un database MySQL: crealo nella pagina Database, incolla i suoi dati nel riquadro Ambiente e il codice qui sotto funziona così com’è. Il database accetta connessioni dai tuoi server PlayNHost e da phpMyAdmin, non dal tuo computer. Abbiamo testato questo codice il 6 novembre 2026 con aiogram 3.31.0, aiomysql 0.3.2 e Python 3.12, su MariaDB 10.11, con un account database configurato come il nostro (20 connessioni, limite di 120 secondi per query) e un sostituto del server Bot API di Telegram: 8 comandi in ingresso, 8 risposte in uscita, le note salvate nel database. Un vero login su Telegram non faceva parte del test.

Cosa ti serve

1. Crea il database

  1. Apri il tuo server bot nel pannello e clicca su Database.
  2. Clicca su Nuovo database. Il campo Etichetta è facoltativo; Verrà creato come mostra il nome completo. Clicca su Crea database.
  3. Si apre la pagina del nuovo database (più tardi, cliccalo in I tuoi database). La sua scheda Connessione mostra Host, Porta, Database, Nome utente e Password. Sotto Config pronta all’uso, Config per ha uno snippet Python (aiomysql) con gli stessi valori già inseriti.

2. Inserisci i dati in Ambiente

  1. Apri la pagina Bot, riquadro Ambiente, e clicca su Incolla un file .env.
  2. Incolla queste cinque righe con i valori della scheda Connessione:
    DB_HOST=your-host
    DB_PORT=3306
    DB_NAME=your-database
    DB_USER=your-username
    DB_PASSWORD=your-password
  3. DB_PASSWORD riceve il badge Segreto: viene salvata in sola scrittura e non viene mai più mostrata. Clicca su Importa 5 variabili.

Usa la porta reale della scheda Connessione. Il token del bot non è una di queste righe: il token che hai salvato nella pagina Bot, riquadro Telegram, arriva già al tuo codice come BOT_TOKEN, il nome usato dagli esempi di aiogram (e come TELEGRAM_BOT_TOKEN, TOKEN, TELEGRAM_TOKEN e TELOXIDE_TOKEN). Se il tuo file .env ha una riga con il token, la finestra la salta. Altro su questo riquadro: Come impostare variabili d'ambiente e segreti per il tuo bot Discord.

3. Il codice

Un piccolo bot di note: /note buy milk salva una nota, /notes mostra le tue ultime cinque.

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

Cosa fa: main() apre il pool, crea la tabella notes se manca e stampa Database ready prima che il bot chieda i messaggi a Telegram. dp.start_polling(bot, pool=pool) è il modo di aiogram di condividere un oggetto: ogni parola chiave in più che passi diventa un dato che aiogram consegna a ogni handler con un parametro di quel nome, quindi note() e notes() ricevono lo stesso pool. Quando il bot si ferma, il blocco finally chiude il pool.

I segnaposto %s vengono riempiti dal driver, quindi una nota come '); DROP TABLE notes; -- viene salvata come testo semplice. Nel nostro test è stata salvata come riga 2 e la tabella è rimasta. Non costruire mai l’SQL con una f-string.

Salva gli ID Telegram come BIGINT

La documentazione della Bot API di Telegram dice che un ID di utente o di chat “può avere più di 32 bit significativi” ma al massimo 52, quindi un intero a 64 bit è sicuro. La sua pagina sugli ID dà gli intervalli: gli ID utente arrivano fino a 1.099.511.627.775, mentre gli ID di gruppi, supergruppi e canali sono negativi. Supergruppi e canali vanno da -1.000.000.000.001 a -1.997.852.516.352 (per esempio -1001234567890). Il tipo INT di MySQL si ferma a 2.147.483.647. Nel nostro test, con la stessa modalità SQL rigorosa che usa il nostro server database, salvare l’ID utente 7.000.000.001 in una colonna INT è fallito. aiomysql ha sollevato:

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

Una colonna BIGINT lo ha salvato, e -1001234567890 anche. Usa un semplice BIGINT, non BIGINT UNSIGNED, così entrano anche gli ID delle chat.

4. Avvialo

  1. Carica bot.py e requirements.txt in Files.
  2. Premi Avvia e apri Console. All’avvio vengono installati i pacchetti da requirements.txt, poi compaiono [PlayNHost] Starting your bot, Database ready e Run polling for bot @your_bot. Quest’ultima riga porta lo stato in Panoramica su Online su Telegram.
  3. In Telegram, invia al tuo bot /note buy milk, poi /notes. Per vedere le righe, apri la pagina del database e clicca su Apri in phpMyAdmin.

Dal secondo avvio, la console mostra anche Warning: Table 'notes' already exists, preceduto dal percorso del file cursors.py di aiomysql. È la nota di MariaDB per CREATE TABLE IF NOT EXISTS, inoltrata dal driver. Non c’è nessun problema.

Nel nostro test il sostituto di Telegram ha inviato questi comandi e ricevuto queste risposte:

InviatoRisposta
/note buy milkSaved. Send /notes to see your last five.
/noteWrite your note after the command, for example: /note buy milk
/note e 501 caratteriA note can be up to 500 characters.
/notesLe note salvate, dalla più recente
/notes da un secondo utenteYou have no notes yet.

Tanti messaggi insieme

aiogram gestisce ogni aggiornamento in arrivo come un task a sé, quindi più comandi possono eseguire le loro query nello stesso momento. Nel nostro test, 4 comandi arrivati insieme sono stati gestiti ciascuno in circa 50 ms. maxsize=5 limita il pool a 5 connessioni; una sesta query aspetta che se ne liberi una invece di aprirne un’altra. Dopo il test, il bot teneva aperte 2 connessioni.

La trappola dell’autocommit

aiomysql apre le connessioni con l’autocommit disattivato. Un INSERT resta quindi dentro una transazione aperta. Quando la connessione torna al pool con quella transazione ancora aperta, aiomysql la chiude e MariaDB scarta la riga non confermata. Nel nostro test, una riga inserita in un pool senza autocommit=True è stata vista da 0 altre connessioni. Passa autocommit=True a create_pool, come sopra, oppure chiama await conn.commit() dopo le tue scritture.

Una query fallita non ferma il bot

Nel nostro test abbiamo eliminato la tabella mentre il bot era in esecuzione e abbiamo inviato /notes. aiogram ha registrato l’errore con un traceback che finiva con pymysql.err.ProgrammingError: (1146, "Table '...notes' doesn't exist"), non ha inviato alcuna risposta a quel comando ed è rimasto in esecuzione: una volta tornata la tabella, il /note successivo è stato salvato. Chi ha inviato il comando non vede nulla, quindi se una query può fallire, intercetta l’errore nell’handler e rispondi con un breve messaggio.

Una notte tranquilla non rompe il pool

Il nostro server database chiude una connessione rimasta inattiva per 8 ore. Un bot che non riceve messaggi per tanto tempo, per esempio di notte, trova chiuse le connessioni del pool. aiomysql lo controlla quando un handler chiede una connessione e ne apre una nuova al posto di quella chiusa. L’abbiamo provato con il limite di inattività abbassato a 5 secondi: dopo 7 secondi di silenzio, il /note successivo è stato salvato senza errori.

I limiti che contano per il tuo pool

  • 20 connessioni contemporanee per utente del database. Ogni processo che usa lo stesso login conta. La connessione 21 viene rifiutata con (1226, "User '...' has exceeded the 'max_user_connections' resource (current value: 20)"). maxsize=5 tiene il bot ben al di sotto.
  • Una query che dura più di 120 secondi viene fermata. MariaDB la termina con l’errore 1969, Query execution was interrupted (max_statement_time exceeded). In un nostro test precedente, su un account configurato allo stesso modo, è successo dopo 120,03 secondi. Le query di un bot di solito durano millisecondi, quindi questo colpisce solo una query fuori controllo.
  • 500 MB per database. La pagina Database mostra quanto spazio è usato. A 500 MB le scritture vengono fermate finché non elimini delle righe.
  • I backup del server non includono il database. Esportalo ogni tanto da phpMyAdmin.

Errori e che cosa significano

Questi sono gli errori che aiomysql ha dato nel nostro test, scritti come terminano un traceback nella console (arrivano da PyMySQL, che aiomysql usa sotto):

  • pymysql.err.OperationalError: (1045, "Access denied for user ..."): valore sbagliato di DB_USER o DB_PASSWORD. Dopo Reimposta password sulla scheda Connessione, la vecchia password smette subito di funzionare: sostituisci DB_PASSWORD e riavvia il bot.
  • pymysql.err.OperationalError: (2003, "Can't connect to MySQL server on ..."): host o porta sbagliati, oppure una connessione da fuori PlayNHost. Il database accetta solo connessioni dai server PlayNHost e da phpMyAdmin, quindi è anche ciò che ottieni dal tuo computer.
  • pymysql.err.OperationalError: (1226, ... 'max_user_connections' ...): troppe connessioni aperte, vedi i limiti sopra. Per esempio una seconda copia del bot, o un pool aperto per ogni messaggio.
  • pymysql.err.DataError: (1264, "Out of range value for column 'user_id' at row 1"): una colonna ID è di tipo INT. Cambiala in BIGINT.
  • pymysql.err.ProgrammingError: (1146, "Table '...' doesn't exist"): la tabella manca. Il codice sopra la crea all’avvio; se l’hai creata a mano, controlla il suo nome.

Una variabile mancante in Ambiente ferma il bot prima che si connetta, con KeyError: 'DB_HOST' o con il nome di quella mancante, perché il codice legge ogni valore con os.environ[...]. Aggiungila, poi riavvia il bot: le variabili si applicano al prossimo avvio.

Un token sbagliato si nota dopo la parte del database. Con il nostro token falso contro il vero Telegram, il bot ha stampato Database ready, poi si è fermato con aiogram.exceptions.TelegramUnauthorizedError: Telegram server says - Unauthorized. Copia di nuovo il token da @BotFather e incollalo nella pagina Bot, riquadro Telegram. Altri errori di Telegram: Come risolvere l’errore 409 Conflict di un bot Telegram.

Quanta memoria usa

Nel nostro test il bot in esecuzione ha usato 174 MB di memoria (residente) dopo aver risposto agli 8 comandi, con il pool aperto, in un container limitato a 512 MB come il nostro piano bot più piccolo. Gran parte è aiogram stesso: nel nostro precedente test di hosting, aiogram da solo ha misurato 173 MB (quel bot si è fermato per un token rifiutato, prima di gestire un messaggio, quindi i due numeri sono vicini ma il confronto non è esatto). Installare aiogram e aiomysql con pip da zero ha raggiunto un picco di 163 MB, cache dei file inclusa. Resta spazio per il tuo codice sul piano più piccolo. La guida all’hosting confronta le librerie: python-telegram-bot vs aiogram su PlayNHost.

Lo stesso con un’altra libreria

Ti serve un server per il tuo bot Telegram con un database incluso? Scopri i piani bot per Telegram.

Il tuo bot è già ospitato altrove? Spostalo da noi

ULTIMI ARTICOLI