PHPMailer批量发送邮件时服务器卡顿问题求助
Hey there! Let's figure out why your bulk email sender is grinding to a halt after around 100 messages and get it running smoothly for all 6000 recipients.
The server choking without PHP errors is almost always due to gradual resource exhaustion rather than a single crash. Here's what's likely going wrong, and how to fix it:
Core Causes to Blame
- Resource Leaks: If you're creating a new PHPMailer instance or database connection inside every loop iteration, those unused objects pile up in memory over time, eventually draining server resources.
- Unclosed SMTP Connections: Each email send might leave an SMTP connection hanging in a
TIME_WAITstate, eating up system ports until there are none left for new connections. - Memory Bloat: Loading all 6000 email addresses into memory at once can strain PHP's memory limits, even if it doesn't trigger an immediate error.
Step-by-Step Fixes
1. Reuse Connections & Objects (Don't Recreate Them)
The biggest mistake is initializing PHPMailer and database connections inside your loop. Instead, set them up once, then reset only the variable parts (like recipients) each time:
// Initialize database connection ONCE before the loop $db = new mysqli('your-db-host', 'user', 'password', 'db-name'); if ($db->connect_error) { die("DB Connection Failed: " . $db->connect_error); } // Initialize PHPMailer ONCE before the loop require 'PHPMailerAutoload.php'; $mail = new PHPMailer; $mail->isSMTP(); $mail->Host = 'your-smtp-host'; $mail->SMTPAuth = true; $mail->Username = 'your-email@domain.com'; $mail->Password = 'your-smtp-password'; $mail->SMTPSecure = 'tls'; // or ssl if needed $mail->Port = 587; $mail->setFrom('sender@domain.com', 'Your Brand'); // Fetch recipients in batches (instead of all at once) $batchSize = 50; $offset = 0; while (true) { // Get a small batch of emails to avoid memory overload $result = $db->query("SELECT email FROM your-recipients-table LIMIT $batchSize OFFSET $offset"); if ($result->num_rows === 0) break; // Exit loop when no more emails while ($row = $result->fetch_assoc()) { // Reset only the dynamic parts for each email $mail->clearAddresses(); $mail->addAddress($row['email']); $mail->Subject = 'Your Custom Subject'; $mail->Body = 'Your Email Content Goes Here'; // Send and log results if (!$mail->send()) { error_log("Failed to send to {$row['email']}: " . $mail->ErrorInfo); } else { error_log("Successfully sent to {$row['email']}"); } // Add your delay sleep(20); } // Free up memory from the current batch $result->free(); $offset += $batchSize; } // Clean up resources when done $db->close(); $mail->smtpClose(); // Properly close the SMTP connection
2. Tweak System & PHP Resource Limits
- Increase PHP Memory: Add this at the top of your script to give PHP more breathing room:
ini_set('memory_limit', '256M'); // Adjust based on your server's capacity - Raise Open File Limits: CentOS defaults might restrict the number of open connections. Run
ulimit -nto check (default is often 1024). Temporarily raise it withulimit -n 4096, or set it permanently by editing/etc/security/limits.conf:* soft nofile 4096 * hard nofile 8192
3. Add Memory Monitoring
To confirm resource leaks, add a log line inside your loop to track memory usage:
error_log("Current Memory Usage: " . memory_get_usage(true) . " bytes");
If you see the number climbing steadily, you know there's a leak to track down (like unused variables not being cleared). You can force garbage collection with gc_collect_cycles() if needed, but fixing the object reuse is a better long-term fix.
4. Split the Job with Cron (Optional)
For extra reliability, split the 6000 emails into smaller chunks and use a cron job to run them at intervals. For example, create a script that sends 50 emails, then set cron to run it every 20 minutes. This avoids having a single PHP process running for hours straight.
内容的提问来源于stack exchange,提问作者Charith Prabhagya

