Krótka odpowiedź. Dodaj aiomysql do swojego bota discord.py, utwórz jedną pulę w setup_hook z autocommit=True i wykonuj każde zapytanie przez nią, używając symboli zastępczych %s. Bez autocommit=True (albo commit() po każdym zapisie) aiomysql nie zapisuje Twoich INSERT-ów. W PlayNHost każdy plan dla bota zawiera jedną bazę MySQL: utwórz ją na stronie Bazy danych, wklej jej dane w panelu Ś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 komputera. Przetestowaliśmy ten kod 6 listopada 2026 z discord.py 2.7.1, 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): start, tabelę i komendę /warn, ale nie prawdziwe logowanie do Discorda.
Otwórz serwer swojego bota w panelu i kliknij Bazy danych.
Kliknij Nowa baza danych. Pole Etykieta jest opcjonalne; w polu Zostanie utworzona jako zobaczysz pełną nazwę. Kliknij Utwórz bazę danych.
Otworzy się strona nowej bazy (później kliknij ją w sekcji Twoje bazy danych). Karta Połączenie pokazuje Adres hosta, Port, Baza danych, Nazwa użytkownika i Hasło. W sekcji Gotowa konfiguracja pole Konfiguracja dla zawiera fragment Python (aiomysql) z tymi samymi, już wypełnionymi wartościami.
2. Wpisz dane w sekcji Środowisko
Otwórz stronę Bot, panel Środowisko i kliknij Wklej plik .env.
Wklej te pięć linii z wartościami z karty Połączenie:
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"])
Co robi kod: discord.py uruchamia setup_hook jeden raz, podczas logowania i zanim nadejdzie jakiekolwiek zdarzenie, więc pula i tabela warnings istnieją, zanim pojawi się pierwsza komenda. Potem rejestruje /warn w Discordzie. @app_commands.guild_only() nie dopuszcza komendy w wiadomościach prywatnych, gdzie nie ma serwera, do którego można przypisać ostrzeżenie. Identyfikatory Discorda mieszczą się w BIGINT UNSIGNED. Symbole zastępcze %s wypełnia sterownik, więc powód taki jak '); DROP TABLE warnings; -- zostaje zapisany jako zwykły tekst. Nigdy nie składaj SQL za pomocą f-stringa.
4. Uruchom bota
Wgraj bot.py i requirements.txt w sekcji Files.
Naciśnij Uruchom i otwórz Console. Start instaluje pakiety z requirements.txt, a potem pokazuje [PlayNHost] Starting your bot i Database ready.
W Discordzie wpisz /warn. Aby zobaczyć wiersze, otwórz stronę bazy i kliknij Otwórz w phpMyAdmin.
Pułapka autocommit
aiomysql otwiera połączenia z wyłączonym autocommit. INSERT zostaje wtedy w otwartej transakcji. Gdy połączenie wraca do puli z wciąż otwartą transakcją, aiomysql ją zamyka, a MariaDB odrzuca niezatwierdzony wiersz. W naszym teście wiersz wstawiony w puli bez autocommit=True widziało 0 innych połączeń. Albo przekaż autocommit=True do create_pool, jak wyżej, albo wywołaj await conn.commit() po zapisach.
Limity, które mają znaczenie dla puli
20 jednoczesnych połączeń na użytkownika bazy. Liczy się każdy proces używający tego samego loginu. Połączenie 21 zostaje odrzucone z błędem (1226, "User '...' has exceeded the 'max_user_connections' resource (current value: 20)"). maxsize=5 trzyma bota wyraźnie poniżej limitu.
Zapytanie trwające dłużej niż 120 sekund zostaje przerwane. MariaDB kończy je błędem 1969, Query execution was interrupted (max_statement_time exceeded). W naszym teście 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ę danych. Strona Bazy danych pokazuje, ile jest zajęte. Przy 500 MB zapisy są wstrzymywane, 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 dokładne błędy, które aiomysql zwrócił w naszym teście (pochodzą z PyMySQL, którego aiomysql używa pod spodem):
pymysql.err.OperationalError (1045, "Access denied for user ..."): błędny DB_USER lub DB_PASSWORD. Po Zresetuj hasło na karcie Połączenie stare hasło przestaje działać od razu: zmień DB_PASSWORD i uruchom bota ponownie.
pymysql.err.OperationalError (2003, "Can't connect to MySQL server on ..."): zły host lub port albo połączenie spoza PlayNHost. Baza przyjmuje tylko połączenia z serwerów PlayNHost i z phpMyAdmin, więc to samo dostaniesz na własnym komputerze.
(1226, ... 'max_user_connections' ...): otwartych jest za dużo połączeń, patrz limity powyżej.
Zmienna, której brakuje w Środowisko, zatrzymuje bota przed połączeniem, z komunikatem KeyError: 'DB_HOST' lub nazwą brakującej zmiennej, bo kod czyta każdą wartość przez os.environ[...]. Dodaj ją i uruchom bota ponownie: zmienne obowiązują od następnego startu.
Instalacja discord.py i aiomysql przez pip od zera osiągnęła w naszym teście szczyt 78 MB pamięci, w kontenerze z limitem 512 MB jak nasz najmniejszy plan dla botów. Ile potrzebuje działający bot, zależy głównie od tego, na ilu serwerach jest: Ile RAM potrzebuje bot Discord?
Używamy plików cookie, aby strona działała, aby rozumieć sposób jej użycia i mierzyć nasz marketing. Możesz zaakceptować wszystko, zaakceptować tylko niezbędne pliki cookie lub wybrać według kategorii.Używamy plików cookie, aby strona działała, aby rozumieć sposób jej użycia i mierzyć nasz marketing. Tutaj są domyślnie włączone. Możesz wyłączyć to, czego nie chcesz.Przeczytaj naszą politykę prywatności