Softomate Solutions logoSoftomate Solutions logo
I'm looking for:
Recently viewed
Database Scaling Strategies for Growing UK Businesses — When and How to Scale — Softomate Solutions blog

DATABASE PERFORMANCE

Database Scaling Strategies for Growing UK Businesses — When and How to Scale

17 May 202617 min readBy Softomate Solutions

Last updated: 17 May 2026

UK businesses should consider database scaling when average query times exceed 200ms, CPU consistently exceeds 70%, or read traffic is growing faster than write traffic. The correct scaling path is: optimise queries first, then add caching, then vertical scale, then read replicas, then sharding. Most businesses need read replicas before they ever need sharding.

When Does a UK Business Actually Need to Scale Its Database

Database scaling decisions should be driven by specific metrics, not by application growth narratives or infrastructure anxiety. The majority of database performance problems that prompt scaling discussions are actually fixable through query optimisation and caching -- changes that cost time rather than money. Scaling infrastructure before exhausting optimisation options is expensive and treats symptoms rather than causes.

Metrics that indicate genuine scaling requirements:

MetricThreshold -- InvestigateThreshold -- ActCorrect Response
Average query time (P50)> 100ms sustained> 200ms sustainedOptimise queries and indexes first; then consider scaling
P95 query time> 500ms> 1,000msIndex review; connection pooling; then vertical scale
CPU utilisation> 50% sustained> 70% sustainedConfirm CPU is for legitimate query work, not lock waits; then vertical scale
Buffer/cache hit rate< 97%< 95%Increase buffer pool (more RAM before scaling hardware)
Active connections> 60% max_connections> 80% max_connectionsAdd connection pooler; then vertical scale or replicas
Read:write ratio> 5:1 reads> 10:1 readsAdd caching layer; then read replicas
Storage utilisation> 70%> 85%Archive old data; then add storage or upgrade instance

The optimise-first rule:

Before any scaling decision, run the three-stage optimisation sequence: (1) identify and fix missing indexes for the top 10 slow queries; (2) implement Redis caching for read-heavy, low-change data; (3) fix N+1 query patterns in application ORM code. This sequence costs 1-3 days of engineering time. It has, in documented client cases, delivered 70-90% reduction in database load on databases that appeared to need scaling. After optimisation is complete and metrics are re-measured, a scaling decision can be made from a baseline that reflects the database's actual capacity, not its capacity minus correctable inefficiency.

Read-heavy vs write-heavy workloads:

The scaling approach differs fundamentally based on workload type. Read-heavy workloads (content sites, product catalogues, reporting) scale effectively with read replicas and caching -- strategies that multiply read capacity without changing write infrastructure. Write-heavy workloads (order processing, event ingestion, real-time analytics) are harder to scale horizontally and benefit more from vertical scaling, connection pooling optimisation, and batching writes where possible.

Vertical Scaling — When It Is Enough and When It Is Not

Vertical scaling (upgrading to a larger database instance with more CPU, RAM and faster storage) is the simplest and most effective scaling approach for most UK businesses. It requires no application changes, no architectural restructuring, and delivers predictable performance improvements. The limitation is cost and the physical ceiling of available instance sizes.

Cost and performance analysis for UK cloud providers (May 2026):

InstancevCPURAMStorageAWS RDS (eu-west-2)Google Cloud SQL (europe-west2)Best For
db.t4g.medium / db-n1-standard-224GBgp3 SSD~£25/month~£22/monthSmall apps, dev/staging
db.t4g.large / db-n1-standard-428GBgp3 SSD~£50/month~£44/monthSmall production MySQL
db.m6g.large / db-n2-standard-428GBgp3 SSD~£80/month~£70/monthConsistent production workloads
db.m6g.xlarge / db-n2-standard-8416GBgp3 SSD~£155/month~£140/monthGrowing applications
db.m6g.2xlarge / db-n2-standard-16832GBgp3 SSD~£305/month~£280/monthHigh-traffic production
db.m6g.4xlarge / db-n2-standard-321664GBgp3 SSD~£605/month~£555/monthLarge applications

When vertical scaling delivers its full benefit:

