You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

discord.py:如何提升服务器最早/最晚创建用户查询命令的处理速度

Hey there! Let's break down why your commands are running slow and fix them step by step. The core issue here is the O(n²) time complexity of your current code—for every member in the server, you're looping through all members again to calculate their position. On a 2000-user server, that's 4 million operations, which is way too slow. Plus, since Discord.py runs on a single-threaded event loop, this blocking operation stops your bot from responding to anything else until it's done.

1. Replace the O(n²) Position Calculation with a Single Sort

Instead of calculating each member's position by looping through everyone every time, just sort the member list once by created_at, then grab the page you need directly. This drops your time complexity to O(n log n), which is way faster even for large servers.

2. Offload Heavy Work to a Thread (Avoid Blocking the Bot)

Even with sorting, if your server grows to 10k+ users, the sort might still block the event loop for a moment. Using asyncio.to_thread (available in Python 3.9+) lets you run the sorting in a separate thread, keeping your bot responsive to other commands while the work happens.

3. Cache the Sorted List (Avoid Repeating Work)

If users run oldest or newest multiple times in a short period, there's no need to re-sort the list every time. You can cache the sorted results with a timeout (like 5 minutes) so subsequent calls use the cached data instead of reprocessing everything.

Here's the optimized version of your code incorporating all these fixes:

import asyncio
from datetime import datetime, timedelta
import discord
from discord.ext import commands

class UserSort(commands.Cog):
    def __init__(self, client):
        self.client = client
        self._cache = {
            "oldest": {"data": None, "expires": None},
            "newest": {"data": None, "expires": None}
        }

    async def _get_sorted_members(self, guild, sort_type):
        # Check if we have a valid cached result
        cache_entry = self._cache[sort_type.lower()]
        now = datetime.utcnow()
        if cache_entry["data"] is not None and cache_entry["expires"] > now:
            return cache_entry["data"]
        
        # Offload sorting to a thread to avoid blocking the event loop
        def sort_members():
            # Filter out members with no created_at (should be rare)
            valid_members = [m for m in guild.members if m.created_at is not None]
            # Sort based on type
            if sort_type == "Oldest":
                return sorted(valid_members, key=lambda x: x.created_at)
            else:
                return sorted(valid_members, key=lambda x: x.created_at, reverse=True)
        
        sorted_members = await asyncio.to_thread(sort_members)
        
        # Update cache with 5-minute expiration
        cache_entry["data"] = sorted_members
        cache_entry["expires"] = now + timedelta(minutes=5)
        return sorted_members

    @commands.command()
    async def oldest(self, ctx, page: int = 1):
        await self._display_sorted_page(ctx, page, "Oldest")

    @commands.command()
    async def newest(self, ctx, page: int = 1):
        await self._display_sorted_page(ctx, page, "Newest")

    async def _display_sorted_page(self, ctx, page, sort_type):
        page = abs(page)
        max_page = 99
        if page > max_page:
            page = max_page
        
        # Get sorted members (from cache or fresh sort)
        sorted_members = await self._get_sorted_members(ctx.guild, sort_type)
        total_members = len(sorted_members)
        
        # Calculate page boundaries
        per_page = 10
        start_idx = (page - 1) * per_page
        end_idx = start_idx + per_page
        page_members = sorted_members[start_idx:end_idx]
        
        # Build the output string
        output_string = ""
        for idx, member in enumerate(page_members, start=start_idx + 1):
            output_string += f"**{idx}** - {member} - {member.created_at.strftime('%d/%m/%Y')}\n"
        
        # Handle empty page (if page is beyond total members)
        if not output_string:
            output_string = "No members found on this page!"
        
        # Create embed
        em = discord.Embed(color=self.client.Blue)
        em.add_field(
            name=f"{sort_type} accounts in **{ctx.guild.name}**",
            value=output_string,
            inline=False
        )
        em.set_footer(text=f"Page: {page} | Total members: {total_members}")
        em.timestamp = datetime.utcnow()
        await ctx.send(embed=em)

# Don't forget to add the cog to your bot
# client.add_cog(UserSort(client))

Let's go over what's changed:

  • Sorting once instead of per member: We sort the list once and then slice it for the desired page, which is way more efficient.
  • Threaded sorting: asyncio.to_thread runs the sorting in a background thread, so your bot can still respond to other commands while this work is done.
  • Caching: The sorted list is stored for 5 minutes, so repeated calls don't re-sort the entire member list every time.
  • Cleaner page handling: Using list slicing (sorted_members[start_idx:end_idx]) simplifies grabbing the right page of members instead of tracking 10 separate variables.

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 19:04:05