You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

为何我的discord.py机器人代码会导致aiosqlite数据库锁定?

Why Your aiosqlite Database Is Locked in discord.py

Let's break down the root causes of the OperationalError: database is locked error in your code, then fix it step by step.

Key Issues Causing the Lock

1. Repeated Database Connections

Every time you call addwarn or clearwarn, you're creating a new database connection with self.bot.db = await aiosqlite.connect("database.db"). SQLite is a file-based database that only allows one active write transaction at a time. Multiple concurrent connections fighting for write access will trigger a lock error.

2. Unmanaged Connections

You never close old connections when creating new ones. These lingering connections hold onto database resources, making lock conflicts more likely.

3. Nested Write Operations

When a user hits 3 warnings, your code calls ban_member, which in turn calls clearwarn. This means you're trying to perform a write (insert warn) and another write (delete warns) from two different connections at the same time—this is a guaranteed way to hit a lock.

Fixes to Resolve the Lock

Step 1: Use a Single Global Connection

Initialize your database connection once when the bot starts, then reuse it for all commands. This eliminates connection conflicts entirely.

Add this to your bot's on_ready event:

async def on_ready(self):
    # Initialize database connection once at startup
    self.bot.db = await aiosqlite.connect("database.db")
    print("Connected to database successfully!")

    # Optional: Create the Warns table if it doesn't exist
    async with self.bot.db.cursor() as cursor:
        await cursor.execute('''
            CREATE TABLE IF NOT EXISTS Warns (
                User INTEGER,
                Reason TEXT,
                Time INTEGER,
                Server INTEGER,
                PRIMARY KEY(User, Time, Server)
            )
        ''')
    await self.bot.db.commit()

Don't forget to close the connection when the bot shuts down:

async def on_disconnect(self):
    await self.bot.db.close()
    print("Database connection closed.")

Step 2: Rewrite Commands to Use the Global Connection

Remove all aiosqlite.connect calls from your commands and use the pre-existing self.bot.db connection. Also, fix the cursor usage (your original code tried to fetch results outside the async with block, which would fail once the cursor is closed).

Updated addwarn Command

@commands.command()
async def addwarn(self, ctx, member, *, reason):
    # Insert the new warning
    async with self.bot.db.cursor() as cursor:
        await cursor.execute(
            "INSERT OR IGNORE INTO Warns (User, Reason, Time, Server) VALUES (?,?,?,?)",
            (member.id, reason, int(datetime.now().timestamp()), member.guild.id)
        )
    
    # Fetch existing warnings (keep fetchall inside the async with block)
    async with self.bot.db.cursor() as cursor:
        await cursor.execute(
            'SELECT Reason, Time FROM Warns WHERE User = ? AND Server = ?',
            (member.id, member.guild.id)
        )
        data = await cursor.fetchall()

    if data:
        warnnum = len(data)  # Simplify counting with len() instead of looping
        if warnnum >= 3:
            await self.ban_member(
                ctx, member=member,
                reason='User has exceeded their warnings limit, and has been banned as a result.'
            )
        # Check if user joined recently
        if member.joined_at >= datetime.now(timezone.utc) - timedelta(hours=3):
            await self.ban_member(
                ctx, member=member,
                reason='User has gained an infraction within a short duration of joining.'
            )
    
    await self.bot.db.commit()

Updated clearwarn Function

async def clearwarn(self, ctx, member: discord.Member):
    async with self.bot.db.cursor() as cursor:
        await cursor.execute(
            'DELETE FROM Warns WHERE User = ? AND Server = ?',
            (member.id, ctx.guild.id)
        )
    await self.bot.db.commit()

Step 3: Simplify Transaction Logic

SQLite works best with short, focused transactions. By using a single connection, all your write operations (insert, delete) will be queued properly under one connection, avoiding lock conflicts.

Additional Notes

  • Avoid long-running database operations in commands—keep transactions quick to minimize lock time.
  • If you ever need to run multiple related operations (like inserting a warn and checking counts), you can wrap them in a single transaction block for consistency:
    async with self.bot.db.transaction():
        # Run multiple cursor operations here
    

内容的提问来源于stack exchange,提问作者Angel

火山引擎 最新活动