Vertical scaling delivers the highest return when: the database is query-optimised (indexes are correct, N+1 patterns are fixed), the limiting resource is clearly identified (CPU for complex queries, RAM for buffer pool misses, I/O for large table scans), and the workload is CPU or memory-bound rather than query-logic-bound. Adding RAM enables a larger buffer pool, which reduces disk I/O -- this is the most common vertical scaling win for databases with growing datasets.

When vertical scaling is insufficient:

Vertical scaling has a ceiling -- the largest available instance on AWS RDS UK (eu-west-2) is a db.x2g.16xlarge with 64 vCPU and 1TB RAM at approximately £10,000/month. Beyond this, or when the cost of the next vertical tier exceeds the cost of horizontal alternatives, read replicas or application-level sharding become the correct approach. More practically, vertical scaling does not help when the bottleneck is write concurrency (many simultaneous writes competing for locks) or when read traffic volume exceeds what any single instance can serve within response time budgets.

Storage tier matters for database performance:

AWS RDS and Google Cloud SQL both offer multiple storage tiers. On AWS RDS eu-west-2:

  • gp2 SSD: 3 IOPS/GB baseline, burst to 3,000 IOPS for small volumes. Avoid for production databases with > 1TB storage.
  • gp3 SSD: 3,000 IOPS baseline (configurable to 16,000 IOPS at additional cost), 125 MB/s throughput baseline. Correct choice for most UK production databases.
  • io2 SSD: Up to 64,000 IOPS, 1,000 MB/s throughput. For extreme I/O workloads. Significantly more expensive.

For most UK businesses, upgrading from gp2 to gp3 storage (at similar or lower cost for small volumes) before upgrading the instance size delivers measurable IOPS improvement. Always specify gp3 for new RDS instances.

Read Replicas for UK Production Databases

A read replica is a continuously updated copy of your primary database that handles SELECT queries, freeing the primary database to handle writes and transactional queries exclusively. Read replicas are the standard horizontal scaling approach for read-heavy workloads and the next architectural step after vertical scaling.

How replication works:

MySQL uses binary log (binlog) replication: the primary writes all data changes to the binary log; replicas read the binary log and apply the same changes in sequence. PostgreSQL uses write-ahead log (WAL) streaming replication: the replica receives WAL records from the primary and applies them continuously. Both approaches deliver near-real-time replication (typically 1-100ms lag under normal load).

Adding a MySQL read replica on AWS RDS:

# AWS CLI -- create a read replica in the UK region (eu-west-2)
aws rds create-db-instance-read-replica \
  --db-instance-identifier myapp-replica-1 \
  --source-db-instance-identifier myapp-primary \
  --db-instance-class db.m6g.large \
  --availability-zone eu-west-2b \
  --publicly-accessible false \
  --tags Key=Environment,Value=production Key=Role,Value=read-replica

# Monitor replication lag
aws cloudwatch get-metric-statistics \
  --namespace AWS/RDS \
  --metric-name ReplicaLag \
  --dimensions Name=DBInstanceIdentifier,Value=myapp-replica-1 \
  --start-time 2026-05-17T00:00:00Z \
  --end-time 2026-05-17T23:59:59Z \
  --period 300 \
  --statistics Average

Adding a PostgreSQL read replica on AWS RDS:

# AWS CLI -- PostgreSQL read replica (identical command structure)
aws rds create-db-instance-read-replica \
  --db-instance-identifier pgapp-replica-1 \
  --source-db-instance-identifier pgapp-primary \
  --db-instance-class db.m6g.large \
  --availability-zone eu-west-2b

# Check replica status
aws rds describe-db-instances \
  --db-instance-identifier pgapp-replica-1 \
  --query 'DBInstances[0].{Status:DBInstanceStatus,Lag:StatusInfos}'

# On the replica, verify WAL receiver status:
-- (Connect to replica and run:)
SELECT status, receive_start_lsn, written_lsn, flushed_lsn,
       replay_lsn, sync_state
FROM pg_stat_wal_receiver;

Routing reads to the replica in Laravel:

// config/database.php -- configure read/write split
'mysql' => [
    'driver' => 'mysql',
    'read' => [
        'host' => [
            env('DB_REPLICA_HOST', 'myapp-replica-1.xxxxxxx.eu-west-2.rds.amazonaws.com'),
        ],
    ],
    'write' => [
        'host' => [
            env('DB_HOST', 'myapp-primary.xxxxxxx.eu-west-2.rds.amazonaws.com'),
        ],
    ],
    'sticky' => true,  // After a write, read from primary for the same request
                        // Prevents reading your own stale write from replica
    'database' => env('DB_DATABASE'),
    'username' => env('DB_USERNAME'),
    'password' => env('DB_PASSWORD'),
    'charset' => 'utf8mb4',
    'collation' => 'utf8mb4_unicode_ci',
],

