Console Login

Database Sharding: The Nuclear Option for Scaling High-Traffic Apps in 2018

Database Sharding: The Nuclear Option for Scaling High-Traffic Apps

Let’s be brutally honest: if you can avoid sharding, you should. Sharding is the architectural equivalent of a root canal. It increases application complexity, makes backups a nightmare, and turns simple JOIN queries into cross-network latency beasts. Yet, here we are. Your primary node is hitting 95% CPU usage on write operations, your NVMe storage is saturating, and vertical scaling has hit the ceiling of what even the beefiest bare-metal server can handle.

It is July 2018. GDPR has been enforceable for two months. The days of carelessly throwing data onto random US-based cloud instances are over. If you are handling Norwegian user data, you need performance, but you also need strict compliance with Datatilsynet's requirements. This guide isn't a fluffy overview. It is a battle plan for horizontal scaling when you have no other choice.

The "War Story": When INSERT becomes the bottleneck

I recently audited a high-traffic e-commerce platform based in Oslo. They were running a heavily customized Magento 2 stack on a standard dedicated server. During a flash sale, their MySQL 5.7 master didn't crash because of reads—they had plenty of slaves for that. It crashed because of writes. The locking contention on the order tables was so high that the application ground to a halt. Latency spiked from 20ms to 4000ms.

We migrated them to a sharded architecture. But before you start splitting tables, you must understand the infrastructure required to support it. Sharding increases the chattiness of your network. If your shards are hosted on oversold VPS nodes with high "steal time" (noisy neighbors), your latency will destroy any gains from parallel processing. This is why we reference CoolVDS architecture often—KVM virtualization with dedicated resource allocation is the baseline requirement for this level of engineering.

Strategy 1: Application-Level Sharding (The "Manual" Way)

The simplest way to shard is to let your application code decide where to route the query. You split your data based on a "Shard Key"—usually user_id or company_id.

The Logic

If you have 4 shards, you might use a simple modulo operator: shard_id = user_id % 4. However, this is dangerous. If you need to add a 5th shard later, you have to rebalance everything. Instead, use a lookup table (Directory-Based Sharding).

Example PHP Implementation (PDO wrapper):

class ShardManager {
    private $lookupDB;

    public function __construct($lookupConnection) {
        $this->lookupDB = $lookupConnection;
    }

    public function getShardConnection($userId) {
        // Check where this user lives
        $stmt = $this->lookupDB->prepare("SELECT shard_host FROM user_shards WHERE user_id = ?");
        $stmt->execute([$userId]);
        $host = $stmt->fetchColumn();

        if (!$host) {
            // Assign new user to the least loaded shard (simplified logic)
            $host = '10.0.0.15'; // minimal example
            $this->assignUserToShard($userId, $host);
        }

        // Return connection to the specific CoolVDS instance
        return new PDO("mysql:host=$host;dbname=app_db", 'user', 'pass');
    }
}
Pro Tip: Never shard your lookup table. Keep it on a highly available pair of NVMe instances. If the lookup table dies, your entire cluster is blind.

Strategy 2: Middleware Sharding with ProxySQL

In 2018, ProxySQL has emerged as the standard for MySQL traffic management. Instead of polluting your PHP or Python code with routing logic, you send all queries to a local ProxySQL instance, which routes them based on query rules.

This is cleaner but requires careful configuration. Here is how you configure ProxySQL to split traffic based on schema or table comments, effectively sharding transparently.

Configuration snippet for proxysql.cnf setup:

-- Define your backend shards (Hostgroups)
INSERT INTO mysql_servers (hostgroup_id, hostname, port) VALUES (10, '192.168.1.101', 3306);
INSERT INTO mysql_servers (hostgroup_id, hostname, port) VALUES (20, '192.168.1.102', 3306);

-- Route based on comments injected by the app
-- Example query: SELECT /* sharding_key:10 */ * FROM orders WHERE user_id=500;
INSERT INTO mysql_query_rules (rule_id, active, match_pattern, destination_hostgroup, apply)
VALUES (1, 1, "sharding_key:10", 10, 1);

INSERT INTO mysql_query_rules (rule_id, active, match_pattern, destination_hostgroup, apply)
VALUES (2, 1, "sharding_key:20", 20, 1);

LOAD MYSQL QUERY RULES TO RUNTIME;
SAVE MYSQL QUERY RULES TO DISK;

The Hardware Reality: Latency and IOPS

Sharding solves CPU and RAM bottlenecks, but it introduces network latency. If Shard A needs data from Shard B to complete a report, you are triggering network calls. In a datacenter, the speed of light is fast, but the TCP stack adds overhead.

This is where infrastructure choice dictates success. You need low-latency internal networking. For our Norwegian clients, hosting shards within the same Oslo datacenter is non-negotiable. If you split shards between Oslo and Frankfurt, your application response time will fluctuate wildly. Furthermore, the underlying storage must be NVMe. Spinning rust (HDD) or even standard SATA SSDs will choke when running parallelized `ALTER TABLE` operations across multiple shards.

Performance Comparison: Standard VPS vs. CoolVDS (KVM/NVMe)

Metric Standard Cloud VPS CoolVDS Performance Instance
Disk IOPS (Random Write) ~5,000 ~20,000+ (NVMe)
Network Latency (Internal) 0.5ms - 2.0ms (Jittery) < 0.2ms (Stable)
Virtualization Container (OpenVZ/LXC) Full Hardware (KVM)

The PostgreSQL approach: Citus

If you are running PostgreSQL 10, you have an advantage. The extension Citus turns Postgres into a distributed database. Unlike manual application sharding, Citus handles the routing logic at the database layer.

To set up a distributed table in Citus on Ubuntu 16.04/18.04:

-- On the coordinator node
CREATE EXTENSION citus;

-- Add worker nodes (your CoolVDS instances)
SELECT * from master_add_node('10.0.0.11', 5432);
SELECT * from master_add_node('10.0.0.12', 5432);

-- Distribute the table
CREATE TABLE user_events (user_id bigint, event_type text, event_time timestamptz);
SELECT create_distributed_table('user_events', 'user_id');

This command physically partitions the table across your worker nodes. Queries are parallelized automatically. It is powerful, but it requires that all your nodes have identical performance characteristics. One slow node slows down the entire cluster.

GDPR and Data Sovereignty

Since May 25th, the rules of the game have changed. When you shard data, you are technically moving it. If you use a managed database service from a US giant, you must ensure that none of those shards inadvertently land in a region outside the EEA, or you face the wrath of GDPR Article 44.

By using self-managed VPS instances in Norway, you maintain total control. You know exactly which physical disk holds shard_04. You can prove to auditors that data never leaves the Oslo jurisdiction. This level of provenance is difficult to guarantee with abstract "serverless" databases where the backend is a black box.

Conclusion: Don't Shard Prematurely

Sharding is a necessary evil for massive scale. Before you write that sharding logic, optimize your indexes. Tune your `innodb_buffer_pool_size`. Switch to NVMe storage. Only when a single CoolVDS instance with 32 cores and 128GB RAM cannot handle the write load should you break out the sharding hammer.

If you are ready to architect a distributed system that stays fast and legal, you need a foundation that doesn't blink under load. Don't let slow I/O kill your SEO or your database locks.

Deploy your high-performance sharding cluster on CoolVDS today. Local Norwegian infrastructure, enterprise-grade NVMe, and zero noisy neighbors.