database-optimization-commerce

Speed up slow product and order queries with proper indexing, query analysis, read replicas, and search engine offloading strategies

11 stars

Best use case

database-optimization-commerce is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Speed up slow product and order queries with proper indexing, query analysis, read replicas, and search engine offloading strategies

Teams using database-optimization-commerce should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/database-optimization-commerce/SKILL.md --create-dirs "https://raw.githubusercontent.com/finsilabs/awesome-ecommerce-skills/main/skills/infrastructure-performance/database-optimization-commerce/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/database-optimization-commerce/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How database-optimization-commerce Compares

Feature / Agentdatabase-optimization-commerceStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Speed up slow product and order queries with proper indexing, query analysis, read replicas, and search engine offloading strategies

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Database Optimization — Commerce

## Overview

E-commerce databases face distinct query patterns: high-cardinality product filtering (category + price + attributes), session-scoped cart lookups, write-heavy order creation, and read-heavy catalog browsing that must scale to concurrent users. This skill covers identifying slow queries, designing effective indexes for product filtering, partitioning order tables, and routing read traffic to replicas.

## When to Use This Skill

- When product listing pages are slow due to unindexed filter combinations (category + price + brand)
- When checkout throughput is limited by order insertion latency
- When read load on the primary database is causing write latency to increase
- When a slow query log reveals queries doing sequential scans on large tables
- When planning a database schema for a new custom e-commerce platform

## Core Instructions

### Step 1: Determine your situation

Database optimization applies primarily to self-hosted setups. Understand your constraints first:

| Platform | Database Control | What to Optimize |
|----------|-----------------|-----------------|
| **Shopify** | None — Shopify manages all infrastructure | Focus on Liquid template rendering speed, app performance, and Shopify's built-in query optimization via Search & Discovery app |
| **WooCommerce** | Full — you manage MySQL/MariaDB on your host | Optimize WooCommerce queries with caching plugins (Redis Object Cache, WP Rocket), add database indexes via WP Optimize plugin, and configure your hosting MySQL settings |
| **BigCommerce** | None — BigCommerce manages all infrastructure | Focus on theme performance, image optimization, and reducing third-party app overhead |
| **Custom / Headless** | Full — you own PostgreSQL (or MySQL) | Apply all the techniques below; PostgreSQL is assumed in code examples |

### Step 2: Quick wins for WooCommerce (managed WordPress/WooCommerce)

Before touching database indexes directly, apply these WooCommerce-specific optimizations:

1. **Install Redis Object Cache** (free, wordpress.org):
   - Your host must support Redis (most managed WordPress hosts — WP Engine, Kinsta, Cloudways — do)
   - Install and activate the plugin; go to **Settings → Redis** and click **Enable Object Cache**
   - This caches all WooCommerce database queries in memory, dramatically reducing repeat query times

2. **Install WP-Optimize** (free, wordpress.org):
   - Go to **WP-Optimize → Database** and run **Clean database** to remove orphaned order meta, expired transients, and post revisions
   - WooCommerce stores build up millions of rows of orphaned meta over time — regular cleanup is essential
   - Schedule automatic cleanup weekly

3. **Enable the WooCommerce HPOS (High-Performance Order Storage)**:
   - Go to **WooCommerce → Settings → Advanced → Features**
   - Enable **High-Performance Order Storage** — this moves orders from WP post tables to dedicated order tables with proper indexes
   - Critical for stores with 10,000+ orders

4. **Upgrade to a host with MySQL 8.0+** — older MySQL versions lack important index improvements; WP Engine, Kinsta, and Cloudways all run MySQL 8.0+

### Step 3: PostgreSQL optimization for custom storefronts

---

#### Identify slow queries

```sql
-- Enable pg_stat_statements to find the worst offenders
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;

-- Top 20 slowest queries by total cumulative time
SELECT
  round(total_exec_time::numeric, 2) AS total_ms,
  round(mean_exec_time::numeric, 2) AS mean_ms,
  calls,
  round((total_exec_time / sum(total_exec_time) OVER()) * 100, 2) AS pct_of_total,
  left(query, 200) AS query
FROM pg_stat_statements
WHERE calls > 100
ORDER BY total_exec_time DESC
LIMIT 20;

-- Diagnose a specific slow query
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)
SELECT p.id, p.name, p.price
FROM products p
JOIN product_categories pc ON pc.product_id = p.id
WHERE pc.category_id = 42
  AND p.price BETWEEN 1000 AND 5000
  AND p.status = 'active'
ORDER BY p.created_at DESC
LIMIT 24;
-- Look for "Seq Scan" on large tables — this means a missing index
```

