Laravel Database Expert
Priority: P1 (HIGH)
Workflow: Optimize a Slow Query
- Profile the query — Use
DB::enableQueryLog()or Laravel Debugbar. - Add missing indexes — Create a migration for join/where columns.
- Replace N+1 — Use
withCount(),withSum(), oraddSelectsubqueries. - Cache results — Apply
Cache::remember()with tags for frequently accessed data. - Split reads/writes — Configure
read/writekeys inconfig/database.php.
Cache-Aside with Tags Example
See implementation examples for cache-aside pattern with tag-based invalidation.
Implementation Guidelines
Advanced Query Builder
- Complex Joins: Prefer
joinSub($subquery, 'alias', ...)andwhereExists(fn($q) => $q->select(DB::raw(1))...)over raw SQL orwhereInfor correlated subqueries. - Subqueries: Use
addSelectwith aDB::rawsubquery to avoid N+1 issues. - Aggregates: Use
withCount(),withSum(), andwithAvg()directly via Eloquent for optimized column-based aggregation. - Raw Expressions: Always use
selectRaworwhereRawwith bindings; never use string concatenation in raw queries.
Caching Strategy (Redis/Memcached)
- Cache-Aside: Utilize
Cache::remember('key', $ttl, $closure)for frequently accessed data (e.g.,posts.all). - Redis Tagging: Group related keys using
Cache::tags(['posts', 'user:1'])for grouped invalidation. - Invalidation: Call
Cache::tags(['posts'])->flush()to clear specific subsets; never useCache::flush()globally in production.
Scalability & Infrastructure
- Read/Write Splitting: Configure 'read' and 'write' keys in
config/database.phpmysql/pgsql connections. Laravel automatically routes SELECT to read and INSERT/UPDATE/DELETE to write; no code changes needed. - Indices: Ensure correct database indexes are present on all join and aggregate columns.
Anti-Patterns
- No string SQL concatenation: Use bindings or Query Builder.
- No queries in loops: Use subqueries, joins, or aggregates.
- No
Cache::flush(): Use tags to target specific cache groups. - No direct Redis calls: Use Laravel Cache wrappers consistently.