| name | performance-optimization |
| description | Drupal performance optimization guide covering Redis, caching, database optimization, and asset management. |
Performance Optimization Skill
Comprehensive guide for optimizing Drupal performance.
Stack Overview
| Component | Technology | Purpose |
|---|
| Database | PostgreSQL or MySQL/MariaDB | Primary data store |
| Cache | Redis (or Memcached) | Object/session cache |
| Search | Apache Solr (or Elasticsearch) | Full-text search |
| Web Server | Apache or Nginx | HTTP serving |
| PHP | 8.2+ | Application runtime |
Quick Diagnostics
./vendor/bin/drush core:status
./vendor/bin/drush cache:list
redis-cli INFO memory | grep used_memory_human
redis-cli INFO stats | grep -E "keyspace_hits|keyspace_misses"
./vendor/bin/drush sql:query "SELECT pg_size_pretty(pg_database_size(current_database()))"
./vendor/bin/drush sql:query "SELECT table_schema AS db, ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS 'Size (MB)' FROM information_schema.tables WHERE table_schema = DATABASE() GROUP BY table_schema"
1. Redis Caching
Configuration Location
sites/default/settings.redis.php (or inline in settings.php)
Recommended Settings
$settings['redis.connection']['interface'] = 'PhpRedis';
$settings['redis.connection']['host'] = '127.0.0.1';
$settings['redis.connection']['port'] = '6379';
$settings['cache']['default'] = 'cache.backend.redis';
$settings['cache']['bins']['render'] = 'cache.backend.redis';
$settings['cache']['bins']['dynamic_page_cache'] = 'cache.backend.redis';
$settings['cache']['bins']['page'] = 'cache.backend.redis';
$settings['cache']['bins']['bootstrap'] = 'cache.backend.redis';
$settings['cache']['bins']['config'] = 'cache.backend.redis';
$settings['cache']['bins']['discovery'] = 'cache.backend.redis';
$settings['cache']['bins']['migrate'] = 'cache.backend.database';
Redis Memory Optimization
redis-cli INFO memory
redis-cli MONITOR
redis-cli FLUSHDB
Hit Rate Analysis
redis-cli INFO stats | grep -E "keyspace_hits|keyspace_misses"
2. Drupal Caching Layers
Cache Hierarchy
- Page Cache - Full page for anonymous users
- Dynamic Page Cache - Partial pages for authenticated users
- Render Cache - Individual render elements
- Internal Cache - Discovery, config, bootstrap
Views Caching
display:
cache:
type: tag
type: time
options:
results_lifespan: 3600
output_lifespan: 3600
display:
cache:
type: time
options:
results_lifespan: 86400
output_lifespan: 86400
Block Caching
public function getCacheMaxAge() {
return 3600;
}
public function getCacheContexts() {
return ['user.roles', 'url.path'];
}
public function getCacheTags() {
return ['node_list:article'];
}
BigPipe
./vendor/bin/drush en big_pipe -y
3. Database Optimization
PostgreSQL Tuning
SELECT query, calls, total_time, mean_time
FROM pg_stat_statements
ORDER BY total_time DESC
LIMIT 10;
SELECT relname, pg_size_pretty(pg_total_relation_size(relid))
FROM pg_catalog.pg_statio_user_tables
ORDER BY pg_total_relation_size(relid) DESC
LIMIT 20;
SELECT indexrelname, idx_scan, idx_tup_read, idx_tup_fetch
FROM pg_stat_user_indexes
ORDER BY idx_scan DESC;
SELECT indexrelname, idx_scan
FROM pg_stat_user_indexes
WHERE idx_scan = 0;
VACUUM ANALYZE;
MySQL/MariaDB Tuning
SELECT table_name,
ROUND(((data_length + index_length) / 1024 / 1024), 2) AS 'Size (MB)'
FROM information_schema.tables
WHERE table_schema = DATABASE()
ORDER BY (data_length + index_length) DESC
LIMIT 20;
SELECT * FROM sys.schema_unused_indexes
WHERE object_schema = DATABASE();
SELECT * FROM sys.statements_with_full_table_scans
WHERE db = DATABASE()
ORDER BY no_index_used_count DESC
LIMIT 10;
OPTIMIZE TABLE cache_default, cache_render, cache_page;
Query Optimization
foreach ($nids as $nid) {
$node = Node::load($nid);
}
$nodes = Node::loadMultiple($nids);
$nodes = $storage->loadMultiple($nids);
$query = $storage->getQuery()
->condition('type', 'article')
->condition('status', 1)
->range(0, 50);
$nids = $query->execute();
Entity Query Best Practices
$query = \Drupal::entityQuery('node')
->accessCheck(TRUE)
->condition('type', 'article')
->condition('status', 1)
->sort('created', 'DESC')
->range(0, 10);
$nodes = Node::loadMultiple($query->execute());
$build['#cache']['tags'] = Cache::mergeTags(
$build['#cache']['tags'] ?? [],
['node_list:article']
);
4. Asset Optimization
CSS/JS Aggregation
$config['system.performance']['css']['preprocess'] = TRUE;
$config['system.performance']['js']['preprocess'] = TRUE;
Image Optimization
responsive_image_style:
id: wide
breakpoints:
- media: '(min-width: 1200px)'
image_style: wide_desktop
- media: '(min-width: 768px)'
image_style: wide_tablet
- media: ''
image_style: wide_mobile
Lazy Loading
{# In templates #}
<img loading="lazy" src="{{ image_url }}" alt="{{ alt }}">
{# Or via preprocess #}
$variables['attributes']['loading'] = 'lazy';
5. Common Performance Issues
Issue: Slow Views
Diagnosis:
./vendor/bin/drush views:analyze
Solutions:
- Add caching to view display
- Reduce fields to minimum needed
- Add appropriate filters
- Use pager or limit results
- Consider using Search API instead
Issue: Heavy Preprocess Functions
Diagnosis:
grep -r "entityTypeManager\|database" themes/custom/*/\*.theme
Solutions:
- Move logic to services
- Cache expensive computations
- Use lazy builders
Issue: Too Many Modules
Diagnosis:
./vendor/bin/drush pm:list --status=enabled | wc -l
Solutions:
- Disable unused modules
- Combine functionality
- Use lazy loading where possible
Issue: Large Sessions
Diagnosis:
redis-cli --scan --pattern '*session*' | head -10
Solutions:
- Reduce session data
- Clean old sessions
- Use Redis for sessions
6. Monitoring & Profiling
Built-in Tools
./vendor/bin/drush config:set system.logging error_level verbose -y
./vendor/bin/drush watchdog:show --severity=warning
Database Query Logging
$settings['container_yamls'][] = DRUPAL_ROOT . '/sites/development.services.yml';
$config['system.logging']['error_level'] = 'verbose';
Memory Profiling
php -i | grep memory_limit
./vendor/bin/drush php:eval "echo 'Memory: ' . round(memory_get_peak_usage() / 1024 / 1024, 2) . 'MB';"
7. Performance Checklist
Before Deployment
Regular Maintenance
Commands
./vendor/bin/drush cr
./vendor/bin/drush cache:rebuild router
./vendor/bin/drush sql:query "VACUUM ANALYZE"
./vendor/bin/drush watchdog:delete all