CockroachDB is designed for horizontal scalability and high availability. This guide covers optimization strategies across schema design, query patterns, and cluster configuration to maximize performance.
Indexes accelerate reads but add overhead to writes. Design indexes carefully based on query patterns.
1
Analyze query patterns
Identify frequently used WHERE clauses, JOIN conditions, and ORDER BY clauses.
2
Create covering indexes
Include all columns needed by a query to avoid table lookups.
3
Use partial indexes
Index only relevant rows to reduce index size and maintenance cost.
4
Monitor index usage
Remove unused indexes that consume resources without providing benefit.
-- Covering index includes all columns needed by queryCREATE INDEX idx_customer_orders ON orders (customer_id, created_at) STORING (total_amount, status);-- Partial index for active orders onlyCREATE INDEX idx_active_orders ON orders (created_at) WHERE status = 'active';-- Multi-column index for composite queriesCREATE INDEX idx_order_search ON orders (status, created_at DESC, customer_id);
Group frequently accessed columns together to reduce I/O.
CREATE TABLE products ( id UUID PRIMARY KEY, name STRING, description TEXT, metadata JSONB, price DECIMAL, stock_count INT, -- Frequently accessed columns in default family FAMILY primary (id, name, price, stock_count), -- Large, infrequently accessed columns in separate family FAMILY details (description, metadata));
Choose the right JOIN type and order for optimal performance.
-- Ensure joined columns are indexedCREATE INDEX idx_order_customer ON orders (customer_id);-- Use INNER JOIN when possible (more efficient than OUTER JOIN)SELECT o.id, o.total, c.nameFROM orders oINNER JOIN customers c ON o.customer_id = c.idWHERE o.created_at > NOW() - INTERVAL '1 day';-- For small lookup tables, consider LOOKUP JOIN hintSELECT o.id, o.total, c.nameFROM orders oINNER LOOKUP JOIN customers c ON o.customer_id = c.id;
Keep transactions small and focused to reduce contention.
Large transactions increase the likelihood of conflicts and retries. Break large operations into smaller batches.
-- Instead of one large transactionBEGIN; UPDATE accounts SET balance = balance - 1 WHERE id IN (SELECT id FROM accounts LIMIT 10000);COMMIT;-- Use smaller batchesBEGIN; UPDATE accounts SET balance = balance - 1 WHERE id IN (SELECT id FROM accounts LIMIT 100);COMMIT;-- Repeat in loop
Use SELECT FOR UPDATE to lock rows and prevent conflicts in read-modify-write patterns.
BEGIN; -- Lock the row to prevent concurrent modifications SELECT balance FROM accounts WHERE id = 'acc123' FOR UPDATE; -- Perform business logic UPDATE accounts SET balance = balance - 100 WHERE id = 'acc123';COMMIT;
Split large tables to distribute load across nodes.
-- Split table at specific key valuesALTER TABLE orders SPLIT AT VALUES ('2024-01-01'), ('2024-02-01'), ('2024-03-01');-- View current splitsSHOW RANGES FROM TABLE orders;-- Scatter ranges to distribute across clusterALTER TABLE orders SCATTER;
Enable follower reads for queries that can tolerate slightly stale data.
-- Use follower reads for analytics queriesSET TRANSACTION AS OF SYSTEM TIME follower_read_timestamp();SELECT COUNT(*) FROM orders WHERE created_at > NOW() - INTERVAL '7 days';
Use regional tables and indexes for multi-region deployments.
-- Regional by row tableALTER TABLE orders SET LOCALITY REGIONAL BY ROW AS region;-- Create regional indexCREATE INDEX idx_regional_orders ON orders (customer_id) WHERE region = 'us-east';