NEU 📢 Begrenztes Startangebot: bis zu 70 % Rabatt auf deinen ersten Monat, für jedes Spiel!Tarife ansehen

MySQL-Datenbank mit einem discord.py-Bot nutzen (aiomysql)

Discord-Bot-HostingUpdated Nov 6, 20267 min read

Kurzantwort. Füge aiomysql zu deinem discord.py-Bot hinzu, erstelle in setup_hook einen Pool mit autocommit=True und führe jede Abfrage darüber mit %s-Platzhaltern aus. Ohne autocommit=True (oder ein commit() nach jedem Schreibzugriff) speichert aiomysql deine INSERTs nicht. Bei PlayNHost enthält jeder Bot-Tarif eine MySQL-Datenbank: Lege sie auf der Seite Datenbanken an, füge ihre Zugangsdaten im Bereich Umgebung ein, und der Code unten funktioniert so, wie er ist. Die Datenbank nimmt Verbindungen von deinen PlayNHost-Servern und von phpMyAdmin an, nicht von deinem eigenen Rechner. Wir haben den Code am 6. November 2026 mit discord.py 2.7.1, aiomysql 0.3.2 und Python 3.12 getestet, gegen MariaDB 10.11 mit einem Datenbankkonto, das wie unseres eingerichtet ist (20 Verbindungen, 120 Sekunden Abfragelimit): Start, Tabelle und den Befehl /warn, aber keinen echten Discord-Login.

Was du brauchst

1. Die Datenbank erstellen

  1. Öffne deinen Bot-Server im Panel und klicke auf Datenbanken.
  2. Klicke auf Neue Datenbank. Eine Bezeichnung ist optional; unter Wird erstellt als siehst du den vollständigen Namen. Klicke auf Datenbank erstellen.
  3. Die Seite der neuen Datenbank öffnet sich (später klickst du sie unter Deine Datenbanken an). Ihre Karte Verbindung zeigt Host, Port, Datenbank, Benutzername und Passwort. Unter Fertige Config bietet Config für einen Ausschnitt Python (aiomysql) mit denselben, bereits eingetragenen Werten.

2. Die Zugangsdaten in „Umgebung“ eintragen

  1. Öffne die Seite Bot, den Bereich Umgebung, und klicke auf Eine .env-Datei einfügen.
  2. Füge diese fünf Zeilen mit den Werten aus der Karte Verbindung ein:
    DB_HOST=your-host
    DB_PORT=3306
    DB_NAME=your-database
    DB_USER=your-username
    DB_PASSWORD=your-password
  3. DB_PASSWORD bekommt ein Secret-Badge: Es wird nur schreibend gespeichert und nie wieder angezeigt. Klicke auf 5 Variablen importieren.

Nimm den echten Port aus der Karte Verbindung. Dein Code liest die Werte mit os.environ, deshalb liegt das Passwort nie in den Dateien deines Bots. Mehr zu diesem Bereich: Umgebungsvariablen und Secrets für deinen Discord-Bot setzen.

3. Der Code

requirements.txt

discord.py
aiomysql

bot.py

import os

import aiomysql
import discord
from discord import app_commands


class Bot(discord.Client):
    def __init__(self):
        super().__init__(intents=discord.Intents.default())
        self.tree = app_commands.CommandTree(self)
        self.pool = None

    async def setup_hook(self):
        # One pool for the whole bot. The values come from the Environment pane.
        self.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 self.pool.acquire() as conn:
            async with conn.cursor() as cur:
                await cur.execute(
                    "CREATE TABLE IF NOT EXISTS warnings ("
                    "id INT AUTO_INCREMENT PRIMARY KEY, "
                    "guild_id BIGINT UNSIGNED NOT NULL, "
                    "user_id BIGINT UNSIGNED NOT NULL, "
                    "reason VARCHAR(200) NOT NULL, "
                    "created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "
                    "KEY guild_user (guild_id, user_id))"
                )
        print("Database ready")
        await self.tree.sync()

    async def close(self):
        if self.pool is not None:
            self.pool.close()
            await self.pool.wait_closed()
        await super().close()


bot = Bot()


@bot.tree.command(description="Warn a member and count their warnings")
@app_commands.guild_only()
@app_commands.describe(user="Who to warn", reason="Why")
async def warn(interaction: discord.Interaction, user: discord.Member, reason: app_commands.Range[str, 1, 200]):
    async with bot.pool.acquire() as conn:
        async with conn.cursor() as cur:
            await cur.execute(
                "INSERT INTO warnings (guild_id, user_id, reason) VALUES (%s, %s, %s)",
                (interaction.guild_id, user.id, reason),
            )
            await cur.execute(
                "SELECT COUNT(*) FROM warnings WHERE guild_id = %s AND user_id = %s",
                (interaction.guild_id, user.id),
            )
            (total,) = await cur.fetchone()
    await interaction.response.send_message(f"{user.mention} has {total} warning(s).")


@bot.event
async def on_ready():
    print(f"Logged in as {bot.user}")


