1月の仮締処理が重くて困っていませんか?Laravelの決算システムで処理速度を5倍改善した最適化手法を、実際のプロジェクト事例とともに詳しく解説します。
こんな悩みありませんか?
- 1月の仮締処理で数時間かかってしまい、業務が止まってしまう
- 年度末の決算作業でシステムが重くなり、残業が増えている
- データ量が増えるにつれて処理速度が遅くなってきた
- 複数の帳票出力で画面が固まってしまう
神奈川でWeb制作を手がけるFivenine Designでも、決算システムの処理速度に関するご相談を多数いただきます。特に年度末の1月は、1年間のデータを集計する仮締処理で多くの企業様が課題を抱えています。
あるクライアント様での課題:処理時間3時間が15分に短縮
昨年、製造業のお客様から「1月の仮締処理に3時間もかかってしまい、経理業務が深夜まで続いてしまう」とご相談いただきました。
課題の詳細分析
調査した結果、以下の問題が判明しました:
- N+1問題:取引先情報を1件ずつクエリで取得
- インデックス不足:日付や取引先IDにインデックスがない
- メモリ不足:大量データを一度にメモリに展開
- 不要なデータ取得:使わないカラムまで全て取得
解決策1:クエリ最適化とEagerローディング
最も効果的だったのが、N+1問題の解決でした。
// 改善前:N+1問題が発生
$transactions = Transaction::where('created_at', '>=', $startDate)
->where('created_at', '<=', $endDate)
->get();
foreach ($transactions as $transaction) {
// 1件ずつクエリが実行される
echo $transaction->company->name;
}
// 改善後:Eagerローディングで一括取得
$transactions = Transaction::with(['company', 'items'])
->where('created_at', '>=', $startDate)
->where('created_at', '<=', $endDate)
->select(['id', 'company_id', 'amount', 'created_at']) // 必要なカラムのみ
->get();
この変更だけで処理時間が60%短縮されました。
解決策2:データベースインデックスの最適化
決算処理でよく使用される条件にインデックスを追加しました。
// マイグレーションファイル
Schema::table('transactions', function (Blueprint $table) {
// 複合インデックスで検索速度向上
$table->index(['created_at', 'company_id']);
$table->index(['fiscal_year', 'status']);
$table->index(['updated_at']); // 差分処理用
});
解決策3:チャンク処理とキュー活用
大量データの処理をチャンクに分割し、バックグラウンドで実行するよう改修しました。
class ProcessYearEndClosing implements ShouldQueue
{
public function handle()
{
$chunkSize = 1000;
Transaction::whereYear('created_at', $this->fiscalYear)
->chunk($chunkSize, function ($transactions) {
$this->processTransactionChunk($transactions);
});
}
private function processTransactionChunk($transactions)
{
$summaryData = [];
foreach ($transactions as $transaction) {
// メモリ効率的な集計処理
$key = $transaction->company_id . '_' . $transaction->category;
$summaryData[$key] = ($summaryData[$key] ?? 0) + $transaction->amount;
}
// バッチでまとめて更新
$this->bulkUpdateSummary($summaryData);
}
}
解決策4:Redis活用による集計データキャッシュ
頻繁にアクセスされる集計データをRedisにキャッシュしました。
class FinancialSummaryService
{
public function getMonthlySummary($year, $month)
{
$cacheKey = "monthly_summary_{$year}_{$month}";
return Cache::remember($cacheKey, 3600, function () use ($year, $month) {
return DB::table('transactions')
->whereYear('created_at', $year)
->whereMonth('created_at', $month)
->selectRaw('SUM(amount) as total, category')
->groupBy('category')
->get();
});
}
}
よくある失敗パターンと注意点
実装時に注意したいポイントをご紹介します:
失敗例1:過度なEagerローディング
// NG:不要なリレーションまで取得してメモリを圧迫
$transactions = Transaction::with([
'company', 'items', 'user', 'attachments', 'logs'
])->get();
// OK:必要なリレーションのみ取得
$transactions = Transaction::with(['company:id,name'])->get();
失敗例2:インデックスの重複
単一カラムのインデックスと複合インデックスを重複して作成してしまい、かえって更新処理が遅くなるケースがありました。
結果:劇的な改善効果
これらの最適化により、以下の成果を実現できました:
- 処理時間:3時間 → 15分(12倍高速化)
- メモリ使用量:2GB → 300MB(約85%削減)
- CPU使用率:常時80% → 平均20%
- 経理担当者の残業時間:月20時間削減
「月末の仮締処理が15分で完了するようになり、経理業務の負担が大幅に軽減されました。これで安心して決算作業に集中できます」とお客様からもご満足いただけました。
実装時のベストプラクティス
段階的な最適化
- 現状の計測:Laravel Debugbarやクエリログで問題箇所を特定
- 優先順位付け:効果の高い問題から順番に解決
- テスト環境での検証:本番同等のデータ量での動作確認
- 段階的リリース:影響範囲を限定して順次適用
モニタリング体制
// 処理時間の監視
class PerformanceMiddleware
{
public function handle($request, Closure $next)
{
$start = microtime(true);
$response = $next($request);
$duration = microtime(true) - $start;
if ($duration > 5.0) { // 5秒以上の処理をログ出力
Log::warning('Slow query detected', [
'url' => $request->url(),
'duration' => $duration
]);
}
return $response;
}
}
まず何から始めるべきか
決算システムの処理速度改善を検討されている方は、以下の順序で進めることをおすすめします:
- 現状分析:Laravel Debugbarで遅いクエリを特定
- インデックス追加:よく使用される検索条件にインデックス設定
- N+1問題の解決:withメソッドでEagerローディング実装
- チャンク処理導入:大量データ処理の分割実行
年度末の決算処理でお困りの企業様は、ぜひお気軽にご相談ください。20年以上の開発実績を活かし、お客様の業務効率化をサポートいたします。