2.2 KiB
2.2 KiB
Query Monitoring Guide
Enable Query Logging
Development:
const prisma = new PrismaClient({
log: [
{ emit: 'event', level: 'query' },
{ emit: 'stdout', level: 'error' },
{ emit: 'stdout', level: 'warn' }
]
})
prisma.$on('query', (e) => {
console.log('Query: ' + e.query)
console.log('Duration: ' + e.duration + 'ms')
})
Production (structured logging):
const prisma = new PrismaClient({
log: [{ emit: 'event', level: 'query' }]
})
prisma.$on('query', (e) => {
if (e.duration > 1000) {
logger.warn('Slow query detected', {
query: e.query,
duration: e.duration,
params: e.params
})
}
})
Analyzing Slow Queries
Identify Patterns:
- Queries without WHERE clause on large tables (full table scans)
- Complex JOINs without indexes on foreign keys
- ORDER BY on unindexed fields
- Missing LIMIT on large result sets
Use Database EXPLAIN:
await prisma.$queryRaw`EXPLAIN ANALYZE
SELECT * FROM "User"
WHERE status = 'active'
ORDER BY "createdAt" DESC
LIMIT 20
`
Look for:
- "Seq Scan" (sequential scan) - needs index
- "Index Scan" - good
- High execution time relative to query complexity
Common Query Anti-Patterns
N+1 Problem:
const users = await prisma.user.findMany()
for (const user of users) {
const orders = await prisma.order.findMany({
where: { userId: user.id }
})
}
Solution - Use include:
const users = await prisma.user.findMany({
include: {
orders: true
}
})
Over-fetching:
const users = await prisma.user.findMany()
Fetches ALL fields for ALL users.
Solution - Select needed fields:
const users = await prisma.user.findMany({
select: {
id: true,
email: true,
name: true
}
})
Offset Pagination on Large Datasets:
await prisma.user.findMany({
skip: 50000,
take: 20
})
Database must scan and skip 50,000 rows.
Solution - Cursor pagination:
await prisma.user.findMany({
take: 20,
cursor: { id: lastSeenId },
skip: 1
})
Constant time regardless of page depth.