Consistency considerations:

Replication lag means a read replica may be 10-100ms behind the primary. For most read operations (product listings, content pages, analytics dashboards), this is acceptable. For operations where a user must immediately see their own writes (after submitting a form, after completing a purchase), use the sticky option in Laravel (shown above) or explicitly route post-write reads to the primary. PostgreSQL's synchronous_standby_names option can enforce synchronous replication for zero-lag replicas at the cost of write latency.

What We See in Practice

The most common scaling mistake in UK SME production databases is vertical scaling before exhausting optimisation options. A London property platform (marketplace application on AWS RDS MySQL) had been experiencing slow search queries and had upgraded their RDS instance twice over 18 months -- from db.t3.medium (£50/month) to db.r5.large (£200/month) to db.r5.xlarge (£400/month). Performance improved after each upgrade but degraded again as data volume grew.

The actual problem was three missing composite indexes on the property search table: the search query filtered on property_type, location_id and price_range simultaneously, but the existing indexes only covered these columns individually. The query planner was choosing the most selective individual index and then filtering on the other two columns via a table scan on the 600,000-row result set.

-- The missing index (identified from EXPLAIN output in 20 minutes)
CREATE INDEX idx_properties_search
ON properties (property_type, location_id, min_price, max_price, status)
WHERE status = 'active';
-- Partial index (status = 'active') reduces index size by 60%
-- as archived properties are never searched but were being indexed

Adding this single index reduced the P95 search query time from 1,400ms to 45ms. The client downgraded from db.r5.xlarge back to db.r5.large -- saving £200/month -- and performance was better than at any point on the larger instance. The three-year cost of the two unnecessary instance upgrades was approximately £7,200. The index analysis and implementation took 2 hours.

This pattern is extremely common. Vertical scaling feels like a direct, controllable response to a performance problem. Index analysis feels like a more uncertain diagnostic exercise. In practice, for the vast majority of UK SME database scaling problems, the correct response is index analysis first, and scaling decisions are made only after confirming the database is performing at its theoretical maximum for the current instance size.

See our complete database performance tuning guide for the full diagnostic sequence that prevents unnecessary scaling costs.

Redis Caching Layer — The Fastest Database Performance Win

A Redis caching layer reduces database load by serving frequently requested data from memory rather than querying the database on every request. For read-heavy workloads where the same data is requested repeatedly -- product catalogues, configuration data, user session data, aggregated statistics -- Redis can reduce database read volume by 60-90%, dramatically improving response times and freeing database capacity for writes and complex queries.

What to cache (and what not to):

Good cache candidatesPoor cache candidates
Product catalogue data (changes infrequently; read thousands of times)Real-time inventory counts (must be accurate; changes constantly)
User session data (read on every request; small per-user)Financial transaction records (must be accurate; regulatory requirement)
Configuration and settings (changes rarely)User-specific personalised data (high cardinality; cache utility low)
Computed aggregates (expensive to compute; result changes slowly)Data with complex invalidation dependencies
Public content pages (same content served to many users)Highly personalised pages (cache miss rate approaches 100%)

TTL strategy -- setting appropriate cache lifetimes:

  • Product catalogue: 5-30 minutes TTL. Slight staleness is acceptable; user will see updated prices after at most 30 minutes.
  • User session data: 30-60 minutes idle TTL, extended on each request. Use Redis EXPIRE with GETEX for this pattern.
  • Aggregated statistics (dashboard counters): 60-300 seconds TTL depending on how frequently they change.
  • Configuration data: 10-60 minutes TTL. Bust manually when configuration is updated via admin.
  • Rate limiting counters: TTL equals the rate limit window (60 seconds for per-minute limits).

Implementing Redis caching in Laravel:

// config/cache.php -- default cache store
'default' => env('CACHE_DRIVER', 'redis'),

'stores' => [
    'redis' => [
        'driver' => 'redis',
        'connection' => 'cache',
        'lock_connection' => 'default',
    ],
],

