196 lines
5.2 KiB
PHP
196 lines
5.2 KiB
PHP
<?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();
|
|
}
|
|
}
|