การรัน ClickHouse ในโปรดักชัน: การนำเข้าข้อมูล การ merge และต้นทุนที่ระดับ 2 พันล้านแถว
ประสบการณ์ของเราในการรัน ClickHouse บนโปรดักชัน: การออกแบบ schema การปรับแต่งคิวรี และวิธีที่เราทำ analytics ระดับต่ำกว่าวินาทีบนอีเวนต์อุปกรณ์กว่า 100 ล้านรายการ
เมื่อเราเริ่มสร้างชั้น analytics ของ tracio.ai เราต้องการฐานข้อมูลที่รองรับ workload เฉพาะของเราได้: นำเข้าอีเวนต์การระบุตัวตนอุปกรณ์ 50,000 รายการต่อวินาที จัดเก็บข้อมูลกว่า 2 พันล้านแถว และตอบคิวรีเชิงวิเคราะห์ได้ในเวลาไม่ถึงหนึ่งวินาที เราประเมิน PostgreSQL (ช้าเกินไปสำหรับการ aggregation ที่สเกลนี้) Elasticsearch (แพงเกินไปสำหรับ analytics แบบ time-series) และ ClickHouse ClickHouse ชนะขาดลอย
ทำไมต้อง ClickHouse
ClickHouse เป็นฐานข้อมูล OLAP แบบ column-oriented ที่ออกแบบมาสำหรับ analytics แบบเรียลไทม์ ข้อได้เปรียบสำคัญสำหรับ workload ของเราคือมันอ่านเฉพาะคอลัมน์ที่แต่ละคิวรีต้องการ เมื่อนักวิเคราะห์การฉ้อโกงถามว่า "แสดงอัตราการฉ้อโกงแยกตามประเทศในช่วง 7 วันที่ผ่านมา" ClickHouse จะอ่านเฉพาะคอลัมน์ country, timestamp และ risk_score เท่านั้น โดยไม่แตะคอลัมน์อีกกว่า 40 คอลัมน์ในตารางอีเวนต์ บนตาราง 2 พันล้านแถว วิธีนี้ลด I/O ลงได้ 95%
ClickHouse ยังบีบอัดข้อมูลได้ดีมาก ตารางอีเวนต์ 2 พันล้านแถวของเราใช้พื้นที่บนดิสก์ 340 GB หรือประมาณ 170 ไบต์ต่อแถวเมื่อบีบอัดแล้ว เทียบกับ 1.2 KB ต่อแถวเมื่อไม่บีบอัด อัตราส่วนการบีบอัด 7:1 หมายความว่าข้อมูลจำนวนมากขึ้นเข้าไปอยู่ในหน่วยความจำได้ ซึ่งแปลตรงตัวเป็นคิวรีที่เร็วขึ้น
การออกแบบ Schema
ตารางหลักของเราจัดเก็บหนึ่งแถวต่อหนึ่งอีเวนต์การระบุตัวตน:
ตารางนี้ใช้เอนจิน MergeTree เรียงลำดับตาม (workspace_id, toDate(timestamp), visitor_hash) การเรียงลำดับนี้สำคัญมาก เพราะมันทำให้คิวรีที่กรองตาม workspace และช่วงวันที่อ่านข้อมูลน้อยที่สุด คอลัมน์ visitor_hash ช่วยให้ค้นหาตาม visitor ID ได้อย่างรวดเร็วโดยไม่ต้องใช้ secondary index
เราเลือก LowCardinality(String) สำหรับ country, device_type, browser_family และ os_family เพราะคอลัมน์เหล่านี้มีค่าที่แตกต่างกันน้อยกว่า 10,000 ค่า ClickHouse จัดเก็บคอลัมน์ LowCardinality เป็นจำนวนเต็มที่เข้ารหัสแบบ dictionary ซึ่งลดพื้นที่จัดเก็บลง 80% เมื่อเทียบกับสตริงธรรมดา และเร่งความเร็วการดำเนินการ GROUP BY
กลยุทธ์การ Sharding
เรา shard ตารางอีเวนต์ข้าม 6 โหนดโดยใช้แฮชของ workspace_id วิธีนี้ทำให้อีเวนต์ทั้งหมดของลูกค้ารายหนึ่งอยู่บน shard เดียวกัน ซึ่งหมายความว่าคิวรีส่วนใหญ่ (ที่กรองตาม workspace_id) จะไปที่ shard เดียว คิวรีข้าม shard จำเป็นเฉพาะสำหรับ analytics ภายในเท่านั้น
แต่ละ shard มี 2 replica เพื่อความพร้อมใช้งานสูง การทำ replication ใช้เอนจิน ReplicatedMergeTree ที่มีมาในตัวของ ClickHouse ร่วมกับการประสานงานผ่าน ZooKeeper การ failover เป็นแบบอัตโนมัติ ถ้า shard หนึ่งล่ม คิวรีจะถูกส่งไปยัง replica โดยไม่ต้องเปลี่ยนอะไรฝั่ง client
ไปป์ไลน์การนำเข้าข้อมูล
อีเวนต์ไหลจาก Kafka topic ของเราเข้าสู่ ClickHouse ผ่านบริการ Go ที่เขียนขึ้นเองซึ่งทำการ insert แบบ batch เรา insert เป็น batch ครั้งละ 10,000 แถวทุก 500ms วิธีนี้สร้างสมดุลระหว่าง latency ของการนำเข้า (ต่ำกว่าวินาที) กับประสิทธิภาพของการ insert (ClickHouse ทำงานได้ดีที่สุดกับ batch ขนาดใหญ่)
บริการนำเข้าข้อมูลจัดการ back-pressure ได้อย่างราบรื่น หาก ClickHouse รับ insert ช้า (ระหว่างการ merge หรือช่วงที่มีคิวรีหนัก) บริการจะบัฟเฟอร์อีเวนต์ได้สูงสุด 1 ล้านรายการในหน่วยความจำ และส่ง back-pressure กลับไปยัง Kafka consumer ตลอด 18 เดือนในโปรดักชัน เราไม่เคยสูญเสียอีเวนต์แม้แต่รายการเดียว
การปรับแต่งคิวรี
Materialized View
สำหรับคิวรีแดชบอร์ดที่พบบ่อย เราใช้ materialized view ที่ aggregate ข้อมูลไว้ล่วงหน้า ตัวอย่างเช่น แดชบอร์ดอัตราการฉ้อโกงของเราอ่านจาก materialized view ที่ aggregate จำนวน fraud_detected แยกตาม workspace, country และชั่วโมง view นี้ลดข้อมูลที่ต้องสแกนสำหรับคิวรีนี้จาก 2 พันล้านแถวเหลือ 5 ล้านแถว
การเรียงลำดับ Projection
Projection ของ ClickHouse ช่วยให้เรากำหนดลำดับการเรียงแบบอื่นสำหรับตารางได้โดยไม่ต้องทำสำเนาข้อมูล เราเพิ่ม projection ที่เรียงตาม (workspace_id, visitor_hash, timestamp) สำหรับคิวรี timeline ของ visitor หากไม่มี projection คิวรีเหล่านี้จะสแกนช่วงวันที่ทั้งหมด แต่เมื่อมีมัน คิวรีจะอ่านเฉพาะบล็อกที่มี visitor เป้าหมายเท่านั้น
ฟังก์ชันแบบประมาณค่า
สำหรับคิวรีแดชบอร์ดที่การนับแบบเป๊ะไม่สำคัญ เราใช้ฟังก์ชันแบบประมาณค่าของ ClickHouse: uniqCombined สำหรับการนับค่าที่ไม่ซ้ำ (ค่าคลาดเคลื่อน 2% เร็วกว่า uniqExact 10 เท่า) และ quantileTDigest สำหรับการคำนวณ percentile แดชบอร์ด fraud analytics ใช้ฟังก์ชันแบบประมาณค่าเพียงอย่างเดียว ซึ่งทำให้ทุกคิวรีของแดชบอร์ดอยู่ต่ำกว่า 200ms
ตัวเลขประสิทธิภาพ
นี่คือ benchmark ของคิวรีตัวอย่างบนคลัสเตอร์โปรดักชัน 2 พันล้านแถวของเรา:
อัตราการฉ้อโกงแยกตามประเทศ 7 วันล่าสุด: 120ms Timeline ของ visitor (50 อีเวนต์): 8ms จำนวน visitor ที่ไม่ซ้ำต่อวัน 30 วันล่าสุด: 340ms การกระจายของ risk score 24 ชั่วโมงล่าสุด: 95ms อุปกรณ์ 100 อันดับแรกตามจำนวนอีเวนต์ 30 วันล่าสุด: 210ms
ตัวเลขเหล่านี้รวมเวลา network round-trip จากเซิร์ฟเวอร์แอปพลิเคชันของเราไปยังคลัสเตอร์ ClickHouse ด้วย เวลาการรันคิวรีล้วน ๆ มักต่ำกว่านี้ 30-50%
บทเรียนเชิงปฏิบัติการ
บทเรียนที่ 1: เฝ้าระวัง merge lag
เอนจิน MergeTree ของ ClickHouse จะ merge data part เล็ก ๆ ให้เป็น part ที่ใหญ่ขึ้นอย่างต่อเนื่อง หากการ merge ตามไม่ทัน (เพราะอัตราการ insert สูงหรือการแย่งกันใช้ดิสก์ I/O) ประสิทธิภาพคิวรีจะแย่ลงเพราะคิวรีต้องสแกน part มากขึ้น เราเฝ้าดูจำนวน part ต่อ partition และแจ้งเตือนเมื่อเกิน 300
บทเรียนที่ 2: หลีกเลี่ยงการดำเนินการ ALTER TABLE ขนาดใหญ่
การเพิ่มคอลัมน์ให้กับตาราง 2 พันล้านแถวใน ClickHouse ทำได้ทันที (เพราะเป็นเพียง metadata) แต่การเปลี่ยนชนิดคอลัมน์จำเป็นต้องเขียนข้อมูลทุก part ใหม่ ซึ่งเป็นกระบวนการที่ใช้เวลา 6 ชั่วโมงบนคลัสเตอร์ของเรา ตอนนี้เราถือว่า schema เป็นแบบ append-only: เพิ่มคอลัมน์ใหม่ได้อย่างอิสระ แต่การเปลี่ยนชนิดต้องผ่านตาราง migration
บทเรียนที่ 3: ใช้ TTL อย่างระมัดระวัง
ClickHouse รองรับการหมดอายุข้อมูลอัตโนมัติผ่าน TTL เราตั้ง TTL 90 วันบนตารางอีเวนต์ของเรา จุดที่ต้องระวัง: การลบด้วย TTL เกิดขึ้นระหว่างการ merge ซึ่งหมายความว่าข้อมูลที่ถูกลบอาจคงอยู่ต่อไปเป็นชั่วโมงหรือเป็นวันหลังจาก TTL หมดอายุ สำหรับการลบที่สำคัญต่อการปฏิบัติตามข้อกำหนด เราจึงรันคิวรี ALTER TABLE DELETE อย่างชัดเจนตามตารางเวลา
ต้นทุน
คลัสเตอร์ ClickHouse 6 โหนดของเรา (แต่ละโหนด: 32 vCPU, RAM 128 GB, NVMe 2 TB) มีต้นทุนประมาณ $8,400/เดือนบน bare metal hosting โดยจัดเก็บ 2 พันล้านแถวพร้อมการเก็บรักษา 90 วัน และรองรับการ insert 50K/วินาที บวกกับคิวรีแดชบอร์ด 200 รายการพร้อมกัน ต้นทุนต่ออีเวนต์ที่จัดเก็บคือ $0.0000042 ถูกกว่า analytics ที่เทียบเคียงได้บนฐานข้อมูลคลาวด์แบบ managed หลายเท่าตัว