// .env
CACHE_DRIVER=redis
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=your-redis-password

// Usage in controller/service:

// Simple cache with TTL
$products = Cache::remember('products:category:' . $categoryId, 1800, function () use ($categoryId) {
    return Product::where('category_id', $categoryId)
        ->where('active', true)
        ->select('id', 'name', 'price', 'slug', 'image_id')
        ->get();
});
// Cache::remember: returns cached value if exists; otherwise runs closure and caches result
// 1800 = 30 minutes TTL

// Cache invalidation on product update
public function update(Product $product, array $data): Product
{
    $product->update($data);

    // Bust all cache keys that might contain this product
    Cache::forget('products:category:' . $product->category_id);
    Cache::forget('products:featured');  // If product is in featured lists
    Cache::tags(['products'])->flush();  // If using tagged caching

    return $product->fresh();
}

// Tagged caching for bulk invalidation (requires Redis)
$products = Cache::tags(['products', 'category:' . $categoryId])
    ->remember('products:category:' . $categoryId, 1800, function () {
        return Product::active()->get();
    });

// Bust all product caches on any product change:
Cache::tags(['products'])->flush();

Redis configuration for UK production:

# /etc/redis/redis.conf

# Memory limit -- prevent Redis consuming all server memory
maxmemory 2gb

# Eviction policy when memory limit is reached
# allkeys-lru: evict least recently used keys (correct for pure cache use)
# volatile-lru: only evict keys with TTL set (use if Redis also stores persistent data)
maxmemory-policy allkeys-lru

# Persistence -- for cache-only use, disable to avoid write overhead
# save ""  # Disables RDB snapshots
appendonly no  # Disables AOF persistence

# Connection limit
maxclients 500

# Password (required -- never run Redis without authentication)
requirepass your-strong-redis-password

# Bind to localhost only (if app server and Redis are on same host)
bind 127.0.0.1

# Or bind to private IP for multi-server setups
# bind 10.0.0.5 127.0.0.1
# Enable TLS for data in transit if Redis is over a network:
# tls-port 6380
# tls-cert-file /etc/redis/tls/redis.crt
# tls-key-file /etc/redis/tls/redis.key

When to Migrate to a Managed Cloud Database UK

Managed cloud databases (AWS RDS, Google Cloud SQL, Supabase, PlanetScale) handle database engine updates, automated backups, failover, and basic monitoring. For UK businesses moving from self-managed MySQL or PostgreSQL on a VPS, the operational overhead reduction is the primary justification -- not performance improvement.

Cost comparison for UK businesses (AWS eu-west-2, May 2026):

SolutionSetupMonthly Cost (16GB RAM)HA FailoverManaged BackupsOps Overhead
Self-managed VPS (Hetzner/OVH)Manual£30-60/monthManual setupManual setupHigh
AWS RDS MySQL (db.m6g.xlarge)15 minutes~£155/monthYes (Multi-AZ +£)Yes (automated)Low
Google Cloud SQL PostgreSQL15 minutes~£140/monthYes (HA option)Yes (automated)Low
Supabase (Pro plan)5 minutes£22/month (limited)YesYesVery low
PlanetScale (Scaler Pro)5 minutes£27/monthYesYesVery low
AWS Aurora PostgreSQL20 minutes~£200/monthYes (built-in)YesVery low

Correct migration triggers:

  • Engineer time spent on database maintenance (OS updates, backup management, monitoring setup) exceeds 4+ hours per month -- managed cloud recovers that time
  • Business requires automated failover with sub-60-second RTO (Recovery Time Objective) -- managed Multi-AZ or HA configurations deliver this without manual configuration
  • Team lacks DBA expertise and needs managed monitoring and alerting included
  • Compliance requires encrypted backups with configurable retention periods -- managed services provide this by default

When NOT to migrate to managed cloud:

  • Strict UK data sovereignty requirements where database must remain in specific physical infrastructure (managed cloud uses AWS/Google data centres -- verify UK region scope with legal team)
  • Need for specific PostgreSQL extensions not available on managed services (PostGIS is available on RDS; some lesser-used extensions are not)
  • Highly cost-sensitive environments where the 3-5x cost premium over self-managed VPS is not justified by the operational overhead saving

For context on the full database performance optimisation picture including when to scale vs when to tune, see our database performance tuning guide.