#### Design indexes for product filtering

```sql
-- Partial index on active products only (smaller, faster)
CREATE INDEX CONCURRENTLY idx_products_status
  ON products (status) WHERE status = 'active';

CREATE INDEX CONCURRENTLY idx_products_price
  ON products (price) WHERE status = 'active';

-- Composite index for the most common filter combination
-- INCLUDE adds non-key columns for index-only scans (no table heap access)
CREATE INDEX CONCURRENTLY idx_products_listing
  ON products (status, brand_id, price, created_at DESC)
  INCLUDE (name, slug, thumbnail_url);

-- GIN index for flexible JSONB attribute filtering
-- Enables: attributes @> '{"color": "blue", "size": "M"}'
CREATE INDEX CONCURRENTLY idx_products_attributes
  ON products USING gin(attributes);

-- ALWAYS index foreign keys (PostgreSQL does NOT do this automatically)
CREATE INDEX CONCURRENTLY idx_product_categories_product_id
  ON product_categories (product_id);
CREATE INDEX CONCURRENTLY idx_order_lines_order_id
  ON order_lines (order_id);
```

#### Partition the orders table by date

```sql
-- Create orders table with range partitioning on created_at
CREATE TABLE orders (
  id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  customer_id UUID NOT NULL,
  status      TEXT NOT NULL,
  total_cents INTEGER NOT NULL,
  created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()
) PARTITION BY RANGE (created_at);

-- Quarterly partitions
CREATE TABLE orders_2025_q1 PARTITION OF orders
  FOR VALUES FROM ('2025-01-01') TO ('2025-04-01');
CREATE TABLE orders_2025_q2 PARTITION OF orders
  FOR VALUES FROM ('2025-04-01') TO ('2025-07-01');
-- (continue for Q3, Q4, 2026...)

-- Indexes on the parent propagate to all partitions
CREATE INDEX CONCURRENTLY ON orders (customer_id, created_at DESC);
CREATE INDEX CONCURRENTLY ON orders (status, created_at DESC);
```

#### Route reads to replicas

```javascript
// lib/database.js — two connection pools
import { Pool } from 'pg';

const primaryPool = new Pool({ connectionString: process.env.DATABASE_URL, max: 20 });
const replicaPool = new Pool({ connectionString: process.env.DATABASE_REPLICA_URL, max: 50 });

export const db = {
  // Writes and anything requiring freshness — primary
  async write(sql, params = []) {
    const result = await primaryPool.query(sql, params);
    return result.rows;
  },
  // Catalog reads — replica (slight staleness is acceptable)
  async read(sql, params = []) {
    const result = await replicaPool.query(sql, params);
    return result.rows;
  },
  // Transactions — always primary
  async transaction(fn) {
    const client = await primaryPool.connect();
    try {
      await client.query('BEGIN');
      const result = await fn(client);
      await client.query('COMMIT');
      return result;
    } catch (e) {
      await client.query('ROLLBACK');
      throw e;
    } finally {
      client.release();
    }
  },
};
```

**Route reads correctly:**
- Catalog pages, product search, order history → `db.read()` (replica)
- Cart operations, checkout, inventory decrement → `db.write()` or `db.transaction()` (primary)

#### Use keyset pagination (never OFFSET for large catalogs)

```sql
-- OFFSET 10000 reads and discards 10,000 rows — slow at scale
-- Use keyset pagination instead: pass the last row's cursor values

-- First page
SELECT id, name, price, created_at FROM products
WHERE status = 'active'
ORDER BY created_at DESC, id DESC
LIMIT 24;

-- Next page (pass last row's created_at and id as cursor)
SELECT id, name, price, created_at FROM products
WHERE status = 'active'
  AND (created_at, id) < ('2025-03-01T12:00:00Z', 'uuid-of-last-row')
ORDER BY created_at DESC, id DESC
LIMIT 24;
```

