Database Indexing
Indexing strategies and best practices for Lager Guru database.
Overview
Proper indexing is crucial for database performance. This document outlines indexing strategies for the Lager Guru database schema.
Index Types
B-Tree Indexes (Default)
Standard indexes for most use cases.
sql
CREATE INDEX idx_orders_tenant_status ON orders(tenant_id, status);GIN Indexes
For JSONB columns and full-text search.
sql
CREATE INDEX idx_inventory_metadata_gin ON inventory_items USING GIN(metadata);GiST Indexes
For geometric data and range queries.
sql
CREATE INDEX idx_zones_coordinates_gist ON zones USING GIST(coordinates);Core Indexes
Tenant Isolation
All tenant-scoped tables should have indexes on tenant_id:
sql
-- Orders table
CREATE INDEX idx_orders_tenant_id ON orders(tenant_id);
CREATE INDEX idx_orders_tenant_status ON orders(tenant_id, status);
CREATE INDEX idx_orders_tenant_created ON orders(tenant_id, created_at DESC);
-- Inventory table
CREATE INDEX idx_inventory_tenant_id ON inventory_items(tenant_id);
CREATE INDEX idx_inventory_tenant_sku ON inventory_items(tenant_id, sku);
CREATE INDEX idx_inventory_tenant_location ON inventory_items(tenant_id, location);Foreign Key Indexes
Index all foreign key columns:
sql
-- Order items
CREATE INDEX idx_order_items_order_id ON order_items(order_id);
CREATE INDEX idx_order_items_item_id ON order_items(item_id);
-- Tasks
CREATE INDEX idx_tasks_worker_id ON tasks(worker_id);
CREATE INDEX idx_tasks_tenant_id ON tasks(tenant_id, status);Lookup Indexes
Index frequently queried columns:
sql
-- Users
CREATE UNIQUE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_tenant_id ON users(tenant_id);
-- Inventory
CREATE INDEX idx_inventory_sku ON inventory_items(sku);
CREATE INDEX idx_inventory_location ON inventory_items(location);
-- Orders
CREATE UNIQUE INDEX idx_orders_order_number ON orders(order_number);Composite Indexes
Multi-Column Indexes
Create composite indexes for common query patterns:
sql
-- Tenant + Status queries
CREATE INDEX idx_tasks_tenant_status ON tasks(tenant_id, status);
-- Tenant + Date range queries
CREATE INDEX idx_orders_tenant_date ON orders(tenant_id, created_at DESC);
-- Tenant + Location queries
CREATE INDEX idx_inventory_tenant_location ON inventory_items(tenant_id, location);Covering Indexes
Include frequently selected columns in indexes:
sql
CREATE INDEX idx_orders_covering ON orders(tenant_id, status)
INCLUDE (order_number, created_at, customer_info);JSONB Indexing
GIN Indexes for JSONB
Index JSONB columns for efficient queries:
sql
-- Metadata queries
CREATE INDEX idx_inventory_metadata_gin ON inventory_items USING GIN(metadata);
-- Settings queries
CREATE INDEX idx_tenants_settings_gin ON tenants USING GIN(settings);JSONB Path Indexes
Index specific JSONB paths:
sql
-- Index specific JSONB field
CREATE INDEX idx_shipments_route_gin ON shipments USING GIN(route);
-- Query example
SELECT * FROM shipments WHERE route @> '{"status": "in_transit"}'::jsonb;Performance Monitoring
Index Usage Statistics
Monitor index usage:
sql
SELECT
schemaname,
tablename,
indexname,
idx_scan,
idx_tup_read,
idx_tup_fetch
FROM pg_stat_user_indexes
WHERE schemaname = 'public'
ORDER BY idx_scan DESC;Unused Indexes
Identify unused indexes:
sql
SELECT
schemaname,
tablename,
indexname,
idx_scan
FROM pg_stat_user_indexes
WHERE schemaname = 'public'
AND idx_scan = 0
AND indexname NOT LIKE '%_pkey'
ORDER BY pg_relation_size(indexrelid) DESC;Best Practices
Index Creation
- Index Foreign Keys: Always index foreign key columns
- Index Tenant Columns: Index
tenant_idin all tenant-scoped tables - Composite Indexes: Create for common multi-column queries
- Covering Indexes: Use INCLUDE for frequently selected columns
- JSONB Indexes: Use GIN indexes for JSONB columns
Index Maintenance
- Regular Monitoring: Check index usage statistics
- Remove Unused: Drop indexes that are never used
- Rebuild When Needed: REINDEX large tables periodically
- Analyze Tables: Run ANALYZE after bulk operations
Query Optimization
- Use Indexes: Write queries that can use indexes
- Avoid Functions: Don't use functions on indexed columns
- Limit Results: Use LIMIT to reduce index scan size
- Explain Plans: Use EXPLAIN ANALYZE to verify index usage
Example: Complete Index Strategy
sql
-- Orders table indexes
CREATE INDEX idx_orders_tenant_id ON orders(tenant_id);
CREATE INDEX idx_orders_tenant_status ON orders(tenant_id, status);
CREATE INDEX idx_orders_tenant_created ON orders(tenant_id, created_at DESC);
CREATE UNIQUE INDEX idx_orders_order_number ON orders(order_number);
-- Inventory table indexes
CREATE INDEX idx_inventory_tenant_id ON inventory_items(tenant_id);
CREATE INDEX idx_inventory_tenant_sku ON inventory_items(tenant_id, sku);
CREATE INDEX idx_inventory_tenant_location ON inventory_items(tenant_id, location);
CREATE INDEX idx_inventory_metadata_gin ON inventory_items USING GIN(metadata);
-- Tasks table indexes
CREATE INDEX idx_tasks_tenant_id ON tasks(tenant_id);
CREATE INDEX idx_tasks_tenant_status ON tasks(tenant_id, status);
CREATE INDEX idx_tasks_worker_id ON tasks(worker_id);
CREATE INDEX idx_tasks_tenant_created ON tasks(tenant_id, created_at DESC);Troubleshooting
Slow Queries
- Check if indexes exist for query columns
- Verify index usage with EXPLAIN ANALYZE
- Consider composite indexes for multi-column queries
- Review query patterns and adjust indexes
Index Bloat
- Monitor index sizes
- Rebuild indexes when needed:
REINDEX INDEX index_name; - Consider partial indexes for filtered queries
- Use VACUUM regularly
Related Documentation
- Database Schema Overview - Schema structure
- Database Schema - Complete schema reference
- RLS Policies - Security policies
- Migrations - Schema migrations
Next Steps
- Database Schema - Complete schema documentation
- Performance Tuning - Deployment optimization