本文目录导读:

在Laravel中处理大量数据时,内存优化非常重要,以下是使用游标查询进行内存优化的详细方案:
基础游标查询
使用 cursor() 方法
use App\Models\User;
// 传统方式(内存消耗大)
$users = User::where('status', 'active')->get(); // 一次性加载所有数据
// 游标方式(内存友好)
foreach (User::where('status', 'active')->cursor() as $user) {
// 每次只加载一条记录到内存
processUser($user);
}
带条件的高级游标
// 添加条件、排序和限制
$query = User::where('status', 'active')
->where('created_at', '>', now()->subDays(30))
->orderBy('id')
->take(100000);
foreach ($query->cursor() as $user) {
// 处理每条记录
echo $user->name;
}
批量游标处理
使用 chunkById() 分批处理
// 按ID分批处理,避免游标长时间占用连接
User::where('status', 'active')
->orderBy('id')
->chunkById(1000, function ($users) {
foreach ($users as $user) {
processUser($user);
}
});
自定义批量处理
// 手动控制批次大小
$batchSize = 500;
$lastId = 0;
do {
$users = User::where('id', '>', $lastId)
->where('status', 'active')
->orderBy('id')
->limit($batchSize)
->get();
foreach ($users as $user) {
processUser($user);
}
$lastId = $users->last()->id ?? 0;
} while ($users->count() >= $batchSize);
高级内存优化技巧
结合集合操作
// 使用cursor并配合集合筛选
User::where('active', true)
->cursor()
->filter(function ($user) {
return $user->age > 18;
})
->each(function ($user) {
// 处理符合条件的用户
});
// 使用map进行数据转换
$emails = User::cursor()
->map(function ($user) {
return [
'id' => $user->id,
'email' => $user->email
];
});
内存监控和释放
use Illuminate\Support\Facades\Log;
// 监控内存使用
$memoryStart = memory_get_usage(true);
foreach (User::cursor() as $user) {
processUser($user);
// 定期检查内存
if (memory_get_usage(true) - $memoryStart > 50000) { // 50MB
Log::warning('High memory usage detected', [
'memory_used' => memory_get_usage(true) / 1024 / 1024 . 'MB',
'processed' => $processed ?? 0
]);
// 强制垃圾回收
gc_collect_cycles();
}
$processed++;
}
$memoryEnd = memory_get_usage(true);
Log::info('Cursor processing completed', [
'processed' => $processed,
'peak_memory' => memory_get_peak_usage(true) / 1024 / 1024 . 'MB'
]);
复杂查询优化
复合索引优化
// 确保使用索引的查询
$query = User::where('status', 'active')
->where('created_at', '>=', now()->subMonth())
->select(['id', 'name', 'email']) // 只选择需要的字段
->orderBy('id')
->cursor();
// 添加索引
// php artisan make:migration add_composite_index_to_users
Schema::table('users', function (Blueprint $table) {
$table->index(['status', 'created_at', 'id']);
});
使用原始查询
use Illuminate\Support\Facades\DB;
// 使用原生SQL和游标
DB::table('users')
->select('id', 'name', 'email')
->whereRaw('status = ?', ['active'])
->orderBy('id')
->cursor();
大数据集导出
CSV导出优化
function exportUsersToCSV($filename) {
$handle = fopen($filename, 'w');
// 添加表头
fputcsv($handle, ['ID', 'Name', 'Email', 'Created At']);
User::query()
->select(['id', 'name', 'email', 'created_at'])
->where('status', 'active')
->orderBy('id')
->cursor()
->each(function ($user) use ($handle) {
fputcsv($handle, [
$user->id,
$user->name,
$user->email,
$user->created_at
]);
});
fclose($handle);
return "Export completed!";
}
内存设置和配置
设置PHP内存限制
// 在服务提供者或引导文件中
ini_set('memory_limit', '256M'); // 不要设置太大,根据需要调整
// 或者按需动态设置
class UserExportService
{
public function exportLargeDataset()
{
$oldMemoryLimit = ini_get('memory_limit');
ini_set('memory_limit', '512M');
try {
// 执行大规模操作
$this->processData();
} finally {
// 恢复原始设置
ini_set('memory_limit', $oldMemoryLimit);
}
}
}
配置连接参数
// config/database.php
'migrations' => [
'table' => 'migrations',
'update_date_on_publish' => true,
],
// 允许MySQL使用无缓冲查询
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => false,
PDO::ATTR_EMULATE_PREPARES => false,
]) : [],
完整示例
<?php
namespace App\Services;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class UserDataProcessor
{
protected int $batchSize = 1000;
protected int $processCount = 0;
protected float $startTime;
protected int $startMemory;
public function __construct()
{
$this->startTime = microtime(true);
$this->startMemory = memory_get_usage(true);
}
public function processActiveUsers(): void
{
$this->logProgress('Starting user processing');
// 使用cursor处理所有活跃用户
User::query()
->select(['id', 'name', 'email', 'attributes'])
->where('status', 'active')
->whereNotNull('email')
->orderBy('id')
->cursor()
->each(function ($user) {
$this->processSingleUser($user);
$this->trackProgress();
});
$this->logProgress('Processing completed');
}
protected function processSingleUser(User $user): void
{
// 处理单个用户逻辑
try {
// 示例:更新用户状态或发送通知
// $this->updateUserData($user);
} catch (\Exception $e) {
Log::error('User processing failed', [
'user_id' => $user->id,
'error' => $e->getMessage()
]);
}
}
protected function trackProgress(): void
{
$this->processCount++;
// 每1000条记录输出一次进度
if ($this->processCount % 1000 === 0) {
$elapsedTime = microtime(true) - $this->startTime;
$currentMemory = memory_get_usage(true);
Log::info('Progress update', [
'processed' => $this->processCount,
'time_elapsed' => round($elapsedTime, 2) . 's',
'memory_used' => round($currentMemory / 1024 / 1024, 2) . 'MB'
]);
// 触发垃圾回收
gc_collect_cycles();
}
}
protected function logProgress(string $message): void
{
Log::info($message, [
'memory_used' => round(memory_get_usage(true) / 1024 / 1024, 2) . 'MB',
'peak_memory' => round(memory_get_peak_usage(true) / 1024 / 1024, 2) . 'MB',
'time_elapsed' => round(microtime(true) - $this->startTime, 2) . 's'
]);
}
}
性能监控和优化
监控查询性能
// 启用查询日志
DB::enableQueryLog();
// 执行查询
$users = User::where('status', 'active')->cursor()->take(10);
// 查看查询日志
dump(DB::getQueryLog());
使用 EXPLAIN 优化
// 检查查询计划
$explainResult = DB::table('users')
->select('id', 'name')
->where('status', 'active')
->orderBy('id')
->explain();
dd($explainResult);
最佳实践建议
- 使用
select()只选择需要的字段 - 适当调整批次大小(1000-2000通常比较好)
- 监控内存使用情况
- 设置合理的数据库索引
- 避免在循环中进行复杂操作
- 使用日志记录处理进度
- 考虑使用队列异步处理
这些方法可以显著降低内存使用,特别是在处理几十万到数百万条记录时,选择合适的方法取决于你的具体场景和数据量。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播