## Best Practices

- **Use `EXPLAIN (ANALYZE, BUFFERS)`** to validate index usage — `EXPLAIN` alone shows estimates; `ANALYZE` runs the query and shows actuals; "Seq Scan" on a large table means a missing index
- **Create indexes `CONCURRENTLY`** — without `CONCURRENTLY`, index creation locks the table for writes; always use it in production
- **Index all foreign keys** — PostgreSQL does not auto-index foreign keys; `customer_id`, `order_id`, and `product_id` in join tables must be explicitly indexed
- **Set `work_mem` carefully** — increasing `work_mem` speeds up sorting but multiplies with connection count; benchmark before raising it
- **Run `VACUUM ANALYZE` regularly** — table bloat from dead tuples slows all queries; configure `autovacuum` aggressively on high-write tables like `carts` and `sessions`

## Common Pitfalls

| Problem | Solution |
|---------|----------|
| Index not used for multi-column filters | Composite index column order matters: equality columns first (`status`, `brand_id`), range columns last (`price`, `created_at`) |
| Slow JSONB attribute filtering | Add a GIN index on the full `attributes` column for `@>` containment queries; use expression indexes for range queries on specific JSON keys |
| Read replica lag causing stale cart data | Route cart reads to primary; only route catalog and order history reads to replica where slight staleness is acceptable |
| Partition pruning not working | Ensure `WHERE` clause includes the partition key (`created_at`) so PostgreSQL can skip irrelevant partitions |
| Slow pagination on page 50+ | Replace `OFFSET` with keyset pagination using the last row's values as a cursor |

## Related Skills

- @flash-sale-scaling
- @monitoring-alerting-commerce
- @ecommerce-caching
- @load-testing-commerce

Related Skills

accessibility-commerce

11
from finsilabs/awesome-ecommerce-skills

Make your store usable by everyone with WCAG 2.1 AA compliance — screen reader support, keyboard navigation, and accessible cart and checkout flows

gdpr-ecommerce

11
from finsilabs/awesome-ecommerce-skills

Make your store GDPR-compliant with cookie consent, customer data export on request, right-to-deletion workflows, and data processing agreements

woocommerce-subscriptions

11
from finsilabs/awesome-ecommerce-skills

Add subscription products to WooCommerce with automatic recurring billing, renewal notifications, and subscriber self-service management

woocommerce-rest-api

11
from finsilabs/awesome-ecommerce-skills

Integrate or build headless frontends on WooCommerce using its REST API for products, orders, customers, and coupons with key authentication

woocommerce-plugin-development

11
from finsilabs/awesome-ecommerce-skills

Create custom WooCommerce plugins using action/filter hooks, the Settings API, and REST API extensions to add features without modifying core

woocommerce-performance

11
from finsilabs/awesome-ecommerce-skills

Fix slow WooCommerce stores by optimizing database queries, clearing transients, enabling Redis object caching, and configuring page caching

woocommerce-blocks

11
from finsilabs/awesome-ecommerce-skills

Customize WooCommerce checkout and cart pages using Gutenberg blocks with server-side rendering, slot-fills, and extensibility hooks

payment-terms-optimization

11
from finsilabs/awesome-ecommerce-skills

Configure flexible payment terms for B2B customers with net-30/60/90 options, early payment discounts, credit limit management, and automated collections

checkout-flow-optimization

11
from finsilabs/awesome-ecommerce-skills

Design a high-converting checkout with address autocomplete, smart field ordering, progress indicators, and minimal friction to reduce abandonment

video-commerce-integration

11
from finsilabs/awesome-ecommerce-skills

Enable shoppable video experiences with live shopping events, interactive product hotspots, and one-click checkout directly from video and livestream content

social-commerce

11
from finsilabs/awesome-ecommerce-skills

Sync your catalog to Instagram, TikTok, and Facebook to enable shoppable posts and in-app checkout directly from your social content

loyalty-program-optimization

11
from finsilabs/awesome-ecommerce-skills

Design and optimize tiered loyalty programs with points, rewards, exclusive perks, and member-only benefits that increase repeat purchase rates and CLV