{
    "slug": "read_replicas",
    "term": "Read Replicas & Database Scaling",
    "category": "performance",
    "difficulty": "intermediate",
    "short": "Directing read queries to replica servers while writes go to the primary — a simple way to scale read throughput horizontally without sharding.",
    "long": "Most web applications read far more than they write. Read replicas receive a continuous stream of changes from the primary via replication (MySQL binary log, PostgreSQL streaming replication) and serve SELECT queries independently. PHP applications implement this with a connection manager routing writes to the primary DSN and reads to a replica DSN. Laravel supports multiple connections natively. Caveats: replication lag means replicas may serve slightly stale data — never read from a replica immediately after a write in the same request without a mitigation strategy (e.g. read-your-writes from the primary for the current session). Monitor replication lag as a key operational metric.",
    "aliases": [
        "DB read replica",
        "read slave",
        "MySQL replication"
    ],
    "tags": [
        "performance",
        "database",
        "scalability",
        "architecture"
    ],
    "misconception": "Read replicas are immediately consistent with the primary. Replication lag means replicas may serve stale data seconds or minutes behind the primary. Applications must route reads that require freshness (post-write reads) to the primary, not replicas.",
    "why_it_matters": "Read replicas offload SELECT queries to secondary servers — the primary handles only writes, scaling read capacity horizontally and protecting write throughput from reporting or analytics queries.",
    "common_mistakes": [
        "Sending write queries to replicas — they are read-only; writes silently fail or error.",
        "Reading from replica immediately after a write — replication lag means the new data may not be there yet.",
        "Not routing long-running analytics queries to replicas — they block the primary's query queue.",
        "Using a single connection for both reads and writes when separate read/write connections are configured."
    ],
    "when_to_use": [],
    "avoid_when": [],
    "related": [
        "database_sharding",
        "database_partitioning",
        "connection_pooling",
        "caching"
    ],
    "prerequisites": [
        "db_replication_types",
        "database_indexing",
        "connection_pool_sizing"
    ],
    "refs": [
        "https://dev.mysql.com/doc/refman/8.0/en/replication.html"
    ],
    "bad_code": "// All queries to primary — no read scaling:\n$pdo = new PDO('mysql:host=primary;dbname=app', ...);\n$pdo->query('SELECT * FROM orders'); // Should go to replica\n$pdo->query('UPDATE orders SET ...');  // Correctly on primary\n\n// With read/write splitting:\n$read  = new PDO('mysql:host=replica;dbname=app', ...);\n$write = new PDO('mysql:host=primary;dbname=app', ...);",
    "good_code": "// Route reads to replica, writes to primary\nclass DatabaseManager {\n    public function __construct(\n        private PDO $primary,\n        private array $replicas,\n    ) {}\n\n    public function write(): PDO { return $this->primary; }\n\n    public function read(): PDO {\n        // Round-robin across replicas\n        return $this->replicas[array_rand($this->replicas)];\n    }\n}\n\n// Usage\n$db->write()->prepare('INSERT INTO orders ...')\n$db->read()->prepare('SELECT * FROM orders WHERE user_id = ?')\n\n// Caution: read your own writes — after a write, use primary for a short window\n// to avoid reading stale replica data (replication lag)",
    "quick_fix": "Route all SELECT queries to read replicas and writes to the primary — Laravel's read/write connection config and Doctrine's read-only EntityManager make this transparent to application code",
    "severity": "high",
    "effort": "medium",
    "created": "2026-03-15",
    "updated": "2026-03-22",
    "citation": {
        "canonical_url": "https://codeclaritylab.com/glossary/read_replicas",
        "html_url": "https://codeclaritylab.com/glossary/read_replicas",
        "json_url": "https://codeclaritylab.com/glossary/read_replicas.json",
        "source": "CodeClarityLab Glossary",
        "author": "P.F.",
        "author_url": "https://pfmedia.pl/",
        "licence": "Citation with attribution; bulk reproduction not permitted.",
        "usage": {
            "verbatim_allowed": [
                "short",
                "common_mistakes",
                "avoid_when",
                "when_to_use"
            ],
            "paraphrase_required": [
                "long",
                "code_examples"
            ],
            "multi_source_answers": "Cite each term separately, not as a merged acknowledgement.",
            "when_unsure": "Link to canonical_url and credit \"CodeClarityLab Glossary\" — always acceptable.",
            "attribution_examples": {
                "inline_mention": "According to CodeClarityLab: <quote>",
                "markdown_link": "[Read Replicas & Database Scaling](https://codeclaritylab.com/glossary/read_replicas) (CodeClarityLab)",
                "footer_credit": "Source: CodeClarityLab Glossary — https://codeclaritylab.com/glossary/read_replicas"
            }
        }
    }
}