$ lexprog.com

// notes from an old coder -- php, databases, and the occasional rant

[April 11, 2026] ClickHouse

ClickHouse for Laravel: Introduction

ClickHouse for Laravel: Introduction

────────────────────────────────────────────────────────

ClickHouse for Laravel: Introduction

Tip: ClickHouse is for Analytics, Not Transactions

Don't use ClickHouse as your primary database. Use it alongside PostgreSQL/MySQL for analytics.

Gotcha: No Updates or Deletes (Easily)

ClickHouse is optimized for inserts and reads. Updates and deletes are expensive. Design for append-only.

Tip: Installation

composer require smi2/phpclickhouse

Gotcha: HTTP Interface, Not TCP

ClickHouse uses port 8123 (HTTP), not 9000 (TCP). The PHP client uses HTTP.

Tip: Configuration

'clickhouse' => [
    'driver' => 'clickhouse',
    'host' => env('CLICKHOUSE_HOST', 'localhost'),
    'port' => env('CLICKHOUSE_PORT', 8123),
    'database' => env('CLICKHOUSE_DATABASE', 'default'),
],

Gotcha: No Foreign Keys

ClickHouse doesn't enforce foreign keys. Data integrity is your responsibility.

Tip: Creating Tables

DB::connection('clickhouse')->statement("
    CREATE TABLE page_views (
        id UInt64,
        url String,
        user_id UInt64,
        created_at DateTime
    ) ENGINE = MergeTree()
    ORDER BY (created_at, url)
");

Gotcha: Primary Key = Sorting Key

In ClickHouse, the ORDER BY clause defines both the primary key and the sort order. Choose wisely.

Tip: Order of Columns in ORDER BY Matters Massively

ClickHouse's primary key is defined by ORDER BY. Put high-cardinality columns first for better data skipping. ORDER BY (timestamp, user_id) is very different from ORDER BY (user_id, timestamp) in query performance.

Tip: Use LowCardinality for Enum-Like Strings

Strings like status, country, browser benefit from LowCardinality(String) — it's stored as a dictionary internally, reducing storage 10x and speeding up scans.

Gotcha: Mutations Are Heavy

ALTER TABLE ... UPDATE and DELETE in ClickHouse create new parts instead of modifying in place. A single mutation on a large table can take hours and block merges. Design for append-only from day one.

Senior Insight

Introducing ClickHouse into a Laravel stack is a significant architectural decision. ClickHouse is not a transactional database — it's an analytical column-store. I've seen teams try to use ClickHouse as a drop-in MySQL replacement and fail spectacularly. The right approach: keep PostgreSQL/MySQL for transactional data, and use ClickHouse as a dedicated analytics layer. I set up a nightly ETL pipeline that exports aggregated data from the OLTP database to ClickHouse for reporting.

Source: ClickHouse Blog (https://clickhouse.com/blog), Altinity Blog (https://altinity.com/blog), Altinity Knowledge Base (https://kb.altinity.com/)

────────────────────────────────────────────────────────
<-- back to posts