This commit is contained in:
Doar Mihai
2026-04-03 20:55:51 +03:00
parent a8a48f3f6f
commit 31e666ef33
7 changed files with 586 additions and 68 deletions
+195
View File
@@ -0,0 +1,195 @@
<?php
namespace App\Services;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\DB;
class QueryOptimizationService
{
/**
* Add eager loading constraints to prevent N+1 queries
*/
public function eagerLoadJobRelations(Builder $query): Builder
{
return $query->with([
'source:id,name',
'category:id,name,slug,icon',
'jobLocation:id,city,state,country',
]);
}
/**
* Optimize job listing query
*/
public function optimizeJobListingQuery(Builder $query): Builder
{
return $query
->select([
'id',
'title',
'company',
'location',
'job_type',
'experience_level',
'salary_min',
'salary_max',
'salary_currency',
'remote_allowed',
'posted_at',
'job_source_id',
'job_category_id',
'job_location_id',
'created_at',
])
->with([
'source:id,name',
'category:id,name,slug,icon',
'jobLocation:id,city,state,country',
]);
}
/**
* Analyze slow queries
*/
public function analyzeSlowQueries(int $threshold = 1000): array
{
// Enable query logging
DB::enableQueryLog();
// Get queries that took longer than threshold (in milliseconds)
$queries = DB::getQueryLog();
$slowQueries = array_filter($queries, function ($query) use ($threshold) {
return ($query['time'] ?? 0) > $threshold;
});
return array_map(function ($query) {
return [
'query' => $query['query'],
'bindings' => $query['bindings'],
'time' => $query['time'] . 'ms',
];
}, $slowQueries);
}
/**
* Get query explain plan
*/
public function explainQuery(string $sql, array $bindings = []): array
{
$explained = DB::select('EXPLAIN ' . $sql, $bindings);
return array_map(function ($row) {
return (array) $row;
}, $explained);
}
/**
* Optimize table (defragment and rebuild indexes)
*/
public function optimizeTable(string $table): void
{
DB::statement("OPTIMIZE TABLE {$table}");
}
/**
* Analyze table statistics for query optimizer
*/
public function analyzeTable(string $table): void
{
DB::statement("ANALYZE TABLE {$table}");
}
/**
* Get table size and index statistics
*/
public function getTableStats(string $table): array
{
$stats = DB::select("
SELECT
table_name,
ROUND(((data_length + index_length) / 1024 / 1024), 2) AS size_mb,
table_rows,
ROUND((data_length / 1024 / 1024), 2) AS data_size_mb,
ROUND((index_length / 1024 / 1024), 2) AS index_size_mb
FROM information_schema.TABLES
WHERE table_schema = DATABASE()
AND table_name = ?
", [$table]);
return $stats ? (array) $stats[0] : [];
}
/**
* Get index usage statistics
*/
public function getIndexStats(string $table): array
{
$stats = DB::select("
SELECT
index_name,
column_name,
seq_in_index,
cardinality,
index_type
FROM information_schema.STATISTICS
WHERE table_schema = DATABASE()
AND table_name = ?
ORDER BY index_name, seq_in_index
", [$table]);
return array_map(function ($row) {
return (array) $row;
}, $stats);
}
/**
* Check for missing indexes based on slow queries
*/
public function suggestIndexes(string $table): array
{
$suggestions = [];
// Get columns that are frequently used in WHERE clauses without indexes
$unindexedColumns = DB::select("
SELECT DISTINCT column_name
FROM information_schema.COLUMNS
WHERE table_schema = DATABASE()
AND table_name = ?
AND column_name NOT IN (
SELECT column_name
FROM information_schema.STATISTICS
WHERE table_schema = DATABASE()
AND table_name = ?
)
", [$table, $table]);
foreach ($unindexedColumns as $column) {
$suggestions[] = [
'type' => 'missing_index',
'table' => $table,
'column' => $column->column_name,
'suggestion' => "Consider adding an index on {$column->column_name}",
];
}
return $suggestions;
}
/**
* Chunk large queries to reduce memory usage
*/
public function chunkQuery(Builder $query, int $chunkSize, callable $callback): void
{
$query->chunk($chunkSize, $callback);
}
/**
* Use cursor for memory-efficient iteration
*/
public function cursorQuery(Builder $query): \Illuminate\Support\LazyCollection
{
return $query->cursor();
}
}