Frequently Asked Questions

When should a UK business add a read replica instead of upgrading the primary database?

Add a read replica when: read traffic is growing faster than write traffic (read:write ratio above 5:1); the primary database is CPU-bound from SELECT queries rather than write operations; you need to offload reporting or analytics queries that run for 10-60 seconds and block other queries on the primary; or you need a database copy for point-in-time recovery without affecting production. Upgrading the primary instance is the better choice when write performance is the bottleneck, when the workload is write-heavy, or when latency requirements make cross-replica consistency a concern.

What is database sharding and does my UK business need it?

Database sharding splits a large database into multiple smaller databases (shards) each holding a subset of the data -- typically by customer ID, geographic region, or date range. Sharding is a significant architectural complexity addition: cross-shard queries become application responsibility, transactions across shards require distributed coordination, and schema changes must be applied to all shards simultaneously. Most UK businesses do not need sharding. Read replicas, vertical scaling and Redis caching handle the scaling requirements of applications serving hundreds of thousands of users. Sharding becomes relevant when a single primary database cannot handle write volume -- typically at millions of writes per day or multi-terabyte datasets that have outgrown the largest available cloud instance.

How much does adding Redis caching improve database performance?

For read-heavy workloads with high data locality (the same data requested repeatedly), Redis caching typically reduces database query volume by 60-90% for cached data paths. A product catalogue page that generates 50 database queries without caching generates 1-2 queries with caching (just the cache miss on first load). The latency improvement is substantial: a Redis GET returns in 0.1-1ms versus 50-200ms for a typical database query. The combination of reduced query volume and lower per-request latency makes Redis caching the highest return-per-hour optimisation for most read-heavy UK web applications, ahead of additional hardware or read replicas.

What is the typical cost of AWS RDS for a growing UK business?

A growing UK business typically starts on AWS RDS in the eu-west-2 (London) region at £50-80/month for a db.t4g.large or db.m6g.large instance (2-4 vCPU, 8GB RAM, 100GB gp3 storage). As the application scales, the progression is typically: db.m6g.large (~£80/month) to db.m6g.xlarge (~£155/month) to adding a read replica (~£80-155/month) to db.m6g.2xlarge (~£305/month plus replica). Multi-AZ deployment (for automatic failover) approximately doubles the instance cost. A business serving 10,000-50,000 daily active users typically runs comfortably on £200-400/month of RDS infrastructure if the application is correctly optimised.

What database should a new UK startup choose in 2026?

For a new UK startup in 2026, PostgreSQL on managed infrastructure (Supabase for early stage, AWS RDS or Google Cloud SQL as you scale) is the pragmatic choice for most applications. PostgreSQL's richer index types, stronger JSON support (JSONB), partial indexes and more powerful query planner provide a performance ceiling significantly higher than MySQL for complex applications. Supabase (PostgreSQL-based) at £22/month includes real-time subscriptions, auto-generated APIs and built-in auth -- a compelling starting point. MySQL remains the correct choice for applications that require specific MySQL ecosystem tooling, or where team expertise is MySQL-weighted and the migration cost is not justified.

Conclusion

UK businesses should follow a disciplined scaling sequence: optimise queries and indexes first (1-3 days, zero cost); implement Redis caching for read-heavy data paths (1-2 days, £15-30/month for a Redis instance); then vertical scale if CPU or memory metrics confirm a hardware constraint; then add read replicas if read:write ratio exceeds 5:1; only consider sharding when write volume exceeds the capacity of the largest available instance. The most expensive scaling decisions -- unnecessary vertical upgrades and premature architectural complexity -- consistently result from skipping optimisation and going straight to infrastructure changes. The correct next step for any UK business experiencing database performance problems is a 2-4 hour diagnostic session identifying the specific bottleneck, not a credit card purchase on the cloud console.

Softomate Solutions provides database scaling strategy and implementation for growing UK businesses from Stanmore, London. Contact us to discuss your scaling requirements.

Written by the Softomate Solutions technical team, database and software specialists based in Stanmore, London.

Let us help

Need help applying this in your business?

Talk to our London-based team about how we can build the AI software, automation, or bespoke development tailored to your needs.

Deen Dayal Yadav, founder of Softomate Solutions

Deen Dayal Yadav

Online

Hi there ðŸ'‹

How can I help you?