ClickHouse-Client: Wie ich mich mit der Konsole und der HTTP-API in einem Glücksspielprojekt anfreundete
Mein erster Versuch an einer verschlossenen Tür
Ich erinnere mich, nach der Installation von ClickHouse tippte ich fröhlich clickhouse-client und bekam einen Fehler: Code: 210. DB::NetException: Connection refused (localhost:9000). Es stellte sich heraus, dass der Server nur auf 127.0.0.1 lauschte, und ich versuchte, mich von einem anderen Rechner aus zu verbinden. Eine Stunde Googeln, Bearbeiten von config.xml, Neustarten – und erst dann lernte ich, dass der Client die Flags --host und --port hat.
ClickHouse bietet zwei Kommunikationswege: den nativen Client (für Menschen und Skripte) und die HTTP-API (für alles andere). Ich nutze beide täglich. Im Folgenden finden Sie alles, was Sie wirklich brauchen, plus die Fallstricke, auf die ich getreten bin.
Verbindungsmethoden: Von einfach bis richtig
Methode 1. Naiv (nur localhost)
clickhouse-client
Dies funktioniert nur, wenn Sie sich auf demselben Rechner wie der Server befinden und Port 9000 nicht geändert haben. In der Produktion macht das niemand.
Methode 2. Professionell: Flags für Fernzugriff
clickhouse-client \
--host analytics.prod.company.com \
--port 9000 \
--user analyst \
--password 'StrongPass123' \
--database betting
Was ich aus der Produktion gelernt habe: Geben Sie niemals ein Passwort in der Befehlszeile an, wenn die Bash-Verlaufsfunktion aktiviert ist. Verwenden Sie stattdessen eine Konfigurationsdatei.
Methode 3. Richtig: Konfigurationsdatei
Erstellen Sie ~/.clickhouse-client/config.xml:
<config>
<host>clickhouse.prod.internal</host>
<port>9000</port>
<user>analyst</user>
<password>${CLICKHOUSE_PASSWORD}</password>
<database>betting</database>
<history_file>/home/user/.clickhouse-client-history</history_file>
</config>
Passwort über Umgebungsvariable:
export CLICKHOUSE_PASSWORD="StrongPass123"
clickhouse-client
Warum das sicherer ist: Das Passwort landet nicht in ps aux oder im Verlauf. In der Produktion hatten wir einen Fall, bei dem ein Entwickler clickhouse-client --password secret ausführte, und innerhalb einer Stunde konnte jeder das Passwort in den Orchestrator-Logs sehen.
Methode 4. HTTP-Verbindung (Alternative für CI/CD)
Für die Automatisierung verwende ich oft HTTP:
curl -u analyst:StrongPass123 \
"http://clickhouse.prod.internal:8123/?query=SELECT+1"
Interaktiv vs. Batch: Wann was verwenden
Interaktiver Modus (für Menschen)
clickhouse-client
Vorteile: Autovervollständigung (Tab-Taste), Befehlsverlauf, mehrzeilige Abfragen. Nachteile: Nicht für Skripte geeignet.
:) SELECT user_id, sum(amount) FROM bets GROUP BY user_id LIMIT 5;
Mein Life-Hack: Im interaktiven Modus funktionieren Abkürzungen wie \l (Datenbanken auflisten), \d (Tabellen auflisten), \c betting (Datenbank wechseln). Nicht jeder weiß das, aber es spart enorm viel Zeit.
Batch-Modus (für Skripte und Cron)
# Einzelner Befehl
clickhouse-client --query "SELECT count() FROM betting.bets"
# Aus Datei
clickhouse-client --queries-file /path/to/analytics.sql
# Mehrzeilig via heredoc
clickhouse-client <<SQL
SELECT
toDate(created_at) AS day,
count() AS bets
FROM betting.bets
WHERE created_at >= today() - 7
GROUP BY day
ORDER BY day;
SQL
Was mich verbrannt hat: Im Batch-Modus müssen Sie Ihre Abfrage immer mit einem Semikolon beenden. Ohne wird der Befehl nicht ausgeführt, aber es wird auch kein Fehler angezeigt – er hängt einfach. Wir haben eine Stunde damit verbracht, Cron-Jobs zu debuggen.
HTTP-API: curl ist Ihr bester Freund
Das HTTP-Interface läuft auf Port 8123. Es ist perfekt für Microservices, Dashboards und Skripte in jeder Sprache.
GET-Anfragen: Einfach und schnell
# Einfachste Abfrage
curl "http://localhost:8123/?query=SELECT+version()"
# Mit Authentifizierung
curl -u user:pass "http://localhost:8123/?query=SELECT+count()+FROM+betting.bets"
# Mit Datenbankparameter
curl "http://localhost:8123/?database=betting&query=SELECT+count()+FROM+bets"
POST-Anfragen: Für große Abfragen und Dateneinfügung
# Lange Abfrage via POST (keine URL-Längenbegrenzung)
curl -X POST "http://localhost:8123/" \
-d "SELECT user_id, sum(amount) FROM betting.bets GROUP BY user_id"
# Daten via POST einfügen
curl -X POST "http://localhost:8123/?query=INSERT+INTO+betting.bets+FORMAT+CSV" \
--data-binary @bets_data.csv
Antwortformate: Wählen Sie für Ihre Aufgabe
ClickHouse kann Daten in vielen Formaten zurückgeben. Ich habe alle ausprobiert – hier ist, was Sie wirklich brauchen:
# Pretty – für Menschen (lesbar, aber viele Formatierungszeichen)
curl "http://localhost:8123/?query=SELECT+user_id,amount+FROM+bets+LIMIT+3&default_format=Pretty"
# JSON – für APIs (überall parsbar)
curl "http://localhost:8123/?query=SELECT+user_id,amount+FROM+bets+LIMIT+3&default_format=JSON"
# JSONEachRow – für zeilenweise Verarbeitung (speicherschonend)
curl "http://localhost:8123/?query=SELECT+user_id,amount+FROM+bets+LIMIT+3&default_format=JSONEachRow"
# CSV – für Export nach Excel/Google Sheets
curl "http://localhost:8123/?query=SELECT+user_id,amount+FROM+bets+LIMIT+3&default_format=CSV"
# TabSeparated – für Weiterleitung an andere Tools (grep, awk)
curl "http://localhost:8123/?query=SELECT+user_id,amount+FROM+bets+LIMIT+3&default_format=TSV"
Praxisbeispiel: Wir senden Aggregate an einen Telegram-Bot. Wir verwenden JSONEachRow, parsen es in Python mit einem einzigen response.json() und formatieren es in eine Nachricht.
Erstellen einer Datenbank für eine Wettplattform
CREATE DATABASE IF NOT EXISTS betting;
Und sofort zu ihr wechseln:
clickhouse-client --database betting
Oder innerhalb des Clients:
USE betting;
Erste Tabelle: Wett-Schema aus einem echten Projekt
In meinem Produktionsprojekt für Wettanalysen sieht die Tabelle so aus:
CREATE TABLE betting.bets
(
user_id UInt64,
created_at DateTime64(3),
amount Decimal(18, 2),
odds Float64,
sport LowCardinality(String),
outcome Enum8('win' = 1, 'loss' = 2, 'void' = 3),
event_id UInt64,
bet_type String
)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(created_at)
ORDER BY (created_at, user_id);
Warum so:
LowCardinalityfür Sport – Fußball, Basketball, Tennis. Sie wiederholen sich tausendfach, komprimiert in ein Wörterbuch.Enum8für Ergebnis – nur drei Werte, benötigt 1 Byte statt eines Strings.DateTime64(3)– Millisekunden sind wichtig für Live-Wettanalysen.
Häufiger Anfängerfehler: Vergessen, ENGINE = MergeTree() anzugeben. Ohne dies erstellt ClickHouse eine Tabelle mit der TinyLog-Engine (nur zum Testen), die nicht partitioniert werden kann und keine Replikation unterstützt. In der Produktion würde das Einfügen von 10 Millionen Zeilen in eine solche Tabelle sie zerstören.
Einfügen von Testdaten
Einzelner Datensatz
INSERT INTO betting.bets (user_id, created_at, amount, odds, sport, outcome, event_id, bet_type)
VALUES (1001, now(), 50.00, 2.1, 'football', 'win', 50001, 'single');
Mehrere Datensätze (Batch-Einfügung)
INSERT INTO betting.bets VALUES
(1002, now() - INTERVAL 1 HOUR, 100.00, 1.8, 'basketball', 'loss', 50002, 'single'),
(1003, now() - INTERVAL 2 HOUR, 200.00, 3.0, 'tennis', 'win', 50003, 'express'),
(1001, now() - INTERVAL 30 MINUTE, 75.00, 2.5, 'football', 'void', 50001, 'single');
Generieren von Testdaten mit numbers()
Für Auslastungstests generiere ich oft eine Million Datensätze spontan:
INSERT INTO betting.bets
SELECT
number % 10000 AS user_id,
now() - INTERVAL (number % 86400) SECOND,
(number % 1000) / 10 + 10,
1.5 + (number % 200) / 100,
arrayElement(['football', 'basketball', 'tennis', 'hockey'], (number % 4) + 1),
CAST((number % 3) + 1 AS Enum8('win' = 1, 'loss' = 2, 'void' = 3)),
number,
'single'
FROM numbers(1000000);
Wichtiger Hinweis: Diese Einfügung wird auf einem anständigen Server 5-10 Sekunden dauern. ClickHouse ist für solche Massenvorgänge optimiert, aber auf einer schwachen VM kann es eine Minute dauern.
Grundlegende SELECT-Abfragen im Kontext von Wetten
WHERE – Filtern
-- Wetten eines bestimmten Benutzers in der letzten Stunde
SELECT *
FROM betting.bets
WHERE user_id = 1001
AND created_at >= now() - INTERVAL 1 HOUR;
-- Gewinnwetten mit Quoten über 2.0
SELECT user_id, amount, odds, amount * odds AS payout
FROM betting.bets
WHERE outcome = 'win' AND odds > 2.0;
ORDER BY – Sortieren
-- Größte Wetten heute
SELECT user_id, amount, created_at
FROM betting.bets
WHERE created_at >= today()
ORDER BY amount DESC
LIMIT 10;
-- Letzte 5 Wetten eines Benutzers
SELECT created_at, sport, amount, odds, outcome
FROM betting.bets
WHERE user_id = 1001
ORDER BY created_at DESC
LIMIT 5;
GROUP BY – Analysen
-- Auszahlungen nach Sportart für die Woche
SELECT
sport,
count() AS total_bets,
sum(amount) AS total_staked,
sumIf(amount * odds, outcome = 'win') AS total_payout,
round(total_payout / total_staked, 4) AS roi
FROM betting.bets
WHERE created_at >= today() - 7
GROUP BY sport
ORDER BY total_bets DESC;
Kombinieren von Bedingungen
-- Benutzer, die mehr als 10 Wetten an einem Tag platziert haben
SELECT
user_id,
count() AS bets_count,
sum(amount) AS total_amount
FROM betting.bets
WHERE created_at >= today()
GROUP BY user_id
HAVING bets_count > 10
ORDER BY total_amount DESC;
Praxisbeispiel: Verdächtige Muster finden
Hier ist eine echte Abfrage aus unserem Betrugserkennungssystem:
WITH hourly_bets AS (
SELECT
user_id,
toStartOfHour(created_at) AS hour,
count() AS bets_per_hour,
avg(amount) AS avg_bet
FROM betting.bets
WHERE created_at >= now() - INTERVAL 3 HOUR
GROUP BY user_id, hour
)
SELECT
user_id,
max(bets_per_hour) AS max_rate,
avg(avg_bet) AS typical_bet,
stddevPop(avg_bet) AS bet_variance
FROM hourly_bets
GROUP BY user_id
HAVING max_rate > 100 AND bet_variance < 0.5;
Diese Abfrage findet Bots: solche, die mehr als 100 Mal pro Stunde mit fast identischen Beträgen wetten. In PostgreSQL mit 50 Millionen Datensätzen würde sie nie fertig werden. ClickHouse liefert die Antwort in 0,6 Sekunden.
Exportieren von Daten: Wenn Sie sie dem Geschäft zur Verfügung stellen müssen
# Export als CSV für die Marketingabteilung
clickhouse-client --query "
SELECT user_id, sum(amount) AS total_bet, count() AS bet_count
FROM betting.bets
WHERE created_at >= '2024-01-01'
GROUP BY user_id
ORDER BY total_bet DESC
LIMIT 1000
" --format CSV > top_users.csv
# Export als JSON für die API eines anderen Dienstes
curl "http://localhost:8123/?query=SELECT+user_id,sum(amount)+FROM+betting.bets+GROUP+BY+user_id+LIMIT+10&default_format=JSON" \
-o top_users.json
Häufige Fehler und ihre Lösungen
Fehler: Code: 102. DB::NetException: Connection refused
Ursache: Falscher Host oder Port, oder der Server lauscht nicht auf externe Verbindungen.
Lösung: Überprüfen Sie netstat -tulpn | grep clickhouse. Wenn keine 0.0.0.0:9000 vorhanden ist, bearbeiten Sie config.xml:
<listen_host>0.0.0.0</listen_host>
Fehler: Code: 81. DB::Exception: Database betting doesn't exist
Ursache: Datenbank nicht erstellt oder --database nicht angegeben.
Lösung: CREATE DATABASE IF NOT EXISTS betting; oder verbinden mit --database betting.
Fehler: Code: 62. DB::Exception: Syntax error: failed at position 1
Ursache: Semikolon im Batch-Modus vergessen.
Lösung: Setzen Sie immer ; am Ende der Abfrage, wenn Sie --query verwenden.
Wie geht es weiter?
Jetzt wissen Sie, wie Sie sich auf jede Art mit ClickHouse verbinden, Tabellen erstellen, Daten einfügen und Abfragen ausführen. Im nächsten Artikel tauchen wir in fortgeschrittene Analysen ein: Fensterfunktionen, Arrays, Aggregationen und materialisierte Ansichten.
Alle Beispiele getestet mit ClickHouse 24.8. Wenn etwas nicht funktioniert, überprüfen Sie zuerst die Version: SELECT version(); Das hat mir hunderte Male geholfen.
← Vorherige: ClickHouse in Docker: Wie ich aufhörte, mir Sorgen zu machen und Analysen in 2 Minuten startete
→ Nächste: ClickHouse: Vollständige Datentyp-Referenz für Wettanalysen (Woran ich mich verbrannt habe)
— Editorial Team
Noch keine Kommentare.