bot.run(os.environ["DISCORD_TOKEN"])

So funktioniert es: discord.py führt setup_hook einmal aus, während der Anmeldung und bevor ein Event eintrifft, sodass der Pool und die Tabelle warnings existieren, bevor der erste Befehl ankommt. Danach registriert es /warn bei Discord. @app_commands.guild_only() hält den Befehl aus Direktnachrichten heraus, wo es keinen Server zum Speichern gibt. Discord-IDs passen in BIGINT UNSIGNED. Die %s-Platzhalter füllt der Treiber aus, sodass ein Grund wie '); DROP TABLE warnings; -- als einfacher Text gespeichert wird. Baue das SQL niemals mit einem f-String zusammen.

4. Starten

  1. Lade bot.py und requirements.txt unter Dateien hoch.
  2. Klicke auf Starten und öffne Konsole. Beim Start werden die Pakete aus requirements.txt installiert, danach erscheinen [PlayNHost] Starting your bot und Database ready.
  3. Tippe in Discord /warn ein. Um die Zeilen anzusehen, öffne die Seite der Datenbank und klicke auf In phpMyAdmin öffnen.

Die autocommit-Falle

aiomysql öffnet Verbindungen mit ausgeschaltetem autocommit. Ein INSERT bleibt dann in einer offenen Transaktion. Geht die Verbindung mit noch offener Transaktion zurück in den Pool, schließt aiomysql sie, und MariaDB wirft die nicht bestätigte Zeile weg. In unserem Test sahen 0 andere Verbindungen eine Zeile, die auf einem Pool ohne autocommit=True eingefügt wurde. Übergib entweder autocommit=True an create_pool, wie oben, oder rufe nach deinen Schreibzugriffen await conn.commit() auf.

Limits, die für deinen Pool wichtig sind

  • 20 gleichzeitige Verbindungen pro Datenbankbenutzer. Jeder Prozess mit demselben Login zählt mit. Verbindung 21 wird mit (1226, "User '...' has exceeded the 'max_user_connections' resource (current value: 20)") abgelehnt. maxsize=5 hält den Bot deutlich darunter.
  • Eine Abfrage, die länger als 120 Sekunden läuft, wird abgebrochen. MariaDB beendet sie mit dem Fehler 1969, Query execution was interrupted (max_statement_time exceeded). In unserem Test kam das nach 120.03 Sekunden. Die Abfragen eines Bots dauern normalerweise Millisekunden, das trifft also nur eine außer Kontrolle geratene Abfrage.
  • 500 MB pro Datenbank. Die Seite Datenbanken zeigt, wie viel belegt ist. Bei 500 MB werden Schreibzugriffe gestoppt, bis du Zeilen löschst.
  • Server-Backups enthalten die Datenbank nicht. Exportiere sie ab und zu aus phpMyAdmin.

Fehler und was sie bedeuten

Das sind die genauen Fehler, die aiomysql in unserem Test lieferte (sie stammen von PyMySQL, das aiomysql darunter verwendet):

  • pymysql.err.OperationalError (1045, "Access denied for user ..."): falscher Wert bei DB_USER oder DB_PASSWORD. Nach Passwort zurücksetzen auf der Karte Verbindung funktioniert das alte Passwort sofort nicht mehr: Ersetze DB_PASSWORD und starte den Bot neu.
  • pymysql.err.OperationalError (2003, "Can't connect to MySQL server on ..."): falscher Host oder Port, oder eine Verbindung von außerhalb von PlayNHost. Die Datenbank nimmt nur Verbindungen von PlayNHost-Servern und phpMyAdmin an, deshalb bekommst du diesen Fehler auch auf deinem eigenen Rechner.
  • (1226, ... 'max_user_connections' ...): zu viele offene Verbindungen, siehe die Limits oben.

Eine Variable, die in Umgebung fehlt, stoppt den Bot vor dem Verbindungsaufbau, mit KeyError: 'DB_HOST' oder dem Namen der fehlenden Variable, weil der Code jeden Wert mit os.environ[...] liest. Füge sie hinzu und starte den Bot neu: Variablen gelten ab dem nächsten Start.

Weitere Bot-Fehler: Häufige Discord-Bot-Fehler und was sie bedeuten.

Wie viel Arbeitsspeicher es dazukommt

Die Installation von discord.py und aiomysql mit pip von Grund auf erreichte in unserem Test einen Spitzenwert von 78 MB Arbeitsspeicher, in einem Container mit 512 MB Limit wie bei unserem kleinsten Bot-Tarif. Wie viel der laufende Bot braucht, hängt vor allem davon ab, auf wie vielen Servern er ist: Wie viel RAM braucht ein Discord-Bot?

Dasselbe in JavaScript oder mit Prisma

Du brauchst einen Bot-Server mit inklusiver Datenbank? Zu den Bot-Tarifen.

Läuft dein Bot schon woanders? Zieh ihn zu uns um

UNSERE NEUESTEN BLOGS