<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>VicWen</title><description>Blog about my life</description><link>https://vicwen.app/</link><language>zh_TW</language><item><title>【SWE 實習回顧】在 FXG 的那些時光</title><link>https://vicwen.app/posts/swe-internship-at-fxg/</link><guid isPermaLink="true">https://vicwen.app/posts/swe-internship-at-fxg/</guid><pubDate>Sat, 28 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;我是 Vic，最近剛從前公司結束實習，回歸部落格來整理一下思緒，在忙錄的開學週忙裡偷閒一下，闡述一下這幾個月的經歷。&lt;/p&gt;
&lt;p&gt;我在 2025 年 7 月到 2026 年 2 月在FXG擔任軟體工程實習生（後端），前期是 on-site（summer），中後期轉 Hybrid（一週去個兩天），皆是 40hr/week，所以開學後對於工作和課業之間的平衡下了一番功夫，幸好最後課業顧得還不錯，擦到書卷的邊邊。&lt;/p&gt;
&lt;p&gt;最近有人問我：「Vic，你為什麼當初決定要去實習啊？」暑假在家裡耍廢不是很舒服嗎（其實我也這麼覺得），那為什麼還要去實習呢？&lt;/p&gt;
&lt;p&gt;對於我來說，學界跟業界是有脫軌的，所以我很想多接觸業界，看看整個開發流程、Tech stack 的差異性、節奏上是不是我所想像的那樣，另一方面也是因為自己閒不太下來（還是會有罪惡感，，，），所以就跑去實習啦！&lt;/p&gt;
&lt;h2&gt;面試過程&lt;/h2&gt;
&lt;p&gt;其實我前面面過幾間新創，都是 Backend 的職缺，基本上都會被問到 System Design 的問題，所以這次的面試過程滿順利的，也聊的很愉快。&lt;/p&gt;
&lt;p&gt;前面就是先看履歷，針對 Side project、語言的熟悉度做詢問，這部分大概花了五分鐘在描述專案細節和我在裡面負責的角色；接著就是問 System Design 的相關問題：「如果要在一秒內做 xxxx 事情，你會怎麼做？」，總之就是 MQ 跟分散式架構的問題，接著第二個問題是：「Kakfa 跟 RabbitMQ 的差別」，啊因為有準備，所以答得很順利，於是就順利入職啦～&lt;/p&gt;
&lt;h2&gt;福利&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;彈性半小時上班打卡&lt;/li&gt;
&lt;li&gt;每週二有午餐吃、每週四有下午茶可以吃（~我都這兩天去蹭吃的~）&lt;/li&gt;
&lt;li&gt;辦公室都會放零食餅乾，有時候同事出國玩也會帶一些食物到公司請大家吃&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;工作內容&lt;/h2&gt;
&lt;p&gt;我主要是做後端，所以前期就是做整合兩個內部工具並優化，中期轉為建構 Data infra，處理雲地同步、ETL pipeline、各式自動化報表，後期轉為優化單體架構和微服務設計。&lt;/p&gt;
&lt;h3&gt;前期&lt;/h3&gt;
&lt;p&gt;由於這兩個內部工具是圍繞在 Backend Team 跟 Data Team，所以常常要在這兩個部門之間周旋，並且&lt;strong&gt;有時候大家也不知道自己的需求跟期望是什麼&lt;/strong&gt;，所以需要大量的溝通確定細節，並且為他想要的這個功能確定可行性（有時候太天馬行空了），幫大家收斂需求和規劃實作流程，否則很有可能會發生辛苦了一整天，最後卻做白工的慘況。&lt;/p&gt;
&lt;p&gt;關於技術方面，從資料表設計、API 設計、查詢優化（專案是讀取多於寫入）皆是我一人包辦，大概設計了 60+ 支 API，透過不斷迭代最終完成了該專案。&lt;/p&gt;
&lt;p&gt;在這段時期學到最多的是&lt;strong&gt;跨部門溝通所需的溝通和分析能力&lt;/strong&gt;。&lt;/p&gt;
&lt;h3&gt;中期&lt;/h3&gt;
&lt;p&gt;在結束了前期的專案後，就跑去協助 Data Team 的基礎建設了，包含採用 ClickHouse (OLAP) 作為資料庫以降低儲存成本及加快查詢效率，還有採用 Apache Airflow 作為自動化排程平台，由此為基礎開始許多專案的設計和實作，接觸到十分大量的資料、SQL 設計和優化，也讓我了解到資料的有趣性，算是很有趣的一段經歷。&lt;/p&gt;
&lt;p&gt;由於我原本對於 ClickHouse 和 Apache Airflow 是完全不熟悉的，也成為讓我踏入 2025 iThome 鐵人挑戰賽（&lt;a href=&quot;https://ithelp.ithome.com.tw/users/20168031/ironman/8221&quot;&gt;ClickHouse 系列：從資料庫底層架構到軟體應用實踐&lt;/a&gt;）和擁抱開源的入門磚。&lt;/p&gt;
&lt;p&gt;中間有個小插曲是，因為我是草台班子，前期在設計架構上沒有考慮到擴充性，所以導致業務邏輯、Airflow 都在同一個 repo 下，導致專案日漸龐大，套件彼此衝突，所以花了一週研究、重新設計架構（網路上好少架構範例 QQ），最後是採用 &lt;code&gt;DockerOperator&lt;/code&gt; 作為任務，將業務邏輯拆成一個個 repo 並包裝成 Image，搭配 CLI 介面驅動和 CI/CD，實現自動更版，增強了可讀性、可維護性，對此還是滿自豪的！&lt;/p&gt;
&lt;p&gt;另外我也十分注重軟體開發流程、測試和文件（沒有 Policy 的專案絕對會是個災難），所以當時有位 New Grad 加入我這個專案，透過文件讓他能夠快速上手，並且保持高效的開發能量，還是挺開心的。&lt;/p&gt;
&lt;p&gt;雖然在這段時期上層一些有點弔詭的事情，導致開發進度緩慢、需要繞一大圈才能完成功能、導致系統逐漸不穩定，但這畢竟是她的課題，我也無法干涉囉。&lt;/p&gt;
&lt;p&gt;最後，在這段時期學到最多的是&lt;strong&gt;如何設計架構、SQL 撰寫和優化、Airflow / ClickHouse 的使用&lt;/strong&gt;。&lt;/p&gt;
&lt;h3&gt;後期&lt;/h3&gt;
&lt;p&gt;當中期的內容趨於穩定並 release 版本後，我便擔任 Maintainer 負責審 PR，將開發重心轉為後端優化和開發微服務架構了。&lt;/p&gt;
&lt;h2&gt;學到了什麼？&lt;/h2&gt;
&lt;h3&gt;技術&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;技術能力（Backend、Data、Database...）&lt;/li&gt;
&lt;li&gt;工具使用&lt;/li&gt;
&lt;li&gt;產業知識&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;軟實力&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;跨部門溝通能力&lt;/li&gt;
&lt;li&gt;時間管理&lt;/li&gt;
&lt;li&gt;抗壓性&lt;/li&gt;
&lt;li&gt;問問題的能力&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;心態轉變&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;從學生思維 → 職場思維&lt;/li&gt;
&lt;li&gt;從害怕犯錯 → 接受不完美&lt;/li&gt;
&lt;li&gt;從被動 → 主動&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;一些想法&lt;/h2&gt;
&lt;p&gt;原本對於 Backend、Data 其實都覺得不錯，什麼都想碰。但真正進到業界之後，我發現自己特別享受的是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;設計系統架構時的思考過程&lt;/li&gt;
&lt;li&gt;把混亂需求收斂成可實作方案&lt;/li&gt;
&lt;li&gt;優化效能、重構專案時那種「變乾淨」的成就感&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我發現自己其實很喜歡偏 &lt;strong&gt;Backend + Data Infra&lt;/strong&gt; 這一塊，尤其是牽涉到架構設計與效能優化的問題。
比起單純 CRUD，我更享受怎麼讓這件事可以 scale。&lt;/p&gt;
&lt;h3&gt;更確定自己喜歡／不喜歡什麼？&lt;/h3&gt;
&lt;p&gt;這題其實比想像中重要。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;我喜歡：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;有 ownership 的專案&lt;/li&gt;
&lt;li&gt;可以自己設計、自己決策的空間&lt;/li&gt;
&lt;li&gt;文件齊全、流程清楚的團隊&lt;/li&gt;
&lt;li&gt;技術討論是理性而不是情緒導向&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;我不喜歡：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;方向反覆橫跳&lt;/li&gt;
&lt;li&gt;為了政治因素繞遠路&lt;/li&gt;
&lt;li&gt;沒有決策者負責收斂需求&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但這些「不喜歡」其實也讓我意識到&lt;strong&gt;工程師不是只寫程式，而是處在一個複雜組織系統裡&lt;/strong&gt;，畢竟技術可以優化，但組織問題有時候更難解。&lt;/p&gt;
&lt;h3&gt;給未來自己的提醒&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;不要因為變熟練就停止思考架構&lt;/li&gt;
&lt;li&gt;永遠保留重構的勇氣&lt;/li&gt;
&lt;li&gt;繼續寫文章、整理知識、勇於分享給他人！&lt;/li&gt;
&lt;li&gt;不要因為別人走得快就焦慮&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;還有一點很重要的是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;別把自己困在「實習生」的心態裡。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;人的成長是無限的，千萬不要用「我只是學生」來降低自己的標準。&lt;/p&gt;
&lt;h2&gt;給未來實習生建議&lt;/h2&gt;
&lt;p&gt;如果你也準備踏入實習，我會給你幾個真心建議。&lt;/p&gt;
&lt;h3&gt;該準備什麼？&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1. 基本功要扎實&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SQL 優化觀念&lt;/li&gt;
&lt;li&gt;RESTful API 設計&lt;/li&gt;
&lt;li&gt;Git flow&lt;/li&gt;
&lt;li&gt;基本系統設計概念（MQ、快取、分散式）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;2. 文件閱讀能力&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;很多時候不是你不會，是你不願意看文件，如果會自己查資料、debug、trace log，比什麼都重要。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. 溝通能力&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;你會發現技術問題常常不是卡在技術，而是卡在需求不清楚。&lt;/p&gt;
&lt;h3&gt;心態上要知道什麼？&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;你一定會犯錯&lt;/li&gt;
&lt;li&gt;你一定會寫出爛 code&lt;/li&gt;
&lt;li&gt;你一定會被 challenge&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但這些都很正常。&lt;/p&gt;
&lt;p&gt;真正拉開差距的不是天分，而是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;你能不能在犯錯後，系統性地修正自己。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;千萬不要做什麼？&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;不要只接簡單任務躲起來&lt;/li&gt;
&lt;li&gt;不要裝懂&lt;/li&gt;
&lt;li&gt;不要悶著不問&lt;/li&gt;
&lt;li&gt;不要把問題丟回去卻沒有自己的思考&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;問問題之前，至少要能說：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;「我試過 A、B、C，現在卡在 D，你覺得方向對嗎？」&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;結語&lt;/h2&gt;
&lt;p&gt;從 2025 年 7 月到 2026 年 2 月，這段時間不算長。&lt;/p&gt;
&lt;p&gt;我學會的不只是技術，而是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;怎麼面對不確定&lt;/li&gt;
&lt;li&gt;怎麼在壓力下保持輸出&lt;/li&gt;
&lt;li&gt;怎麼承認自己還不夠強，但仍然持續往前&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;未來還很長，但至少現在的我，比半年前更清楚自己想成為什麼樣的工程師，也比半年前更不害怕走慢一點。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;— Vic&lt;/p&gt;
</content:encoded></item><item><title>ClickHouse 系列：從原始碼看 MergeTree 的六大核心機制</title><link>https://vicwen.app/posts/clickhouse-mergetree-sourcecode-introduction/</link><guid isPermaLink="true">https://vicwen.app/posts/clickhouse-mergetree-sourcecode-introduction/</guid><pubDate>Wed, 03 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在前 29 天的系列文章中，我們已經從使用者角度理解了 ClickHouse 的表引擎設計：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;為什麼用列式存儲？&lt;/li&gt;
&lt;li&gt;MergeTree 不使用 B-tree，是如何依靠 min-max 索引做到快速查詢？&lt;/li&gt;
&lt;li&gt;背景合併、物化檢視、TTL 是如何發揮作用？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;今天作為系列收尾篇，我們要從 &lt;strong&gt;開發者角度&lt;/strong&gt; 帶大家走進 ClickHouse GitHub 原始碼，探索 MergeTree 的內部結構。&lt;/p&gt;
&lt;p&gt;我從茫茫 code 海中挑出了 6 個最重要的模組與函式（我愛 GPT 🤚😭🤚），對應 MergeTree 的「一生」：從&lt;strong&gt;建立 → 插入 → 合併 → 查詢 → 搬移&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;::github{repo=&quot;clickhouse/clickhouse&quot;}&lt;/p&gt;
&lt;p&gt;本篇建議各位在電腦上看，開兩個視窗分別對應，函數都會特別提醒位置。&lt;/p&gt;
&lt;h2&gt;初始化與格式管理&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;MergeTreeData::initializeDirectoriesAndFormatVersion(...)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;該函式在 &lt;a href=&quot;https://github.com/ClickHouse/ClickHouse/blob/master/src/Storages/MergeTree/MergeTreeData.cpp#L381&quot;&gt;src/Storages/MergeTree/MergeTreeData.cpp&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;這個函式負責 Table 的資料目錄初始化。&lt;/li&gt;
&lt;li&gt;功能：
&lt;ul&gt;
&lt;li&gt;檢查/建立資料目錄&lt;/li&gt;
&lt;li&gt;讀取或寫入 &lt;code&gt;format_version.txt&lt;/code&gt; (寫入至第一個 non-readonly 磁碟中)&lt;/li&gt;
&lt;li&gt;檢查是否支援 &lt;strong&gt;custom partitioning&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;設計：
&lt;ul&gt;
&lt;li&gt;確保磁碟資料與程式版本相容&lt;/li&gt;
&lt;li&gt;若版本過舊，直接丟 Exception 阻止啟動&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以下是寫入 &lt;code&gt;format_version.txt&lt;/code&gt; 的部分程式碼，有解釋到 write once disk 幾乎是等於 read-only（例如 S3 object storage），我們不能寫入在裡面，因為不支援移動或刪除，避免後續 DROP 留下垃圾，造成空間浪費。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/// When data path or file not exists, ignore the format_version check
if (!attach || !read_format_version)
{
    format_version = min_format_version;

    /// Try to write to first non-readonly disk
    for (const auto &amp;amp; disk : getStoragePolicy()-&amp;gt;getDisks())
    {
        if (disk-&amp;gt;isBroken())
            continue;

        /// Write once disk is almost the same as read-only for MergeTree,
        /// since it does not support move, that is required for any
        /// operation over MergeTree, so avoid writing format_version.txt
        /// into it as well, to avoid leaving it after DROP.
        if (!disk-&amp;gt;isReadOnly() &amp;amp;&amp;amp; !disk-&amp;gt;isWriteOnce())
        {
            auto buf = disk-&amp;gt;writeFile(format_version_path, 16, WriteMode::Rewrite, getContext()-&amp;gt;getWriteSettings());
            writeIntText(format_version.toUnderType(), *buf);
            buf-&amp;gt;finalize();
            if (getContext()-&amp;gt;getSettingsRef()[Setting::fsync_metadata])
                buf-&amp;gt;sync();
        }

        break;
    }
}
else
{
    format_version = *read_format_version;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;沒有這一步，後面所有 parts 都無法正確讀寫。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Parts 管理 (Immutable data parts)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;代表函式&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;MergeTreeData::loadDataParts(...)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MergeTreeData::getDataParts(...)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MergeTreeData::renameTempPartAndReplace(...)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在 MergeTree 中，&lt;strong&gt;資料不是一張完整的大表，而是一個個 Immutable parts&lt;/strong&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;啟動時&lt;/strong&gt;：&lt;code&gt;loadDataParts&lt;/code&gt; 掃描磁碟，把所有 parts 載入記憶體索引結構。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;插入資料&lt;/strong&gt;：每次 INSERT 會先生成一個臨時 part，寫完後再 &lt;code&gt;renameTempPartAndReplace&lt;/code&gt;，正式掛到 parts 目錄。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;查詢時&lt;/strong&gt;：透過 &lt;code&gt;getDataParts&lt;/code&gt; 取得一致性的 parts 視圖（支援 snapshot 概念）。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;這就是為什麼 MergeTree 能同時支援 &lt;strong&gt;高效讀取&lt;/strong&gt; 與 &lt;strong&gt;高併發寫入&lt;/strong&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;MergeTreeData::loadDataParts&lt;/h3&gt;
&lt;p&gt;該函式在 &lt;a href=&quot;https://github.com/ClickHouse/ClickHouse/blob/master/src/Storages/MergeTree/MergeTreeData.cpp#L2121&quot;&gt;src/Storages/MergeTree/MergeTreeData.cpp&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;loadDataParts&lt;/code&gt; 負責：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;掃描磁碟&lt;/strong&gt; → 找出所有符合 MergeTree part 命名規則的資料目錄。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;檢查磁碟合法性&lt;/strong&gt; → 確保 parts 不會出現在未定義的 disk 上。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;建構 PartLoadingTree&lt;/strong&gt; → 把所有 part 整理成樹狀結構（支援包含關係，例如新 part 覆蓋舊 part）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;載入 parts 到記憶體&lt;/strong&gt; → 建立 &lt;code&gt;DataPart&lt;/code&gt; 物件，並加入 &lt;code&gt;data_parts_indexes&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;處理異常情況&lt;/strong&gt; → broken parts、duplicate parts、unexpected parts/outdated parts。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;統計與監控&lt;/strong&gt; → 用 &lt;code&gt;ProfileEvents&lt;/code&gt; 記錄載入耗時、載入數量。&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;檢查 orphaned parts&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;if (!getStoragePolicy()-&amp;gt;isDefaultPolicy() &amp;amp;&amp;amp; !skip_sanity_checks ...)
{
    // 確保所有 parts 都落在定義的 storage policy 的磁碟上
    // 如果發現 part 在未知磁碟，直接丟 Exception
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;防止「資料實體還在，但 metadata 已經指不到」，確保資料一致性。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;掃描磁碟，收集 parts&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;runner([&amp;amp;expected_parts, &amp;amp;unexpected_disk_parts, &amp;amp;disk_parts, this, disk_ptr]()
{
    for (auto it = disk_ptr-&amp;gt;iterateDirectory(relative_data_path); it-&amp;gt;isValid(); it-&amp;gt;next())
    {
        if (auto part_info = MergeTreePartInfo::tryParsePartName(it-&amp;gt;name(), format_version))
        {
            if (expected_parts &amp;amp;&amp;amp; !expected_parts-&amp;gt;contains(it-&amp;gt;name()))
                unexpected_disk_parts.emplace_back(...);
            else
                disk_parts.emplace_back(...);
        }
    }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;跳過 &lt;code&gt;tmp*&lt;/code&gt;、&lt;code&gt;format_version.txt&lt;/code&gt;、&lt;code&gt;detached/&lt;/code&gt; 這些特殊目錄。&lt;/li&gt;
&lt;li&gt;判斷目錄名稱能否 parse 出 &lt;code&gt;MergeTreePartInfo&lt;/code&gt;（不符合規則就當垃圾忽略）。&lt;/li&gt;
&lt;li&gt;把 parts 分為 &lt;strong&gt;預期內&lt;/strong&gt;（正常）與 &lt;strong&gt;unexpected&lt;/strong&gt;（意外找到的）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;建立 PartLoadingTree&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;auto loading_tree = PartLoadingTree::build(std::move(parts_to_load));
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;parts 之間可能有「包含」關係（例如新 part 覆蓋了老 part 的範圍）。&lt;/li&gt;
&lt;li&gt;PartLoadingTree 幫助找出「最上層、最完整」的 parts 作為 active parts。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;載入 active parts&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;auto loaded_parts = loadDataPartsFromDisk(active_parts);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;對於每個 active part：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;檢查是否 broken（檔案缺失、壓縮錯誤等）&lt;/li&gt;
&lt;li&gt;檢查是否 duplicate&lt;/li&gt;
&lt;li&gt;判斷 index granularity（adaptive vs non-adaptive）&lt;/li&gt;
&lt;li&gt;記錄是否帶有 lightweight delete、transaction metadata&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;這裡是把磁碟上的目錄轉換為 &lt;code&gt;DataPart&lt;/code&gt; 物件的關鍵。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;處理異常情況&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;broken parts&lt;/strong&gt;：移動到 &lt;code&gt;detached/broken-on-start/&lt;/code&gt; 目錄&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;duplicate parts&lt;/strong&gt;：刪除（除非 static storage）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;unexpected parts&lt;/strong&gt;：記錄下來，開 background task 去處理&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;outdated parts&lt;/strong&gt;：同樣丟到 background task async 載入&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;防呆檢查&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;if (have_non_adaptive_parts &amp;amp;&amp;amp; have_adaptive_parts &amp;amp;&amp;amp; !enable_mixed_granularity_parts)
    throw Exception(...);
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;如果表裡同時有 &lt;strong&gt;舊的非 adaptive granularity parts&lt;/strong&gt; 和 &lt;strong&gt;新的 adaptive parts&lt;/strong&gt;，預設是不允許混用的（除非開 &lt;code&gt;enable_mixed_granularity_parts&lt;/code&gt; 設定）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;紀錄監控 &amp;amp; 任務註冊&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;LOG_DEBUG(log, &quot;Loaded data parts ({} items) took {} seconds&quot;, data_parts_indexes.size(), watch.elapsedSeconds());
ProfileEvents::increment(ProfileEvents::LoadedDataParts, data_parts_indexes.size());
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;用 log 與 ProfileEvents 記錄 parts 載入耗時與數量。&lt;/li&gt;
&lt;li&gt;如果 disk 全是 Readonly（例如所有磁碟都是 cold storage），會啟動一個定期 refresh parts 的任務。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;MergeTreeData::getDataParts&lt;/h3&gt;
&lt;p&gt;該函式在 &lt;a href=&quot;https://github.com/ClickHouse/ClickHouse/blob/master/src/Storages/MergeTree/MergeTreeData.cpp#L7640&quot;&gt;src/Storages/MergeTree/MergeTreeData.cpp&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;這個函式很短，它主要是&lt;strong&gt;查詢要讀哪些 parts，就是從這裡開始決定的&lt;/strong&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;MergeTreeData::DataParts MergeTreeData::getDataParts(
    const DataPartStates &amp;amp; affordable_states,
    const DataPartsKinds &amp;amp; affordable_kinds) const
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;輸入參數&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;affordable_states&lt;/code&gt;：允許的 part 狀態（active、outdated、temporary…）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;affordable_kinds&lt;/code&gt;：允許的 part 類型（wide、compact、in-memory…）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;回傳值&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;一個 &lt;code&gt;DataParts&lt;/code&gt; 容器，包含目前符合條件的 parts。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;auto lock = lockParts();
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;加鎖，確保 &lt;code&gt;data_parts_indexes&lt;/code&gt; 不會在迭代 (iteration) 時被修改。&lt;/li&gt;
&lt;li&gt;因為 parts 可能同時被 background merge/mutation 移動。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;for (auto state : affordable_states)
{
    for (auto kind : affordable_kinds)
    {
        auto range = getDataPartsStateRange(state, kind);
        res.insert(range.begin(), range.end());
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;雙層迴圈，對每個 &lt;code&gt;(state, kind)&lt;/code&gt; 配對，從內部索引拿出符合條件的 parts range。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;getDataPartsStateRange(state, kind)&lt;/code&gt; → 回傳一個 iterator 範圍，代表目前符合條件的 parts。&lt;/li&gt;
&lt;li&gt;把這些 parts 全部收集到 &lt;code&gt;res&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;MergeTreeData::renameTempPartAndReplace&lt;/h3&gt;
&lt;p&gt;該函式在 &lt;a href=&quot;https://github.com/ClickHouse/ClickHouse/blob/master/src/Storages/MergeTree/MergeTreeData.cpp#L4810&quot;&gt;src/Storages/MergeTree/MergeTreeData.cpp&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;這個函式主要是 &lt;strong&gt;INSERT 進 MergeTree流程的收尾&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;MergeTreeData::DataPartsVector MergeTreeData::renameTempPartAndReplace(
    MutableDataPartPtr &amp;amp; part,
    Transaction &amp;amp; out_transaction,
    bool rename_in_transaction)
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;輸入參數&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;part&lt;/code&gt;：剛寫好的 &lt;strong&gt;臨時 part (tmp_xxx)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;out_transaction&lt;/code&gt;：事務物件，用來確保替換 parts 的操作原子性&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rename_in_transaction&lt;/code&gt;：是否在事務中完成 rename（可選擇延遲到 commit）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;回傳值&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;covered_parts&lt;/code&gt;：被新 part 覆蓋、取代的舊 parts（例如 overlapped 的小 part）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;auto part_lock = lockParts();
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;對 &lt;code&gt;parts&lt;/code&gt; 容器加鎖，確保替換過程 thread-safe。&lt;/li&gt;
&lt;li&gt;因為同一時間可能有 background merge/mutation 在動 parts。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;renameTempPartAndReplaceImpl(part, out_transaction, part_lock, &amp;amp;covered_parts, rename_in_transaction);
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;核心邏輯在 &lt;code&gt;renameTempPartAndReplaceImpl&lt;/code&gt;：
&lt;ol&gt;
&lt;li&gt;把 &lt;code&gt;tmp_xxx&lt;/code&gt; 的 part 實際 &lt;strong&gt;rename 成正式名稱&lt;/strong&gt;（例如 &lt;code&gt;all_1_1_0&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;更新 &lt;code&gt;data_parts_indexes&lt;/code&gt;，把這個新 part 插入。&lt;/li&gt;
&lt;li&gt;找出與它重疊的舊 parts，把它們標記為過時（outdated）或直接移除。&lt;/li&gt;
&lt;li&gt;若開啟 &lt;code&gt;rename_in_transaction&lt;/code&gt;，則這些更新會在 transaction commit 時一次性生效。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;return covered_parts;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;把被覆蓋的舊 parts 傳回給呼叫端（方便後續處理，例如 detach 或刪除）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;INSERT 流程&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;使用者做一個 &lt;code&gt;INSERT INTO table VALUES (...)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;ClickHouse 把資料寫成一個 &lt;strong&gt;臨時 part&lt;/strong&gt;（名字以 &lt;code&gt;tmp_&lt;/code&gt; 開頭），確保寫入中途崩潰不會污染active parts。&lt;/li&gt;
&lt;li&gt;寫完後呼叫 &lt;code&gt;renameTempPartAndReplace(...)&lt;/code&gt;：
&lt;ul&gt;
&lt;li&gt;把 &lt;code&gt;tmp_xxx&lt;/code&gt; rename 成正式 part 名稱&lt;/li&gt;
&lt;li&gt;更新 in-memory 索引結構&lt;/li&gt;
&lt;li&gt;移除被它覆蓋的舊 parts&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;INSERT commit 成功 → 新 part 對查詢可見&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;可以達到以下的功能&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Temp file Protection&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;所有 INSERT 都先寫成 &lt;code&gt;tmp_&lt;/code&gt; part，只有最後 rename 成功才「生效」，確保 Atomicity。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Optimistic Merge&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;插入新 part 時順便覆蓋舊的 overlapped parts，避免資料重疊，保持狀態乾淨。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Transaction Consistency&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;如果用 &lt;code&gt;rename_in_transaction&lt;/code&gt;，就能確保多個操作同時 commit 或 rollback ，支持更複雜的 ALTER/REPLACE 操作。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;合併 (Background Merge / Compaction)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;代表函式&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;MergeTreeDataMergerMutator::mergePartsToTemporaryPart(...)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MergeTreeBackgroundExecutor&amp;lt;Queue&amp;gt;::trySchedule(...)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MergeTask::execute(...)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;MergeTree 的核心是「&lt;strong&gt;不做更新，只新增 part，後台再合併&lt;/strong&gt;」。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Insert 會產生許多小 parts。&lt;/li&gt;
&lt;li&gt;背景任務 (&lt;code&gt;tryScheduleMerge&lt;/code&gt;) 會挑選多個小 part，呼叫 &lt;code&gt;mergePartsToTemporaryPart&lt;/code&gt; 合併成一個大 part。&lt;/li&gt;
&lt;li&gt;合併過程會同時應用 TTL、壓縮策略，最終釋放磁碟與加速查詢。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;這就是 ClickHouse 可以維持高寫入速度的祕密：寫入快 → 在背景自行慢慢整理。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;MergeTreeBackgroundExecutor&amp;lt;Queue&amp;gt;::trySchedule&lt;/h3&gt;
&lt;p&gt;該函式在 &lt;a href=&quot;https://github.com/ClickHouse/ClickHouse/blob/master/src/Storages/MergeTree/MergeTreeBackgroundExecutor.cpp#L140&quot;&gt;src/Storages/MergeTree/MergeTreeBackgroundExecutor.cpp&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;這個函數是 &lt;strong&gt;MergeTree 合併/後台任務排程的入口&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;template &amp;lt;class Queue&amp;gt;
bool MergeTreeBackgroundExecutor&amp;lt;Queue&amp;gt;::trySchedule(ExecutableTaskPtr task)
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;作用&lt;/strong&gt;：嘗試把一個後台任務（例如 merge、mutation、TTL clean）放進執行 queue 裡面。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;回傳&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;true&lt;/code&gt; → 成功排程&lt;/li&gt;
&lt;li&gt;&lt;code&gt;false&lt;/code&gt; → 被拒絕（可能是系統 shutdown 或 queue 已滿）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;鎖保護&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;std::lock_guard lock(mutex);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;保護 &lt;code&gt;pending&lt;/code&gt; 任務隊列，避免多執行緒同時修改。&lt;/p&gt;
&lt;h4&gt;檢查當前任務數量是否超限&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;auto &amp;amp; value = CurrentMetrics::values[metric];
if (value.load() &amp;gt;= static_cast&amp;lt;int64_t&amp;gt;(max_tasks_count))
    return false;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;透過 &lt;code&gt;CurrentMetrics&lt;/code&gt; 監控目前這個 executor 的 task 數量。&lt;/li&gt;
&lt;li&gt;如果超過 &lt;code&gt;max_tasks_count&lt;/code&gt;（例如限制同時執行的合併數量），就拒絕排程。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;把任務放進 queue 中&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;pending.push(std::make_shared&amp;lt;TaskRuntimeData&amp;gt;(std::move(task), metric));
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;把傳入的 &lt;code&gt;task&lt;/code&gt; 包裝成 &lt;code&gt;TaskRuntimeData&lt;/code&gt; 放入 &lt;code&gt;pending&lt;/code&gt; queue。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TaskRuntimeData&lt;/code&gt; 附帶 metric，用來追蹤這個 task 的執行狀態。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;通知 worker thread&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;has_tasks.notify_one();
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;喚醒一個等待中的 worker thread，讓它開始處理任務。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;整理&lt;/h4&gt;
&lt;p&gt;這個函式做了&lt;strong&gt;非阻塞調度&lt;/strong&gt;、&lt;strong&gt;資源控制&lt;/strong&gt;、&lt;strong&gt;監控&lt;/strong&gt; 等作用，避免同時合併過多 parts，導致磁碟 I/O 打爆、所有任務數量都掛在 &lt;code&gt;CurrentMetrics&lt;/code&gt; 上，方便監控系統看到目前 Merge/Mutation 壓力&lt;/p&gt;
&lt;h3&gt;MergeTreeDataMergerMutator::mergePartsToTemporaryPart&lt;/h3&gt;
&lt;p&gt;該函式在 &lt;a href=&quot;https://github.com/ClickHouse/ClickHouse/blob/master/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp#L557&quot;&gt;src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;這函數是 &lt;strong&gt;MergeTree 合併的入口&lt;/strong&gt;。我們來一步步拆解：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;用途&lt;/strong&gt;：建立一個 &lt;code&gt;MergeTask&lt;/code&gt;，它代表一次「合併（merge）或變更（mutation）」的任務。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;位置&lt;/strong&gt;：&lt;code&gt;src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;回傳值&lt;/strong&gt;：&lt;code&gt;MergeTaskPtr&lt;/code&gt;（指向新建的 &lt;code&gt;MergeTask&lt;/code&gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：這裡只是「構造 MergeTask」，真正的合併邏輯會在 &lt;code&gt;MergeTask::execute()&lt;/code&gt; 裡跑。&lt;/p&gt;
&lt;h4&gt;參數&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;FutureMergedMutatedPartPtr future_part,
StorageMetadataPtr metadata_snapshot,
MergeList::Entry * merge_entry,
std::unique_ptr&amp;lt;MergeListElement&amp;gt; projection_merge_list_element,
TableLockHolder &amp;amp; holder,
time_t time_of_merge,
ContextPtr context,
ReservationSharedPtr space_reservation,
bool deduplicate,
const Names &amp;amp; deduplicate_by_columns,
bool cleanup,
MergeTreeData::MergingParams merging_params,
MergeTreeTransactionPtr txn,
bool need_prefix,
IMergeTreeDataPart * parent_part,
const String &amp;amp; suffix
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;future_part&lt;/code&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;將要產生的新 part（描述來源 parts 與輸出名稱）。&lt;/li&gt;
&lt;li&gt;是合併計畫的 blueprint。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;metadata_snapshot&lt;/code&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;表的 metadata 快照，包含欄位定義、索引、TTL 等資訊。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;merge_entry / projection_merge_list_element&lt;/code&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;追蹤 merge 在 &lt;code&gt;system.merges&lt;/code&gt;（或 &lt;code&gt;system.part_log&lt;/code&gt;）的紀錄，方便監控。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;holder&lt;/code&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;表鎖，保證合併過程不與 DDL 衝突。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;space_reservation&lt;/code&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;磁碟空間預留，確保合併過程不會因空間不足失敗。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;deduplicate / deduplicate_by_columns&lt;/code&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;是否需要做去重（適用於 &lt;code&gt;ReplacingMergeTree&lt;/code&gt;、&lt;code&gt;OPTIMIZE TABLE ... DEDUPLICATE&lt;/code&gt;）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;cleanup&lt;/code&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;是否在合併時執行清理（例如 TTL）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;merging_params&lt;/code&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;MergeTree 的合併參數（普通、Collapsing、VersionedCollapsing、Summing、Aggregating 等）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;txn&lt;/code&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;與 transaction 結合，支援多語句 Transaction 或 MVCC。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;need_prefix / parent_part / suffix&lt;/code&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;控制新 part 的命名方式，特別是在 mutation、projection merge 的情境下。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;判斷式&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;if (future_part-&amp;gt;isResultPatch())
{
    merging_params = MergeTreeData::getMergingParamsForPatchParts();
    metadata_snapshot = future_part-&amp;gt;parts.front()-&amp;gt;getMetadataSnapshot();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;如果這是一個 &lt;strong&gt;patch merge&lt;/strong&gt;（例如 mutation 補丁），就調整 merging_params，並從來源 part 重新抓 metadata。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;回傳值&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;return std::make_shared&amp;lt;MergeTask&amp;gt;(...);
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;最後建立 &lt;code&gt;MergeTask&lt;/code&gt;，包含所有合併需要的上下文：
&lt;ul&gt;
&lt;li&gt;要合併哪些 parts (&lt;code&gt;future_part&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;怎麼合併（&lt;code&gt;merging_params&lt;/code&gt;、&lt;code&gt;deduplicate&lt;/code&gt;、&lt;code&gt;cleanup&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;合併過程需要的資源（&lt;code&gt;space_reservation&lt;/code&gt;、&lt;code&gt;holder&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;log tracing（&lt;code&gt;merge_entry&lt;/code&gt;、&lt;code&gt;projection_merge_list_element&lt;/code&gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;之後 background thread pool 會呼叫 &lt;code&gt;MergeTask::execute()&lt;/code&gt;，真正讀取來源 parts → 做合併 → 輸出新 part。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;所以 &lt;code&gt;mergePartsToTemporaryPart(...)&lt;/code&gt; 的角色：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;不是直接合併資料&lt;/strong&gt;，而是 &lt;strong&gt;建立一個 MergeTask&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;MergeTask 帶齊所有 context：parts、metadata、transaction、磁碟空間、dedup、TTL、log。&lt;/li&gt;
&lt;li&gt;Background Executor 會排程並執行這個 MergeTask，最後產生新的 &lt;strong&gt;臨時 part&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;臨時 part 成功後，透過 &lt;code&gt;renameTempPartAndReplace(...)&lt;/code&gt; 變成正式 part。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;MergeTask::execute(...)&lt;/h3&gt;
&lt;p&gt;該函式在 &lt;a href=&quot;https://github.com/ClickHouse/ClickHouse/blob/master/src/Storages/MergeTree/MergeTask.cpp#L1590&quot;&gt;src/Storages/MergeTree/MergeTask.cpp&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;這個函數就是 &lt;strong&gt;MergeTree 真正執行合併的核心函數&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;代表一次 &lt;strong&gt;合併（merge）或 mutation&lt;/strong&gt; 任務的執行邏輯。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特點&lt;/strong&gt;：合併不是一次性做完，而是拆成 &lt;strong&gt;多個 Stage（階段）&lt;/strong&gt;，逐步執行。&lt;/li&gt;
&lt;li&gt;回傳值：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;true&lt;/code&gt; → 還有後續 Stage 未完成（task 還需要繼續跑）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;false&lt;/code&gt; → 所有 Stage 都完成，合併結束&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;取出當前階段&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;chassert(stages_iterator != stages.end());
const auto &amp;amp; current_stage = *stages_iterator;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;MergeTask&lt;/code&gt; 持有一個 &lt;code&gt;stages&lt;/code&gt; 向量（pipeline 概念）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;stages_iterator&lt;/code&gt; 指向當前要執行的 Stage。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;嘗試執行當前 Stage&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;if (current_stage-&amp;gt;execute())
    return true;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;如果當前 Stage 還沒做完，回傳 &lt;code&gt;true&lt;/code&gt; → 表示下次還要繼續跑這個 Stage。&lt;/li&gt;
&lt;li&gt;注意：Stage 本身可能是耗時操作（例如讀取、壓縮、寫入），會分批執行。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Stage 結束 → 記錄耗時&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;UInt64 current_elapsed_ms = global_ctx-&amp;gt;merge_list_element_ptr-&amp;gt;watch.elapsedMilliseconds();
UInt64 stage_elapsed_ms = current_elapsed_ms - global_ctx-&amp;gt;prev_elapsed_ms;
global_ctx-&amp;gt;prev_elapsed_ms = current_elapsed_ms;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;記錄從上次到現在為止的時間差 = 本階段耗時。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;global_ctx&lt;/code&gt; 是 merge 任務的全域上下文，裡面有 ProfileEvents 追蹤器。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;更新 ProfileEvents&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;if (global_ctx-&amp;gt;parent_part == nullptr)
{
    ProfileEvents::increment(current_stage-&amp;gt;getTotalTimeProfileEvent(), stage_elapsed_ms);
    ProfileEvents::increment(ProfileEvents::MergeTotalMilliseconds, stage_elapsed_ms);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;如果不是 projection 合併，更新總耗時統計。&lt;/li&gt;
&lt;li&gt;每個 Stage 都有對應的 ProfileEvent，例如 &lt;code&gt;MergeReadBlocks&lt;/code&gt;、&lt;code&gt;MergeWriteBlocks&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;移動到下一個 Stage&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;++stages_iterator;
if (stages_iterator == stages.end())
    return false;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;移動迭代器，進入下一階段。&lt;/li&gt;
&lt;li&gt;如果已經到 &lt;code&gt;stages.end()&lt;/code&gt; → 合併全部完成，回傳 &lt;code&gt;false&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;初始化下一個 Stage&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;(*stages_iterator)-&amp;gt;setRuntimeContext(std::move(next_stage_context), global_ctx);
return true;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;把上一個 Stage 的輸出 context 傳給下一個 Stage（類似 pipeline）。&lt;/li&gt;
&lt;li&gt;回傳 &lt;code&gt;true&lt;/code&gt; → 表示還有 Stage 要跑。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;例外處理&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;catch (...)
{
    merge_failures.withLabels({String(ErrorCodes::getName(getCurrentExceptionCode()))}).increment();
    throw;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;如果合併失敗（I/O 錯誤、磁碟滿、資料壞掉），紀錄失敗次數並拋出 Exception。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;merge_failures&lt;/code&gt; 是一個 metrics counter，用來統計失敗的類型。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;查詢讀取 (Query Read Path)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;代表函式&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ClickHouse/ClickHouse/blob/master/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp#L173&quot;&gt;&lt;code&gt;MergeTreeDataSelectExecutor::read(...)&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ClickHouse/ClickHouse/blob/master/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp#L1283&quot;&gt;&lt;code&gt;MergeTreeDataSelectExecutor::readFromParts(...)&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;當使用者執行 &lt;code&gt;SELECT&lt;/code&gt;，查詢流程會進入 &lt;code&gt;MergeTreeDataSelectExecutor::read&lt;/code&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;選出符合分區的 parts&lt;/li&gt;
&lt;li&gt;透過索引裁剪 granules（例如 min-max index）&lt;/li&gt;
&lt;li&gt;呼叫 &lt;code&gt;readFromParts&lt;/code&gt; 讀取真正需要的資料塊&lt;/li&gt;
&lt;li&gt;將資料送入 QueryPlan pipeline（Filter、Join、Aggregate、Sort 等）&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;const auto &amp;amp; snapshot_data = assert_cast&amp;lt;const MergeTreeData::SnapshotData &amp;amp;&amp;gt;(*storage_snapshot-&amp;gt;data);
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;每次查詢都有一個 &lt;code&gt;StorageSnapshot&lt;/code&gt;，裡面包含表當前可見的 parts 與 mutation snapshot。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;snapshot_data.parts&lt;/code&gt; → 這次查詢要讀的 parts（已經裁剪過的 active parts）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;snapshot_data.mutations_snapshot&lt;/code&gt; → 確保查詢在一個一致性版本上執行。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;auto step = readFromParts(
    snapshot_data.parts,
    snapshot_data.mutations_snapshot,
    column_names_to_return,
    storage_snapshot,
    query_info,
    context,
    max_block_size,
    num_streams,
    max_block_numbers_to_read,
    /*merge_tree_select_result_ptr=*/ nullptr,
    enable_parallel_reading);
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;輸入參數&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;parts&lt;/code&gt; → 要讀的資料分片&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mutations_snapshot&lt;/code&gt; → 保證一致性&lt;/li&gt;
&lt;li&gt;&lt;code&gt;column_names_to_return&lt;/code&gt; → 只讀需要的欄位&lt;/li&gt;
&lt;li&gt;&lt;code&gt;query_info&lt;/code&gt; → 包含 WHERE、ORDER BY、LIMIT 等查詢條件&lt;/li&gt;
&lt;li&gt;&lt;code&gt;max_block_size&lt;/code&gt; → 每個 Block 的最大行數（控制批次大小）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;num_streams&lt;/code&gt; → 幾個執行緒並行讀&lt;/li&gt;
&lt;li&gt;&lt;code&gt;enable_parallel_reading&lt;/code&gt; → 是否允許副本平行讀&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;回傳值&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;step&lt;/code&gt; → 一個 QueryPlanStep (&lt;code&gt;ReadFromMergeTree&lt;/code&gt;)，代表「如何從 MergeTree parts 讀資料」。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;auto plan = std::make_unique&amp;lt;QueryPlan&amp;gt;();
if (step)
    plan-&amp;gt;addStep(std::move(step));
return plan;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;建立空的 &lt;code&gt;QueryPlan&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;如果成功得到一個讀取 Step，就把它加進 plan 裡。&lt;/li&gt;
&lt;li&gt;最後回傳完整的 QueryPlan，交給 pipeline 去執行。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;索引與過濾 (Index &amp;amp; Skipping)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;代表函式&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;MergeTreeWhereOptimizer::optimize(...)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;查詢過程中，ClickHouse 不會「全表掃描」，而是依靠索引：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;主鍵索引&lt;/strong&gt;（排序後的 min-max 範圍）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data Skipping Index&lt;/strong&gt;（布隆過濾器、Set index、Token index）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;MergeTreeWhereOptimizer::optimize&lt;/code&gt; 負責重寫 WHERE 條件，盡量利用索引過濾掉無效 granules，讓查詢只掃必要的資料。&lt;/p&gt;
&lt;p&gt;該函式在 &lt;a href=&quot;https://github.com/ClickHouse/ClickHouse/blob/master/src/Storages/MergeTree/MergeTreeWhereOptimizer.cpp#L104C1-L142C2&quot;&gt;src/Storages/MergeTree/MergeTreeWhereOptimizer.cpp&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;這個函式是在分析 &lt;code&gt;SELECT ... WHERE ...&lt;/code&gt; 條件，決定哪些可以下推到 &lt;strong&gt;PREWHERE&lt;/strong&gt;，然後改寫 AST，把部分條件移到 &lt;code&gt;PREWHERE&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;PREWHERE 的特點：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;先讀部分列，過濾掉大部分資料後，再讀剩下需要的列&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;對 columnar storage 的查詢效率特別有用&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;if (!select.where() || select.prewhere())
    return;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;如果查詢沒有 WHERE，或已經有 PREWHERE，就不用優化。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;auto block_with_constants = KeyCondition::getBlockWithConstants(...);
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;取得查詢裡的常量（方便條件下推）。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;WhereOptimizerContext where_optimizer_context;
where_optimizer_context.context = context;
where_optimizer_context.array_joined_names = determineArrayJoinedNames(select);
where_optimizer_context.move_all_conditions_to_prewhere = context-&amp;gt;getSettingsRef()[Setting::move_all_conditions_to_prewhere];
...
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;準備優化器需要的上下文，包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;是否強制把所有條件移到 PREWHERE&lt;/li&gt;
&lt;li&gt;是否允許重新排序條件&lt;/li&gt;
&lt;li&gt;是否使用統計資料&lt;/li&gt;
&lt;li&gt;查詢是否帶 &lt;code&gt;FINAL&lt;/code&gt;（可能會影響可下推性）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;RPNBuilderTreeContext tree_context(context, std::move(block_with_constants), {});
RPNBuilderTreeNode node(select.where().get(), tree_context);
auto optimize_result = optimizeImpl(node, where_optimizer_context);
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;把 WHERE 條件解析成 &lt;strong&gt;布林運算樹 (RPN – Reverse Polish Notation)&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;呼叫 &lt;code&gt;optimizeImpl&lt;/code&gt;，嘗試把合適的條件下推。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;回傳結果包含：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;where_conditions&lt;/code&gt;（留下的 WHERE 條件）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;prewhere_conditions&lt;/code&gt;（被搬移的條件）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;auto where_filter_ast = reconstructAST(optimize_result-&amp;gt;where_conditions);
auto prewhere_filter_ast = reconstructAST(optimize_result-&amp;gt;prewhere_conditions);

select.setExpression(ASTSelectQuery::Expression::WHERE, std::move(where_filter_ast));
select.setExpression(ASTSelectQuery::Expression::PREWHERE, std::move(prewhere_filter_ast));
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;把結果重新組裝回 AST（抽象語法樹）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;SELECT 查詢此時變成：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT ...
PREWHERE &amp;lt;部分條件&amp;gt;
WHERE &amp;lt;剩餘條件&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;LOG_DEBUG(
    log,
    &quot;MergeTreeWhereOptimizer: condition \&quot;{}\&quot; moved to PREWHERE&quot;,
    select.prewhere()-&amp;gt;formatForLogging(...));
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;做 log 記錄搬移後的 PREWHERE 條件。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;這是從「數十億行」中秒級查詢的關鍵。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;TTL 與資料搬移&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;代表函式&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;MergeTreeData::moveParts(...)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MergeTreeData::removeOutdatedPartsAndDirs(...)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;MergeTree 支援 TTL（Time-to-Live），讓資料自動過期或搬移：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;舊資料刪除&lt;/strong&gt; → &lt;code&gt;removeOutdatedPartsAndDirs&lt;/code&gt; 清理過期 parts&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;冷熱數據分層&lt;/strong&gt; → &lt;code&gt;moveParts&lt;/code&gt; 把舊資料搬到慢速磁碟（HDD、S3），新資料留在 SSD&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;這讓 MergeTree 不只是 OLAP 表，還能做到資料生命週期管理 (Data Lifecycle Management)。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;&lt;code&gt;MergeTreeData::moveParts(...)&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;該函式在 &lt;a href=&quot;https://github.com/ClickHouse/ClickHouse/blob/master/src/Storages/MergeTree/MergeTreeWhereOptimizer.cpp#L104C1-L142C2&quot;&gt;src/Storages/MergeTree/MergeTreeWhereOptimizer.cpp&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;會根據 StoragePolicy（例如多磁碟策略：SSD → HDD，或節省空間）將 parts 實際移動到不同磁碟。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;LOG_INFO(log, &quot;Got {} parts to move.&quot;, moving_tagger-&amp;gt;parts_to_move.size());
MovePartsOutcome result{MovePartsOutcome::PartsMoved};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;記錄這次要搬多少個 parts，初始假設結果是成功移動。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;for (const auto &amp;amp; moving_part : moving_tagger-&amp;gt;parts_to_move)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;每個 part 都要嘗試 clone（複製）並替換。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;auto moves_list_entry = getContext()-&amp;gt;getMovesList().insert(...);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;更新系統表 &lt;code&gt;system.moves&lt;/code&gt;，讓使用者可觀察目前有哪些 parts 正在被移動。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if (supportsReplication() &amp;amp;&amp;amp; disk-&amp;gt;supportZeroCopyReplication() &amp;amp;&amp;amp; (*settings)[MergeTreeSetting::allow_remote_fs_zero_copy_replication])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果表是 ReplicatedMergeTree 且啟用零拷貝複製：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;多個 replica &lt;strong&gt;不能同時移動同一個 part&lt;/strong&gt;，否則會產生多份副本。&lt;/li&gt;
&lt;li&gt;使用 &lt;strong&gt;ZooKeeper/keeper 的 zero-copy lock&lt;/strong&gt; 來互斥。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;auto lock = tryCreateZeroCopyExclusiveLock(moving_part.part-&amp;gt;name, disk);
if (!lock)
{
    result = MovePartsOutcome::MoveWasPostponedBecauseOfZeroCopy;
    break;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果沒拿到 lock，就 postpon，避免競爭。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cloned_part = parts_mover.clonePart(moving_part, read_settings, write_settings);
if (lock-&amp;gt;isLocked())
    parts_mover.swapClonedPart(cloned_part);
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;如果 lock 仍有效 → clone part，然後替換。&lt;/li&gt;
&lt;li&gt;如果 lock 在 clone 過程失效 → 延後處理（retry）。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;cloned_part = parts_mover.clonePart(moving_part, read_settings, write_settings);
parts_mover.swapClonedPart(cloned_part);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;直接 clone → swap，完成 part 移動。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;write_part_log({});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;寫入 &lt;code&gt;system.part_log&lt;/code&gt;，紀錄這次 MOVE_PART 操作，包含耗時、來源/目的磁碟等資訊。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;catch (...)
{
    write_part_log(ExecutionStatus::fromCurrentException(&quot;&quot;, true));
    throw;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果移動過程失敗，紀錄錯誤並重新拋出例外。&lt;/p&gt;
&lt;h2&gt;結語&lt;/h2&gt;
&lt;p&gt;終於這系列迎來了一個結束，跟我的暑期實習一起畢業了 XD（沒啦，開學後還會繼續做），希望各位喜歡這系列文章。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/posts/clickhouse-mergetree-sourcecode-introduction.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;之前有人問我：「Vic，你為什麼想開始鐵人賽」。當初覺得透過鐵人賽壓力讓自己快速深入一個服務，而且查閱了市面上很少人做 ClickHouse 相關的文章，就算有也只是講應用居多，我可以說是第一個開始提及底層原理和架構，可能客群都是資料科學家，很少我這種後端工程師會專注於這個）？&lt;/p&gt;
&lt;p&gt;ClickHouse 是個很讓人著迷的 TB~PB 級別資料處理神器，前提是具備良好的基礎知識，才能在建表的時候考慮到所有情況。透過正確的根據業務邏輯、資料型別採取對應建表策略。&lt;/p&gt;
&lt;p&gt;像是我在這次實習當中，替公司從雲上搬遷了資料，原本在 PostgreSQL 上單個表有約 &lt;strong&gt;400GB&lt;/strong&gt;，但是使用 ClickHouse 搭配正確的配置策略，我可以將&lt;strong&gt;壓縮比達到 5x~86x&lt;/strong&gt; 左右（依照欄位資料型別而定），替公司單一表格省下了&lt;strong&gt;約 360GB&lt;/strong&gt; 的儲存成本，並且提高查詢效率，有助於公司內部資料分析、自動化效率。&lt;/p&gt;
&lt;p&gt;這一系列文章也側面驗證了：&lt;strong&gt;為什麼 ClickHouse 能同時承受高寫入與大規模查詢，並在業界成為主流 OLAP 資料庫服務&lt;/strong&gt;。&lt;/p&gt;
&lt;h3&gt;ClickHouse 系列&lt;s&gt;持續更新中&lt;/s&gt; 完結啦:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/what-is-clickhouse/&quot;&gt;ClickHouse 系列：ClickHouse 是什麼？與傳統 OLAP/OLTP 資料庫的差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-column-row-based-storage/&quot;&gt;ClickHouse 系列：ClickHouse 為什麼選擇 Column-based 儲存？講解 Row-based 與 Column-based 的核心差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-engine&quot;&gt;ClickHouse 系列：ClickHouse 儲存引擎 - MergeTree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-compression-skipping-index/&quot;&gt;ClickHouse 系列：壓縮技術與 Data Skipping Indexes 如何大幅加速查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replacingmergetree-deduplication/&quot;&gt;ClickHouse 系列：ReplacingMergeTree 與資料去重機制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-summingmergetree-aggregation/&quot;&gt;ClickHouse 系列：SummingMergeTree 進行資料彙總的應用場景&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-materialized-view/&quot;&gt;ClickHouse 系列：Materialized Views 即時聚合查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-partition-pruning/&quot;&gt;ClickHouse 系列：分區策略與 Partition Pruning 原理解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-primary-sorting-key/&quot;&gt;ClickHouse 系列：Primary Key、Sorting Key 與 Granule 索引運作原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-collapsingmergetree/&quot;&gt;ClickHouse 系列：CollapsingMergeTree 與邏輯刪除的最佳實踐&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-versioned-collapsingmergetree/&quot;&gt;ClickHouse 系列：VersionedCollapsingMergeTree 版本控制與資料衝突解決&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-aggregatingmergetree/&quot;&gt;ClickHouse 系列：AggregatingMergeTree 實時指標統計的進階應用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-distributed-table-architecture/&quot;&gt;ClickHouse 系列：Distributed Table 與分布式查詢架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replication-failover/&quot;&gt;ClickHouse 系列：Replicated Tables 高可用性與零停機升級實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-kafka-data-streaming-pipeline/&quot;&gt;ClickHouse 系列：與 Kafka 整合打造即時 Data Streaming Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-batch-import/&quot;&gt;ClickHouse 系列：批次匯入最佳實踐 (CSV、Parquet、Native Format)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-external-data-integration/&quot;&gt;ClickHouse 系列：ClickHouse 與外部資料源整合（PostgreSQL）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-query-log-explain/&quot;&gt;ClickHouse 系列：如何提升查詢優化？system.query_log 與 EXPLAIN 用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-projections-optimization/&quot;&gt;ClickHouse 系列：Projections 進階查詢加速技術&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-sampling-statistics/&quot;&gt;ClickHouse 系列：Sampling 抽樣查詢與統計技術原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-ttl-storage-management/&quot;&gt;ClickHouse 系列：TTL 資料清理與儲存成本優化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-storage-policies/&quot;&gt;ClickHouse 系列：儲存政策（Storage Policies）與磁碟資源分層策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-schemas-storage-improvement/&quot;&gt;ClickHouse 系列：表格設計與儲存優化細節&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-grafana-dashboard/&quot;&gt;ClickHouse 系列：ClickHouse 系列：整合 Grafana 打造可視化監控&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-select-optimization/&quot;&gt;ClickHouse 系列：查詢優化案例&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-bi-integration/&quot;&gt;ClickHouse 系列：與 BI 工具整合（Power BI）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-cloud-vs-self-host/&quot;&gt;ClickHouse 系列：ClickHouse Cloud 與自建部署的優劣比較&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-security-rbac/&quot;&gt;ClickHouse 系列：資料庫安全性與權限管理（RBAC）實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-operator-kubernates/&quot;&gt;ClickHouse 系列：Kubernetes 部署分散式架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-sourcecode-introduction/&quot;&gt;ClickHouse 系列：從原始碼看 MergeTree 的六大核心機制&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>ClickHouse 系列：Kubernetes 部署分散式架構</title><link>https://vicwen.app/posts/clickhouse-operator-kubernates/</link><guid isPermaLink="true">https://vicwen.app/posts/clickhouse-operator-kubernates/</guid><pubDate>Tue, 02 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在過去 28 天，我們深入探討了 ClickHouse 的內部設計，包括 MergeTree 引擎、索引、查詢優化技巧，以及不同引擎在資料處理上的應用。這些都屬於「單機或單節點」的觀點。然而，當我們要把 ClickHouse 放到&lt;strong&gt;真實的生產環境&lt;/strong&gt;，面對高併發、資料量成長、以及高可用性的需求時，部署策略就變得格外重要。&lt;/p&gt;
&lt;p&gt;傳統部署方式（例如直接在 VM 或裸機上安裝）雖然簡單，但在現代雲端架構下已經不足以滿足需求。Kubernetes 作為事實上的容器編排標準，提供了自動化、可擴展、彈性化的能力，而 &lt;strong&gt;ClickHouse Operator&lt;/strong&gt; 則讓我們能輕鬆在 Kubernetes 上管理複雜的 ClickHouse 叢集。&lt;/p&gt;
&lt;p&gt;本篇文章會透過在單一主機上快速使用 minikube 和 clickhouse operator 模擬 Kubernates 部署分散式架構。&lt;/p&gt;
&lt;h2&gt;為什麼要在 Kubernetes 上部署 ClickHouse？&lt;/h2&gt;
&lt;p&gt;ClickHouse 本身的設計就非常快，但隨著資料量和使用者數成長，單一節點往往無法承受所有負載。我們需要：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;高可用 (HA, High Availability)&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;節點故障時，系統能自動切換，不影響查詢。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;水平擴展 (Horizontal Scalability)&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;當資料量從 100GB 成長到數 TB，甚至 PB 級，能透過擴充節點快速分擔壓力。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;自動化維運&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;部署、升級、監控、滾動更新都可以透過 Kubernetes 自動化完成。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;雲原生整合&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Kubernetes 的優勢在於「一切皆為 API」。監控 (Prometheus)、儲存 (PVC)、網路 (Ingress/Service) 都能與 ClickHouse 無縫結合。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ClickHouse Operator 的出現，就是為了解決這些問題。&lt;/p&gt;
&lt;h2&gt;ClickHouse Operator 核心概念&lt;/h2&gt;
&lt;p&gt;ClickHouse Operator 是由 &lt;a href=&quot;https://github.com/Altinity&quot;&gt;Altinity&lt;/a&gt; 與開源社群維護的 &lt;a href=&quot;https://github.com/Altinity/clickhouse-operator&quot;&gt;Kubernetes Operator&lt;/a&gt;，主要目標是簡化 ClickHouse 叢集的管理。&lt;/p&gt;
&lt;p&gt;::github{repo=&quot;Altinity/clickhouse-operator&quot;}&lt;/p&gt;
&lt;p&gt;它的主要功能包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Cluster CRD (Custom Resource Definition)&lt;/strong&gt;：允許你用 YAML 定義叢集（shards, replicas, storage, resources）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;自動化管理&lt;/strong&gt;：建立、升級、刪除、滾動更新節點。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;高可用支援&lt;/strong&gt;：透過 Zookeeper 或 Keeper，支援 Replicated Tables。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;監控整合&lt;/strong&gt;：自動匯出 metrics 給 Prometheus。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;結構上，Operator 會監控 Kubernetes 中的 ClickHouseCluster 資源，一旦偵測到改動（例如新增一個 replica），就會自動調整底層 StatefulSet 與 Pod，確保叢集狀態與宣告式配置一致。&lt;/p&gt;
&lt;h2&gt;雲端部署架構設計&lt;/h2&gt;
&lt;p&gt;在雲端上，我們通常會設計一個具備以下特性的架構：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;分片 (Shards) + 副本 (Replicas)&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Shard：將資料分散到不同節點，分擔儲存壓力&lt;/li&gt;
&lt;li&gt;Replica：為每個 shard 建立副本，提供高可用&lt;/li&gt;
&lt;li&gt;本次實作我們使用 1 Shard, 2 Replica 架構做為測試&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Zookeeper/Keeper 管理&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;協調叢集的一致性（表格複製、分片資訊）。&lt;/li&gt;
&lt;li&gt;本次實作我們使用 3 個 Zookeeper&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Persistent Volume Claims (PVC)&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;保證節點重啟後資料不會丟失。&lt;/li&gt;
&lt;li&gt;本次實作我們採用 &lt;code&gt;emptyDir&lt;/code&gt; (作為 Demo, 關掉後就會自動刪除資料)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;資源配置&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;CPU 與 Memory 限額，避免與其他工作負載競爭。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;架構圖示例：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/posts/clickhouse-operator-kubernates/zookeeper-clickhouse-strcture.png&quot; alt=&quot;Zookeeper Clickhouse Structure&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;圖好像有點大...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;這樣的設計能確保：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;任一 Replica 宕機時，查詢不會中斷&lt;/li&gt;
&lt;li&gt;當資料量變大，可以橫向擴展新的 Shard&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;實作環節&lt;/h2&gt;
&lt;p&gt;建立分散式表之前有幾項前置作業：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;安裝 &lt;a href=&quot;https://minikube.sigs.k8s.io/docs/start/?arch=%2Fwindows%2Fx86-64%2Fstable%2F.exe+download&quot;&gt;minikube&lt;/a&gt;，我是使用 wsl2 (Ubuntu 24.04.2 LTS)
&lt;ul&gt;
&lt;li&gt;安裝好後可以使用 &lt;code&gt;minikube dashboard&lt;/code&gt; 開啟 GUI 介面~ (Optional)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;安裝 &lt;a href=&quot;https://github.com/Altinity/clickhouse-operator/blob/master/docs/operator_installation_details.md&quot;&gt;ClickHouse Operator&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;我是只有跑下面的指令就安裝好了，想知道細節可以看文件本身有講解部署了什麼元件&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;kubectl apply -f https://raw.githubusercontent.com/Altinity/clickhouse-operator/master/deploy/operator/clickhouse-operator-install-bundle.yaml
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;創建 namespace，用於隔離環境&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;kubectl create namespace zoo3ns
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;部署三節點 Zookeeper，新增檔案如下：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;# zookeeper-3-nodes.yaml
# Setup Service to provide access to Zookeeper for clients
apiVersion: v1
kind: Service
metadata:
  # DNS would be like zookeeper.zoons
  name: zookeeper
  labels:
    app: zookeeper
spec:
  ports:
    - port: 2181
      name: client
    - port: 7000
      name: prometheus
  selector:
    app: zookeeper
    what: node
---
# Setup Headless Service for StatefulSet
apiVersion: v1
kind: Service
metadata:
  # DNS would be like zookeeper-0.zookeepers.etc
  name: zookeepers
  labels:
    app: zookeeper
spec:
  ports:
    - port: 2888
      name: server
    - port: 3888
      name: leader-election
  clusterIP: None
  selector:
    app: zookeeper
    what: node
---
# Setup max number of unavailable pods in StatefulSet
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: zookeeper-pod-disruption-budget
spec:
  selector:
    matchLabels:
      app: zookeeper
  maxUnavailable: 1
---
# Setup Zookeeper StatefulSet
# Possible params:
# 1. replicas
# 2. memory
# 3. cpu
# 4. storage
# 5. storageClassName
# 6. user to run app
apiVersion: apps/v1
kind: StatefulSet
metadata:
  # nodes would be named as zookeeper-0, zookeeper-1, zookeeper-2
  name: zookeeper
  labels:
    app: zookeeper
spec:
  selector:
    matchLabels:
      app: zookeeper
  serviceName: zookeepers
  replicas: 3
  updateStrategy:
    type: RollingUpdate
  podManagementPolicy: OrderedReady
  template:
    metadata:
      labels:
        app: zookeeper
        what: node
      annotations:
        prometheus.io/port: &apos;7000&apos;
        prometheus.io/scrape: &apos;true&apos;
    spec:
      affinity: {}
      containers:
        - name: kubernetes-zookeeper
          imagePullPolicy: IfNotPresent
          image: &quot;docker.io/zookeeper:3.8.4&quot;
          resources:
            requests:
              memory: &quot;512M&quot;
              cpu: &quot;1&quot;
            limits:
              memory: &quot;4Gi&quot;
              cpu: &quot;2&quot;
          ports:
            - containerPort: 2181
              name: client
            - containerPort: 2888
              name: server
            - containerPort: 3888
              name: leader-election
            - containerPort: 7000
              name: prometheus
          env:
            - name: SERVERS
              value: &quot;3&quot;

# See those links for proper startup settings:
# https://github.com/kow3ns/kubernetes-zookeeper/blob/master/docker/scripts/start-zookeeper
# https://clickhouse.yandex/docs/en/operations/tips/#zookeeper
# https://github.com/ClickHouse/ClickHouse/issues/11781
          command:
            - bash
            - -x
            - -c
            - |
              HOST=`hostname -s` &amp;amp;&amp;amp;
              DOMAIN=`hostname -d` &amp;amp;&amp;amp;
              CLIENT_PORT=2181 &amp;amp;&amp;amp;
              SERVER_PORT=2888 &amp;amp;&amp;amp;
              ELECTION_PORT=3888 &amp;amp;&amp;amp;
              PROMETHEUS_PORT=7000 &amp;amp;&amp;amp;
              ZOO_DATA_DIR=/var/lib/zookeeper/data &amp;amp;&amp;amp;
              ZOO_DATA_LOG_DIR=/var/lib/zookeeper/datalog &amp;amp;&amp;amp;
              {
                echo &quot;clientPort=${CLIENT_PORT}&quot;
                echo &apos;tickTime=2000&apos;
                echo &apos;initLimit=300&apos;
                echo &apos;syncLimit=10&apos;
                echo &apos;maxClientCnxns=2000&apos;
                echo &apos;maxTimeToWaitForEpoch=2000&apos;
                echo &apos;maxSessionTimeout=60000000&apos;
                echo &quot;dataDir=${ZOO_DATA_DIR}&quot;
                echo &quot;dataLogDir=${ZOO_DATA_LOG_DIR}&quot;
                echo &apos;autopurge.snapRetainCount=10&apos;
                echo &apos;autopurge.purgeInterval=1&apos;
                echo &apos;preAllocSize=131072&apos;
                echo &apos;snapCount=3000000&apos;
                echo &apos;leaderServes=yes&apos;
                echo &apos;standaloneEnabled=false&apos;
                echo &apos;4lw.commands.whitelist=*&apos;
                echo &apos;metricsProvider.className=org.apache.zookeeper.metrics.prometheus.PrometheusMetricsProvider&apos;
                echo &quot;metricsProvider.httpPort=${PROMETHEUS_PORT}&quot;
                echo &quot;skipACL=true&quot;
                echo &quot;fastleader.maxNotificationInterval=10000&quot;
              } &amp;gt; /conf/zoo.cfg &amp;amp;&amp;amp;
              {
                echo &quot;zookeeper.root.logger=CONSOLE&quot;
                echo &quot;zookeeper.console.threshold=INFO&quot;
                echo &quot;log4j.rootLogger=\${zookeeper.root.logger}&quot;
                echo &quot;log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender&quot;
                echo &quot;log4j.appender.CONSOLE.Threshold=\${zookeeper.console.threshold}&quot;
                echo &quot;log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout&quot;
                echo &quot;log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} - %-5p [%t:%C{1}@%L] - %m%n&quot;
              } &amp;gt; /conf/log4j.properties &amp;amp;&amp;amp;
              echo &apos;JVMFLAGS=&quot;-Xms128M -Xmx4G -XX:ActiveProcessorCount=8 -XX:+AlwaysPreTouch -Djute.maxbuffer=8388608 -XX:MaxGCPauseMillis=50&quot;&apos; &amp;gt; /conf/java.env &amp;amp;&amp;amp;
              if [[ $HOST =~ (.*)-([0-9]+)$ ]]; then
                  NAME=${BASH_REMATCH[1]} &amp;amp;&amp;amp;
                  ORD=${BASH_REMATCH[2]};
              else
                  echo &quot;Failed to parse name and ordinal of Pod&quot; &amp;amp;&amp;amp;
                  exit 1;
              fi &amp;amp;&amp;amp;
              mkdir -pv ${ZOO_DATA_DIR} &amp;amp;&amp;amp;
              mkdir -pv ${ZOO_DATA_LOG_DIR} &amp;amp;&amp;amp;
              whoami &amp;amp;&amp;amp;
              chown -Rv zookeeper &quot;$ZOO_DATA_DIR&quot; &quot;$ZOO_DATA_LOG_DIR&quot; &amp;amp;&amp;amp;
              export MY_ID=$((ORD+1)) &amp;amp;&amp;amp;
              echo $MY_ID &amp;gt; $ZOO_DATA_DIR/myid &amp;amp;&amp;amp;
              for (( i=1; i&amp;lt;=$SERVERS; i++ )); do
                  echo &quot;server.$i=$NAME-$((i-1)).$DOMAIN:$SERVER_PORT:$ELECTION_PORT&quot; &amp;gt;&amp;gt; /conf/zoo.cfg;
              done &amp;amp;&amp;amp;
              if [[ $SERVERS -eq 1 ]]; then
                  echo &quot;group.1=1&quot; &amp;gt;&amp;gt; /conf/zoo.cfg;
              else
                  echo &quot;group.1=1:2:3&quot; &amp;gt;&amp;gt; /conf/zoo.cfg;
              fi &amp;amp;&amp;amp;
              for (( i=1; i&amp;lt;=$SERVERS; i++ )); do
                  WEIGHT=1
                  if [[ $i == 1 ]]; then
                    WEIGHT=10
                  fi
                  echo &quot;weight.$i=$WEIGHT&quot; &amp;gt;&amp;gt; /conf/zoo.cfg;
              done &amp;amp;&amp;amp;
              zkServer.sh start-foreground
          readinessProbe:
            exec:
              command:
                - bash
                - -c
                - &apos;
                  IFS=;
                  MNTR=$(exec 3&amp;lt;&amp;gt;/dev/tcp/127.0.0.1/2181 ; printf &quot;mntr&quot; &amp;gt;&amp;amp;3 ; tee &amp;lt;&amp;amp;3; exec 3&amp;lt;&amp;amp;- ;);
                  while [[ &quot;$MNTR&quot; == &quot;This ZooKeeper instance is not currently serving requests&quot; ]];
                  do
                    echo &quot;wait mntr works&quot;;
                    sleep 1;
                    MNTR=$(exec 3&amp;lt;&amp;gt;/dev/tcp/127.0.0.1/2181 ; printf &quot;mntr&quot; &amp;gt;&amp;amp;3 ; tee &amp;lt;&amp;amp;3; exec 3&amp;lt;&amp;amp;- ;);
                  done;
                  STATE=$(echo -e $MNTR | grep zk_server_state | cut -d &quot; &quot; -f 2);
                  if [[ &quot;$STATE&quot; =~ &quot;leader&quot; ]]; then
                    echo &quot;check leader state&quot;;
                    SYNCED_FOLLOWERS=$(echo -e $MNTR | grep zk_synced_followers | awk -F&quot;[[:space:]]+&quot; &quot;{print \$2}&quot; | cut -d &quot;.&quot; -f 1);
                    if [[ &quot;$SYNCED_FOLLOWERS&quot; != &quot;0&quot; ]]; then
                      ./bin/zkCli.sh ls /;
                      exit $?;
                    else
                      exit 0;
                    fi;
                  elif [[ &quot;$STATE&quot; =~ &quot;follower&quot; ]]; then
                    echo &quot;check follower state&quot;;
                    PEER_STATE=$(echo -e $MNTR | grep zk_peer_state);
                    if [[ &quot;$PEER_STATE&quot; =~ &quot;following - broadcast&quot; ]]; then
                      ./bin/zkCli.sh ls /;
                      exit $?;
                    else
                      exit 1;
                    fi;
                  else
                    exit 1;
                  fi
                   &apos;
            initialDelaySeconds: 15
            periodSeconds: 10
            timeoutSeconds: 60
          livenessProbe:
            exec:
              command:
                - bash
                - -xc
                - &apos;date &amp;amp;&amp;amp; OK=$(exec 3&amp;lt;&amp;gt;/dev/tcp/127.0.0.1/2181 ; printf &quot;ruok&quot; &amp;gt;&amp;amp;3 ; IFS=; tee &amp;lt;&amp;amp;3; exec 3&amp;lt;&amp;amp;- ;); if [[ &quot;$OK&quot; == &quot;imok&quot; ]]; then exit 0; else exit 1; fi&apos;
            initialDelaySeconds: 10
            periodSeconds: 30
            timeoutSeconds: 5
          volumeMounts:
            - name: datadir-volume
              mountPath: /var/lib/zookeeper
      # Run as a non-privileged user
      securityContext:
        runAsUser: 1000
        fsGroup: 1000
      volumes:
        - name: datadir-volume
          emptyDir:
            medium: &quot;&quot; #accepted values:  empty str (means node&apos;s default medium) or Memory
            sizeLimit: 1Gi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接著 apply 該部署文件和確認 zookeeper 是否都已建立：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 部署配置
kubectl apply -f zookeeper-3-nodes.yaml -n zoo3ns

# 確認 service
kubectl get svc -n zoo3ns
NAME        TYPE        CLUSTER-IP          EXTERNAL-IP   PORT(S)            AGE
zookeeper   ClusterIP   {YOUR-CLUSTER-IP}   &amp;lt;none&amp;gt;        2181/TCP,7000/TCP  54m
zookeepers  ClusterIP   None                &amp;lt;none&amp;gt;        2888/TCP,3888/TCP  54m

# 確認 pods
kubectl get pod -n zoo3ns
NAME            READY   STATUS    RESTARTS   AGE
zookeeper-0     1/1     Running   0          53m
zookeeper-1     1/1     Running   0          53m
zookeeper-2     1/1     Running   0          52m
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;部署 Clickhouse with 1 shards and 2 replicas&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;# clickhouse-1shards-2replicas.yaml
apiVersion: &quot;clickhouse.altinity.com/v1&quot;
kind: &quot;ClickHouseInstallation&quot;

metadata:
  name: &quot;repl-05&quot;

spec:
  defaults:
    templates:
      dataVolumeClaimTemplate: default
      podTemplate: clickhouse-20.7

  configuration:
    zookeeper:
      nodes:
      - host: zookeeper.zoo3ns
    clusters:
      - name: replicated
        layout:
          shardsCount: 1
          replicasCount: 2

  templates:
    volumeClaimTemplates:
      - name: default
        spec:
          accessModes:
            - ReadWriteOnce
          resources:
            requests:
              storage: 500Mi
    podTemplates:
      - name: clickhouse-20.7
        spec:
          containers:
            - name: clickhouse-pod
              image: clickhouse/clickhouse-server:24.8
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接著 apply 該部署文件和確認 zookeeper 是否都已建立：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 部署配置
kubectl apply -f clickhouse-1shards-2replicas.yaml -n zoo3ns

# 確認 service
kubectl get svc -n zoo3ns
NAME                         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
chi-repl-05-replicated-0-0   ClusterIP   None            &amp;lt;none&amp;gt;        9000/TCP,8123/TCP,9009/TCP   50m
chi-repl-05-replicated-0-1   ClusterIP   None            &amp;lt;none&amp;gt;        9000/TCP,8123/TCP,9009/TCP   49m
clickhouse-repl-05           ClusterIP   None            &amp;lt;none&amp;gt;        8123/TCP,9000/TCP            49m

# 確認 pods
kubectl get pod -n zoo3ns
NAME                           READY   STATUS    RESTARTS   AGE
chi-repl-05-replicated-0-0-0   1/1     Running   0          50m
chi-repl-05-replicated-0-1-0   1/1     Running   0          50m
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果都完成了，恭喜你完成最難的一步🚀：&lt;strong&gt;建置環境&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;進入 ClickHouse 內部測試是否成功&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;開啟兩個 terminal，個別進入不同的 pods&lt;pre&gt;&lt;code&gt;kubectl exec -it chi-repl-05-replicated-0-0-0 -- bash
kubectl exec -it chi-repl-05-replicated-0-1-0 -- bash
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;進入後輸入 &lt;code&gt;clickhouse-client&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;在 &lt;code&gt;chi-repl-05-replicated-0-0-0&lt;/code&gt; pod 內部建立 &lt;code&gt;ReplicatedMergeTree&lt;/code&gt;，這個 MergeTree 引擎可以幫助你自動同步不同 cluster, shards, replicas... 的資料&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE events_local ON CLUSTER `{cluster}`
(
    `event_date` Date,
    `event_type` Int32,
    `article_id` Int32,
    `title` String
)
ENGINE = ReplicatedMergeTree(&apos;/clickhouse/{installation}/{cluster}/tables/{shard}/{database}/{table}&apos;, &apos;{replica}&apos;)
PARTITION BY toYYYYMM(event_date)
ORDER BY (event_type, article_id)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;得到結果，代表你已經成功新增了。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Query id: 0e9d3beb-59ea-4194-9dbe-9f7cf88e19cc

┌─host───────────────────────┬─port─┬─status─┬─error─┬─num_hosts_remaining─┬─num_hosts_active─┐
1. │ chi-repl-05-replicated-0-0 │ 9000 │      0 │       │                   1 │                0 │
2. │ chi-repl-05-replicated-0-1 │ 9000 │      0 │       │                   0 │                0 │
└────────────────────────────┴──────┴────────┴───────┴─────────────────────┴──────────────────┘

2 rows in set. Elapsed: 0.253 sec.
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;接著建立本地表&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE events ON CLUSTER `{cluster}` AS events_local
ENGINE = Distributed(&apos;{cluster}&apos;, default, events_local, rand())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;得到結果，代表你已經成功新增了。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Query id: b203ec4b-08b1-45bf-98ea-6d4ad32956d8

┌─host───────────────────────┬─port─┬─status─┬─error─┬─num_hosts_remaining─┬─num_hosts_active─┐
1. │ chi-repl-05-replicated-0-0 │ 9000 │      0 │       │                   1 │                0 │
2. │ chi-repl-05-replicated-0-1 │ 9000 │      0 │       │                   0 │                0 │
└────────────────────────────┴──────┴────────┴───────┴─────────────────────┴──────────────────┘

2 rows in set. Elapsed: 0.084 sec.
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;接著可以在 &lt;code&gt;chi-repl-05-replicated-0-0-0&lt;/code&gt; 插入資料，在 &lt;code&gt;chi-repl-05-replicated-0-1-0&lt;/code&gt; 觀察資料是否有同步
&lt;ul&gt;
&lt;li&gt;先在 &lt;code&gt;chi-repl-05-replicated-0-1-0&lt;/code&gt; 觀察，沒有資料是正常的:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;SELECT *
FROM events_local
WHERE event_type = 100

Query id: 4dd7fbbd-4089-4b7a-aa16-af78baeaf3f4

Ok.

0 rows in set. Elapsed: 0.002 sec.
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;在 &lt;code&gt;chi-repl-05-replicated-0-0-0&lt;/code&gt; 插入資料&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;INSERT INTO events VALUES (today(), 100, 123, &apos;from pod A&apos;);
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;回到 &lt;code&gt;chi-repl-05-replicated-0-1-0&lt;/code&gt; 觀察:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;SELECT *
FROM events_local
WHERE event_type = 100

Query id: 1537f542-a13d-4a19-b29b-baed69b476c8

┌─event_date─┬─event_type─┬─article_id─┬─title──────┐
1. │ 2025-08-28 │        100 │        123 │ from pod A │
└────────────┴────────────┴────────────┴────────────┘

1 row in set. Elapsed: 0.002 sec.
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;到這邊都是正確的，代表你成功了！！！（但是是在單節點上）&lt;/p&gt;
&lt;h2&gt;部署過程中的挑戰與解決方案&lt;/h2&gt;
&lt;p&gt;即便有了 Operator，仍然會遇到一些挑戰：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;儲存管理&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PVC 大小需事先規劃，否則後期調整麻煩。&lt;/li&gt;
&lt;li&gt;解法：使用 StorageClass 提供動態擴展。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;升級策略&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;直接升級可能導致節點不一致。&lt;/li&gt;
&lt;li&gt;解法：使用 Rolling Update，並確保表格引擎為 Replicated 系列。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;監控與觀測性&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;查詢效能下降時需要快速診斷。&lt;/li&gt;
&lt;li&gt;解法：結合 Prometheus + Grafana，監控 query latency、merge 數量、磁碟使用率。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;網路與流量分配&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;多 Shard 查詢需透過 Distributed Table 或外部負載平衡。&lt;/li&gt;
&lt;li&gt;解法：Kubernetes Ingress + ClickHouse Distributed Engine。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;與傳統 VM 部署的差異&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;面向&lt;/th&gt;
&lt;th&gt;VM/裸機部署&lt;/th&gt;
&lt;th&gt;Kubernetes 部署&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;部署方式&lt;/td&gt;
&lt;td&gt;手動安裝、配置&lt;/td&gt;
&lt;td&gt;YAML 定義、自動化&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;擴容&lt;/td&gt;
&lt;td&gt;需人工加機器、改設定&lt;/td&gt;
&lt;td&gt;修改 replicas/shards 即可&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;高可用&lt;/td&gt;
&lt;td&gt;需人工維護 Zookeeper&lt;/td&gt;
&lt;td&gt;Operator 自動協調&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;升級&lt;/td&gt;
&lt;td&gt;容易停機&lt;/td&gt;
&lt;td&gt;滾動更新、零停機&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;監控&lt;/td&gt;
&lt;td&gt;額外安裝&lt;/td&gt;
&lt;td&gt;Prometheus/Grafana 整合&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;結論很明顯：如果你是單機測試，VM 部署即可；但若要進入生產環境，Kubernetes + Operator 幾乎是標準解。&lt;/p&gt;
&lt;h2&gt;結語&lt;/h2&gt;
&lt;p&gt;ClickHouse 本身非常強大，但若缺乏好的部署方式，容易因節點故障、擴展困難、升級不便而影響穩定性。Kubernetes 與 ClickHouse Operator 的結合，讓我們能：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;以 &lt;strong&gt;宣告式配置 (YAML)&lt;/strong&gt; 管理整個叢集&lt;/li&gt;
&lt;li&gt;自動化完成 &lt;strong&gt;部署、升級、擴展&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;提供高可用與容錯能力，支援雲端規模的數據分析&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在資料量不斷成長的今天，這種雲原生部署方式已成為 ClickHouse 生產環境的首選。&lt;/p&gt;
&lt;p&gt;明天就是 ClickHouse 系列最後一天了：Ｄ&lt;/p&gt;
&lt;h3&gt;ClickHouse 系列持續更新中:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/what-is-clickhouse/&quot;&gt;ClickHouse 系列：ClickHouse 是什麼？與傳統 OLAP/OLTP 資料庫的差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-column-row-based-storage/&quot;&gt;ClickHouse 系列：ClickHouse 為什麼選擇 Column-based 儲存？講解 Row-based 與 Column-based 的核心差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-engine&quot;&gt;ClickHouse 系列：ClickHouse 儲存引擎 - MergeTree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-compression-skipping-index/&quot;&gt;ClickHouse 系列：壓縮技術與 Data Skipping Indexes 如何大幅加速查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replacingmergetree-deduplication/&quot;&gt;ClickHouse 系列：ReplacingMergeTree 與資料去重機制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-summingmergetree-aggregation/&quot;&gt;ClickHouse 系列：SummingMergeTree 進行資料彙總的應用場景&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-materialized-view/&quot;&gt;ClickHouse 系列：Materialized Views 即時聚合查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-partition-pruning/&quot;&gt;ClickHouse 系列：分區策略與 Partition Pruning 原理解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-primary-sorting-key/&quot;&gt;ClickHouse 系列：Primary Key、Sorting Key 與 Granule 索引運作原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-collapsingmergetree/&quot;&gt;ClickHouse 系列：CollapsingMergeTree 與邏輯刪除的最佳實踐&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-versioned-collapsingmergetree/&quot;&gt;ClickHouse 系列：VersionedCollapsingMergeTree 版本控制與資料衝突解決&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-aggregatingmergetree/&quot;&gt;ClickHouse 系列：AggregatingMergeTree 實時指標統計的進階應用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-distributed-table-architecture/&quot;&gt;ClickHouse 系列：Distributed Table 與分布式查詢架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replication-failover/&quot;&gt;ClickHouse 系列：Replicated Tables 高可用性與零停機升級實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-kafka-data-streaming-pipeline/&quot;&gt;ClickHouse 系列：與 Kafka 整合打造即時 Data Streaming Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-batch-import/&quot;&gt;ClickHouse 系列：批次匯入最佳實踐 (CSV、Parquet、Native Format)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-external-data-integration/&quot;&gt;ClickHouse 系列：ClickHouse 與外部資料源整合（PostgreSQL）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-query-log-explain/&quot;&gt;ClickHouse 系列：如何提升查詢優化？system.query_log 與 EXPLAIN 用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-projections-optimization/&quot;&gt;ClickHouse 系列：Projections 進階查詢加速技術&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-sampling-statistics/&quot;&gt;ClickHouse 系列：Sampling 抽樣查詢與統計技術原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-ttl-storage-management/&quot;&gt;ClickHouse 系列：TTL 資料清理與儲存成本優化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-storage-policies/&quot;&gt;ClickHouse 系列：儲存政策（Storage Policies）與磁碟資源分層策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-schemas-storage-improvement/&quot;&gt;ClickHouse 系列：表格設計與儲存優化細節&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-grafana-dashboard/&quot;&gt;ClickHouse 系列：ClickHouse 系列：整合 Grafana 打造可視化監控&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-select-optimization/&quot;&gt;ClickHouse 系列：查詢優化案例&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-bi-integration/&quot;&gt;ClickHouse 系列：與 BI 工具整合（Power BI）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-cloud-vs-self-host/&quot;&gt;ClickHouse 系列：ClickHouse Cloud 與自建部署的優劣比較&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-security-rbac/&quot;&gt;ClickHouse 系列：資料庫安全性與權限管理（RBAC）實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-operator-kubernates/&quot;&gt;ClickHouse 系列：Kubernetes 部署分散式架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-sourcecode-introduction/&quot;&gt;ClickHouse 系列：從原始碼看 MergeTree 的六大核心機制&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>ClickHouse 系列：資料庫安全性與權限管理（RBAC）實作</title><link>https://vicwen.app/posts/clickhouse-security-rbac/</link><guid isPermaLink="true">https://vicwen.app/posts/clickhouse-security-rbac/</guid><pubDate>Sun, 31 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;資料庫安全性與權限管理（RBAC, Role-Based Access Control）是不可或缺的基礎設施。ClickHouse 支援細緻的權限設計與 RBAC 機制，能夠確保數據資源的正確隔離與授權，降低操作風險與資安威脅。&lt;/p&gt;
&lt;h2&gt;RBAC 架構與核心概念&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;元件&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;User&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;資料庫使用者，可指定密碼、網路存取範圍、預設角色等。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Role&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;角色，承載一組權限（Privilege），可賦予多個使用者。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Privilege&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;權限，例如 SELECT、INSERT、ALTER、DROP，可指定作用範圍 (Database/Table)。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Quota&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;資源限制，如每分鐘可執行的查詢數、讀取資料量等。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Profile&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;設定檔，如 max_memory_usage、readonly 模式等使用者層級設定。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;RBAC 設計以 &lt;strong&gt;User → Role → Privilege&lt;/strong&gt; 的方式進行權限授予，能讓權限管理變得簡單且可重複使用。&lt;/p&gt;
&lt;h2&gt;啟用 RBAC 與使用者權限管理&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;啟用 Access Management&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;確保 config.xml 中已啟用：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;access_control&amp;gt;
    &amp;lt;enabled&amp;gt;true&amp;lt;/enabled&amp;gt;
&amp;lt;/access_control&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;或使用 Docker 時，指定環境變數：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT: 1
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;建立 User&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;CREATE USER analyst IDENTIFIED WITH plaintext_password BY &apos;analyst_pass&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;建立 Role 並授予權限&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;CREATE ROLE analytics_reader;
GRANT SELECT ON default.user_events TO analytics_reader;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;將 Role 指派給 User&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;GRANT analytics_reader TO analyst;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;檢查授權結果&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;SHOW GRANTS FOR analyst;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;進階：Profile 與 Quota 設定&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;建立資源限制 (Quota)&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;CREATE QUOTA daily_quota KEYED BY user_name FOR INTERVAL 1 DAY MAX queries = 1000, errors = 100;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;建立 Profile（用於參數限制）&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;CREATE SETTINGS PROFILE analyst_profile SETTINGS
    max_memory_usage = 1000000000,
    readonly = 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;將 Quota 與 Profile 指派給 User&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;ALTER USER analyst
    SETTINGS PROFILE analyst_profile
    QUOTA daily_quota;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;模擬登入與權限驗證&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;使用權限帳號進行查詢&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;clickhouse-client --user=analyst --password=analyst_pass --query=&quot;SELECT * FROM default.user_events LIMIT 10&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;測試未授權操作&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;clickhouse-client --user=analyst --password=analyst_pass --query=&quot;DROP TABLE default.user_events&quot;
-- Expected: DB::Exception: analyst: Not enough privileges.
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;RBAC 實作策略建議&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;情境&lt;/th&gt;
&lt;th&gt;實作建議&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;多使用者查詢不同資料表&lt;/td&gt;
&lt;td&gt;以 Role 將不同表的 SELECT 權限進行組合管理。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;嚴格限制查詢資源消耗&lt;/td&gt;
&lt;td&gt;使用 Quota 及 Profile 限制記憶體、查詢數量、錯誤次數等。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;只讀帳號/唯讀 API 查詢&lt;/td&gt;
&lt;td&gt;配置 readonly Profile，禁止寫入/DDL 操作。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;多租戶 (Multi-Tenant) 架構&lt;/td&gt;
&lt;td&gt;以 Role 與 Database Scope 控制租戶隔離權限。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;結語&lt;/h2&gt;
&lt;p&gt;ClickHouse 的 RBAC 機制，能夠協助你從「靜態權限管理」升級到「動態可控的使用者行為治理」，不僅能細緻控管資料資源存取權限，還能結合 Quota、Profile 等策略進行資源保護與行為限制，提升系統安全性與穩定性。&lt;/p&gt;
&lt;h3&gt;ClickHouse 系列持續更新中:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/what-is-clickhouse/&quot;&gt;ClickHouse 系列：ClickHouse 是什麼？與傳統 OLAP/OLTP 資料庫的差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-column-row-based-storage/&quot;&gt;ClickHouse 系列：ClickHouse 為什麼選擇 Column-based 儲存？講解 Row-based 與 Column-based 的核心差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-engine&quot;&gt;ClickHouse 系列：ClickHouse 儲存引擎 - MergeTree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-compression-skipping-index/&quot;&gt;ClickHouse 系列：壓縮技術與 Data Skipping Indexes 如何大幅加速查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replacingmergetree-deduplication/&quot;&gt;ClickHouse 系列：ReplacingMergeTree 與資料去重機制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-summingmergetree-aggregation/&quot;&gt;ClickHouse 系列：SummingMergeTree 進行資料彙總的應用場景&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-materialized-view/&quot;&gt;ClickHouse 系列：Materialized Views 即時聚合查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-partition-pruning/&quot;&gt;ClickHouse 系列：分區策略與 Partition Pruning 原理解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-primary-sorting-key/&quot;&gt;ClickHouse 系列：Primary Key、Sorting Key 與 Granule 索引運作原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-collapsingmergetree/&quot;&gt;ClickHouse 系列：CollapsingMergeTree 與邏輯刪除的最佳實踐&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-versioned-collapsingmergetree/&quot;&gt;ClickHouse 系列：VersionedCollapsingMergeTree 版本控制與資料衝突解決&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-aggregatingmergetree/&quot;&gt;ClickHouse 系列：AggregatingMergeTree 實時指標統計的進階應用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-distributed-table-architecture/&quot;&gt;ClickHouse 系列：Distributed Table 與分布式查詢架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replication-failover/&quot;&gt;ClickHouse 系列：Replicated Tables 高可用性與零停機升級實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-kafka-data-streaming-pipeline/&quot;&gt;ClickHouse 系列：與 Kafka 整合打造即時 Data Streaming Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-batch-import/&quot;&gt;ClickHouse 系列：批次匯入最佳實踐 (CSV、Parquet、Native Format)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-external-data-integration/&quot;&gt;ClickHouse 系列：ClickHouse 與外部資料源整合（PostgreSQL）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-query-log-explain/&quot;&gt;ClickHouse 系列：如何提升查詢優化？system.query_log 與 EXPLAIN 用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-projections-optimization/&quot;&gt;ClickHouse 系列：Projections 進階查詢加速技術&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-sampling-statistics/&quot;&gt;ClickHouse 系列：Sampling 抽樣查詢與統計技術原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-ttl-storage-management/&quot;&gt;ClickHouse 系列：TTL 資料清理與儲存成本優化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-storage-policies/&quot;&gt;ClickHouse 系列：儲存政策（Storage Policies）與磁碟資源分層策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-schemas-storage-improvement/&quot;&gt;ClickHouse 系列：表格設計與儲存優化細節&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-grafana-dashboard/&quot;&gt;ClickHouse 系列：ClickHouse 系列：整合 Grafana 打造可視化監控&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-select-optimization/&quot;&gt;ClickHouse 系列：查詢優化案例&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-bi-integration/&quot;&gt;ClickHouse 系列：與 BI 工具整合（Power BI）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-cloud-vs-self-host/&quot;&gt;ClickHouse 系列：ClickHouse Cloud 與自建部署的優劣比較&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-security-rbac/&quot;&gt;ClickHouse 系列：資料庫安全性與權限管理（RBAC）實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-operator-kubernates/&quot;&gt;ClickHouse 系列：Kubernetes 部署分散式架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-sourcecode-introduction/&quot;&gt;ClickHouse 系列：從原始碼看 MergeTree 的六大核心機制&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>ClickHouse 系列：ClickHouse Cloud 與自建部署的優劣比較</title><link>https://vicwen.app/posts/clickhouse-cloud-vs-self-host/</link><guid isPermaLink="true">https://vicwen.app/posts/clickhouse-cloud-vs-self-host/</guid><pubDate>Sat, 30 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;隨著雲端原生架構盛行，越來越多企業在選擇 ClickHouse 時，會在 &lt;strong&gt;ClickHouse Cloud（官方雲託管服務）&lt;/strong&gt; 與 &lt;strong&gt;自行部署 ClickHouse Cluster&lt;/strong&gt; 之間決定。&lt;/p&gt;
&lt;h2&gt;一、ClickHouse Cloud 是什麼？&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;ClickHouse Cloud&lt;/strong&gt; 是 ClickHouse 官方提供的 &lt;strong&gt;全託管雲端服務&lt;/strong&gt;，讓開發者與資料工程師能夠「免去基礎設施維運」的負擔，專注於資料分析應用開發。&lt;/p&gt;
&lt;h3&gt;特色：&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;無需自行管理叢集、儲存、節點配置與升級。&lt;/li&gt;
&lt;li&gt;彈性調整運算與儲存資源（Pay-as-you-go）。&lt;/li&gt;
&lt;li&gt;內建高可用性 (HA)、自動備份、零停機升級。&lt;/li&gt;
&lt;li&gt;支援與 AWS、GCP 直接整合。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;二、自建 ClickHouse 部署方式&lt;/h2&gt;
&lt;p&gt;企業也可以選擇將 ClickHouse 安裝於自己的虛擬機 (VM)、Kubernetes 環境中，打造專屬的 ClickHouse Cluster。&lt;/p&gt;
&lt;h3&gt;自建架構特色：&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;完全掌控 ClickHouse 配置、資源調度與網路隔離。&lt;/li&gt;
&lt;li&gt;可依需求設計客製化儲存分層（SSD + HDD + S3）。&lt;/li&gt;
&lt;li&gt;彈性選擇監控、DevOps、自動化工具鏈（如 Ansible, Terraform, Zabbix 等）。&lt;/li&gt;
&lt;li&gt;可搭配公司內部安全策略（私有網路、特定 IAM 身份驗證）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;三、ClickHouse Cloud v.s 自建部署比較&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;項目&lt;/th&gt;
&lt;th&gt;ClickHouse Cloud&lt;/th&gt;
&lt;th&gt;自建 ClickHouse&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;上手速度&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;快速，開啟服務即可使用&lt;/td&gt;
&lt;td&gt;需自行安裝、建置與配置&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;維運負擔&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;免維運 (自動升級、備份、監控)&lt;/td&gt;
&lt;td&gt;需自行維護節點狀態、升級、監控系統&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;資源調度彈性&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;雲端隨用隨擴（按量計費）&lt;/td&gt;
&lt;td&gt;需自行管理資源規劃與擴容策略&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;初期成本&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;低，依用量計價&lt;/td&gt;
&lt;td&gt;資源建置成本與時間較高&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;長期成本&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;流量與儲存量大時，成本成長明顯&lt;/td&gt;
&lt;td&gt;資源持有後，長期維運成本較低&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;性能調校&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;部分參數無法自訂（由 Cloud 平台管控）&lt;/td&gt;
&lt;td&gt;可完全自訂所有 ClickHouse 配置參數&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;網路延遲&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;資料流需經過雲端網路&lt;/td&gt;
&lt;td&gt;可部署於企業內部，降低內部網路延遲&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;安全隔離&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;基於雲端 IAM，與其他租戶共享雲資源&lt;/td&gt;
&lt;td&gt;完全專屬資源，可設計私有隔離環境&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;支援分層儲存（Storage Policies）&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;受限於 ClickHouse Cloud 的儲存架構&lt;/td&gt;
&lt;td&gt;可自訂 SSD/HDD/S3 儲存分層策略&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;擴展性與可靠性&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;由 Cloud 平台提供 HA, 自動 Failover&lt;/td&gt;
&lt;td&gt;需自行設計 Replica 與高可用機制&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;維運資源需求&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;適合無專職 DBA 的小型團隊&lt;/td&gt;
&lt;td&gt;適合有專業 SRE/DBA 團隊的大型企業&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;四、何時選擇 ClickHouse Cloud？&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;新創團隊/小型企業&lt;/strong&gt;：快速導入數據分析，沒有維運團隊支援時。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;資料量變動頻繁的業務場景&lt;/strong&gt;：如活動高峰期流量瞬間暴增，需要雲端自動擴容能力。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;專案試行與 PoC 階段&lt;/strong&gt;：用量未明朗、預算有限時。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;跨區域應用&lt;/strong&gt;：需要快速部署於多雲或跨國的資料應用架構。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;五、何時選擇自建 ClickHouse？&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;超大規模資料量（PB 級以上）&lt;/strong&gt;：為了壓低長期儲存與流量成本，自行持有硬體更具經濟效益。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;有專業 SRE / DBA 團隊支援&lt;/strong&gt;：企業內有 ClickHouse 專家能進行參數優化與系統維運。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;對效能與延遲極度敏感的應用&lt;/strong&gt;：如金融交易、即時風控系統，資料需內網低延遲流通。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;需高度客製化架構&lt;/strong&gt;：例如需要與內部資料湖、大數據平台（如 Hadoop/Spark）整合。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;內部資安、法規需求&lt;/strong&gt;：資料需存放於私有數據中心，無法使用雲服務。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;結語&lt;/h2&gt;
&lt;p&gt;ClickHouse Cloud 與自建部署並非互相取代，而是根據你的 &lt;strong&gt;團隊資源、預算規劃、資料規模與商業需求&lt;/strong&gt; 做出取捨。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;專注快速上線，選 Cloud。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;追求極致成本與效能最佳化，選自建。&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;未來也可考慮 &lt;strong&gt;混合雲部署（Hybrid Cloud）&lt;/strong&gt;，在核心數據選用自建 Cluster，同時將非核心分析流量交由 ClickHouse Cloud 彈性處理。&lt;/p&gt;
&lt;h3&gt;ClickHouse 系列持續更新中:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/what-is-clickhouse/&quot;&gt;ClickHouse 系列：ClickHouse 是什麼？與傳統 OLAP/OLTP 資料庫的差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-column-row-based-storage/&quot;&gt;ClickHouse 系列：ClickHouse 為什麼選擇 Column-based 儲存？講解 Row-based 與 Column-based 的核心差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-engine&quot;&gt;ClickHouse 系列：ClickHouse 儲存引擎 - MergeTree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-compression-skipping-index/&quot;&gt;ClickHouse 系列：壓縮技術與 Data Skipping Indexes 如何大幅加速查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replacingmergetree-deduplication/&quot;&gt;ClickHouse 系列：ReplacingMergeTree 與資料去重機制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-summingmergetree-aggregation/&quot;&gt;ClickHouse 系列：SummingMergeTree 進行資料彙總的應用場景&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-materialized-view/&quot;&gt;ClickHouse 系列：Materialized Views 即時聚合查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-partition-pruning/&quot;&gt;ClickHouse 系列：分區策略與 Partition Pruning 原理解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-primary-sorting-key/&quot;&gt;ClickHouse 系列：Primary Key、Sorting Key 與 Granule 索引運作原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-collapsingmergetree/&quot;&gt;ClickHouse 系列：CollapsingMergeTree 與邏輯刪除的最佳實踐&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-versioned-collapsingmergetree/&quot;&gt;ClickHouse 系列：VersionedCollapsingMergeTree 版本控制與資料衝突解決&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-aggregatingmergetree/&quot;&gt;ClickHouse 系列：AggregatingMergeTree 實時指標統計的進階應用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-distributed-table-architecture/&quot;&gt;ClickHouse 系列：Distributed Table 與分布式查詢架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replication-failover/&quot;&gt;ClickHouse 系列：Replicated Tables 高可用性與零停機升級實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-kafka-data-streaming-pipeline/&quot;&gt;ClickHouse 系列：與 Kafka 整合打造即時 Data Streaming Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-batch-import/&quot;&gt;ClickHouse 系列：批次匯入最佳實踐 (CSV、Parquet、Native Format)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-external-data-integration/&quot;&gt;ClickHouse 系列：ClickHouse 與外部資料源整合（PostgreSQL）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-query-log-explain/&quot;&gt;ClickHouse 系列：如何提升查詢優化？system.query_log 與 EXPLAIN 用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-projections-optimization/&quot;&gt;ClickHouse 系列：Projections 進階查詢加速技術&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-sampling-statistics/&quot;&gt;ClickHouse 系列：Sampling 抽樣查詢與統計技術原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-ttl-storage-management/&quot;&gt;ClickHouse 系列：TTL 資料清理與儲存成本優化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-storage-policies/&quot;&gt;ClickHouse 系列：儲存政策（Storage Policies）與磁碟資源分層策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-schemas-storage-improvement/&quot;&gt;ClickHouse 系列：表格設計與儲存優化細節&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-grafana-dashboard/&quot;&gt;ClickHouse 系列：ClickHouse 系列：整合 Grafana 打造可視化監控&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-select-optimization/&quot;&gt;ClickHouse 系列：查詢優化案例&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-bi-integration/&quot;&gt;ClickHouse 系列：與 BI 工具整合（Power BI）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-cloud-vs-self-host/&quot;&gt;ClickHouse 系列：ClickHouse Cloud 與自建部署的優劣比較&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-security-rbac/&quot;&gt;ClickHouse 系列：資料庫安全性與權限管理（RBAC）實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-operator-kubernates/&quot;&gt;ClickHouse 系列：Kubernetes 部署分散式架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-sourcecode-introduction/&quot;&gt;ClickHouse 系列：從原始碼看 MergeTree 的六大核心機制&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>ClickHouse 系列：與 BI 工具整合（Power BI）</title><link>https://vicwen.app/posts/clickhouse-bi-integration/</link><guid isPermaLink="true">https://vicwen.app/posts/clickhouse-bi-integration/</guid><pubDate>Fri, 29 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在企業數據分析場景中，BI（Business Intelligence）工具是將資料轉化為商業決策的重要橋樑。ClickHouse 雖然提供強大的查詢與聚合能力，但若要將分析結果可視化並提供互動式操作，整合 BI 工具是必經之路。&lt;/p&gt;
&lt;p&gt;常見與 ClickHouse 整合的 BI 工具有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Metabase&lt;/strong&gt;：開源、易用，適合中小型團隊快速部署&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Apache Superset&lt;/strong&gt;：開源、可高度自訂，支援多種資料來源&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Power BI&lt;/strong&gt;：微軟旗艦級 BI 工具，整合度高、可與 Excel/Office 365 深度連動&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;本篇將以 &lt;strong&gt;Power BI&lt;/strong&gt; 為例，示範如何將 ClickHouse 資料導入並進行可視化分析。&lt;/p&gt;
&lt;h2&gt;為什麼選擇 Power BI 與 ClickHouse 整合&lt;/h2&gt;
&lt;p&gt;Power BI 在企業端有以下優勢：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;與 Microsoft 生態系統整合&lt;/strong&gt;（Excel、Teams、Azure 等）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;即時儀表板更新&lt;/strong&gt;，適合搭配 ClickHouse 高效查詢能力&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;易於分享與權限控管&lt;/strong&gt;（支援 AD、RBAC）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;支援 DirectQuery / Import 模式&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;整合步驟：Power BI 連接 ClickHouse&lt;/h2&gt;
&lt;h3&gt;1. 安裝 ClickHouse ODBC Driver&lt;/h3&gt;
&lt;p&gt;Power BI 目前無原生 ClickHouse 連接器，因此需使用 &lt;strong&gt;ODBC Driver&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;安裝方式（以 Windows 為例）：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;前往 ClickHouse 官方下載頁
&lt;a href=&quot;https://github.com/ClickHouse/clickhouse-odbc/releases&quot;&gt;https://github.com/ClickHouse/clickhouse-odbc/releases&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;下載對應系統版本（建議 64-bit 與 Power BI Desktop 相容的版本）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;安裝完成後，在 ODBC Data Source Administrator 中新增 DSN：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Driver：ClickHouse ODBC Unicode&lt;/li&gt;
&lt;li&gt;Server：&lt;code&gt;&amp;lt;ClickHouse Host&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Port：&lt;code&gt;8123&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Database：&lt;code&gt;default&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;User：&lt;code&gt;default&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Password：&lt;code&gt;default&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;2. 在 Power BI 中新增資料來源&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;開啟 &lt;strong&gt;Power BI Desktop&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;點擊 &lt;strong&gt;取得資料 (Get Data)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;搜尋並選擇 &lt;strong&gt;ODBC&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;選擇剛才設定的 &lt;strong&gt;ClickHouse DSN&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;輸入 ClickHouse SQL 查詢（或直接選擇資料表）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;範例查詢：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT
    toStartOfDay(EventDate) AS day,
    Action,
    count() AS action_count
FROM user_events
GROUP BY day, Action
ORDER BY day ASC;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3. 設定匯入模式&lt;/h3&gt;
&lt;p&gt;Power BI 提供兩種模式連接 ClickHouse：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;模式&lt;/th&gt;
&lt;th&gt;特點&lt;/th&gt;
&lt;th&gt;適用情境&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Import&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;將資料匯入 Power BI，本地快取，查詢速度快但需手動/排程更新&lt;/td&gt;
&lt;td&gt;靜態報表、每日或每小時更新&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DirectQuery&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;每次查詢時即時連到 ClickHouse，保證最新資料，但效能依賴 ClickHouse 查詢速度&lt;/td&gt;
&lt;td&gt;即時監控、低延遲需求&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;在即時監控場景下，建議使用 &lt;strong&gt;DirectQuery&lt;/strong&gt; 模式，充分發揮 ClickHouse 高速查詢的優勢。&lt;/p&gt;
&lt;h3&gt;4. 建立可視化圖表&lt;/h3&gt;
&lt;p&gt;在 Power BI 中可建立多種圖表類型：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;折線圖 (Line chart)&lt;/strong&gt;：每日事件數變化&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;圓餅圖 (Pie chart)&lt;/strong&gt;：Action 類型比例&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;堆疊直條圖 (Stacked column chart)&lt;/strong&gt;：不同操作在不同日期的分佈&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;卡片 (Card)&lt;/strong&gt;：關鍵指標（如今日事件數、活躍用戶數）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;範例儀表板：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;面板名稱&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;每日事件數趨勢&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;折線圖顯示每日總事件數&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;操作類型比例&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;圓餅圖顯示各 Action 百分比&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;用戶活躍分佈&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;長條圖顯示不同用戶的事件數量排名&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;效能優化建議&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;策略&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;使用 Materialized View 彙總表&lt;/td&gt;
&lt;td&gt;將複雜聚合預先計算後供 Power BI 查詢，避免掃描大表&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;採用 Partition Key&lt;/td&gt;
&lt;td&gt;在 ClickHouse 中對日期或業務維度分區，減少掃描範圍&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;選擇 DirectQuery + 高效 SQL&lt;/td&gt;
&lt;td&gt;確保 Power BI 查詢即時響應&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;減少資料列數量&lt;/td&gt;
&lt;td&gt;使用 &lt;code&gt;LIMIT&lt;/code&gt;、&lt;code&gt;WHERE&lt;/code&gt; 過濾不必要的歷史資料&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;與其他 BI 工具比較&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;工具&lt;/th&gt;
&lt;th&gt;特點&lt;/th&gt;
&lt;th&gt;適用場景&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Metabase&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;安裝簡單、開源免費、支援 SQL &amp;amp; GUI Query&lt;/td&gt;
&lt;td&gt;中小型團隊快速部署&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Superset&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;開源、支援多資料源、可高度自訂&lt;/td&gt;
&lt;td&gt;技術團隊需要靈活擴展&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Power BI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;商業級 BI、與 Microsoft 生態整合&lt;/td&gt;
&lt;td&gt;企業級報表與跨部門協作&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;總結&lt;/h2&gt;
&lt;p&gt;透過 ODBC 連接，ClickHouse 可以與 Power BI 無縫整合，並結合其可視化與分享能力，打造高效、即時的商業分析平台。&lt;/p&gt;
&lt;p&gt;在即時分析場景下，建議採用 &lt;strong&gt;DirectQuery&lt;/strong&gt; 搭配 ClickHouse 的 Materialized View，既能保證資料新鮮度，又能降低查詢負載。&lt;/p&gt;
&lt;h4&gt;ClickHouse 系列持續更新中:&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/what-is-clickhouse/&quot;&gt;ClickHouse 系列：ClickHouse 是什麼？與傳統 OLAP/OLTP 資料庫的差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-column-row-based-storage/&quot;&gt;ClickHouse 系列：ClickHouse 為什麼選擇 Column-based 儲存？講解 Row-based 與 Column-based 的核心差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-engine&quot;&gt;ClickHouse 系列：ClickHouse 儲存引擎 - MergeTree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-compression-skipping-index/&quot;&gt;ClickHouse 系列：壓縮技術與 Data Skipping Indexes 如何大幅加速查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replacingmergetree-deduplication/&quot;&gt;ClickHouse 系列：ReplacingMergeTree 與資料去重機制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-summingmergetree-aggregation/&quot;&gt;ClickHouse 系列：SummingMergeTree 進行資料彙總的應用場景&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-materialized-view/&quot;&gt;ClickHouse 系列：Materialized Views 即時聚合查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-partition-pruning/&quot;&gt;ClickHouse 系列：分區策略與 Partition Pruning 原理解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-primary-sorting-key/&quot;&gt;ClickHouse 系列：Primary Key、Sorting Key 與 Granule 索引運作原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-collapsingmergetree/&quot;&gt;ClickHouse 系列：CollapsingMergeTree 與邏輯刪除的最佳實踐&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-versioned-collapsingmergetree/&quot;&gt;ClickHouse 系列：VersionedCollapsingMergeTree 版本控制與資料衝突解決&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-aggregatingmergetree/&quot;&gt;ClickHouse 系列：AggregatingMergeTree 實時指標統計的進階應用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-distributed-table-architecture/&quot;&gt;ClickHouse 系列：Distributed Table 與分布式查詢架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replication-failover/&quot;&gt;ClickHouse 系列：Replicated Tables 高可用性與零停機升級實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-kafka-data-streaming-pipeline/&quot;&gt;ClickHouse 系列：與 Kafka 整合打造即時 Data Streaming Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-batch-import/&quot;&gt;ClickHouse 系列：批次匯入最佳實踐 (CSV、Parquet、Native Format)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-external-data-integration/&quot;&gt;ClickHouse 系列：ClickHouse 與外部資料源整合（PostgreSQL）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-query-log-explain/&quot;&gt;ClickHouse 系列：如何提升查詢優化？system.query_log 與 EXPLAIN 用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-projections-optimization/&quot;&gt;ClickHouse 系列：Projections 進階查詢加速技術&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-sampling-statistics/&quot;&gt;ClickHouse 系列：Sampling 抽樣查詢與統計技術原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-ttl-storage-management/&quot;&gt;ClickHouse 系列：TTL 資料清理與儲存成本優化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-storage-policies/&quot;&gt;ClickHouse 系列：儲存政策（Storage Policies）與磁碟資源分層策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-schemas-storage-improvement/&quot;&gt;ClickHouse 系列：表格設計與儲存優化細節&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-grafana-dashboard/&quot;&gt;ClickHouse 系列：ClickHouse 系列：整合 Grafana 打造可視化監控&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-select-optimization/&quot;&gt;ClickHouse 系列：查詢優化案例&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-bi-integration/&quot;&gt;ClickHouse 系列：與 BI 工具整合（Power BI）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-cloud-vs-self-host/&quot;&gt;ClickHouse 系列：ClickHouse Cloud 與自建部署的優劣比較&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-security-rbac/&quot;&gt;ClickHouse 系列：資料庫安全性與權限管理（RBAC）實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-operator-kubernates/&quot;&gt;ClickHouse 系列：Kubernetes 部署分散式架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-sourcecode-introduction/&quot;&gt;ClickHouse 系列：從原始碼看 MergeTree 的六大核心機制&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>ClickHouse 系列：查詢優化案例</title><link>https://vicwen.app/posts/clickhouse-select-optimization/</link><guid isPermaLink="true">https://vicwen.app/posts/clickhouse-select-optimization/</guid><pubDate>Thu, 28 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在前幾篇文章中，我們已經介紹過 ClickHouse 的基礎架構、MergeTree 儲存引擎，以及各種索引與資料壓縮機制。這些特性讓 ClickHouse 成為一個極具效能的 OLAP（線上分析處理）資料庫，特別適合處理大規模資料查詢。&lt;/p&gt;
&lt;p&gt;然而，在實際專案中，我們會發現「效能並非理所當然」。同樣一張表、同樣一筆數據，如果查詢設計不當，效能可能差到百倍以上。&lt;/p&gt;
&lt;h2&gt;為什麼需要查詢優化？&lt;/h2&gt;
&lt;p&gt;ClickHouse 的確擅長處理數十億筆資料，但這並不代表我們可以「無腦查詢」。隨著資料量從 &lt;strong&gt;100 萬 → 1000 萬 → 1 億&lt;/strong&gt; 成長，若沒有妥善設計，查詢速度可能會從毫秒級惡化成數秒甚至數十秒，直接影響使用者體驗。&lt;/p&gt;
&lt;p&gt;舉例來說：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dashboard 無法即時更新，導致決策延遲&lt;/li&gt;
&lt;li&gt;API 響應超過數秒，造成前端操作卡頓&lt;/li&gt;
&lt;li&gt;Backend, Data Scientist 無法在合適時間內完成查詢分析&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因此，&lt;strong&gt;查詢優化不只是為了「快」，更是為了系統的穩定性與可擴展性&lt;/strong&gt;。&lt;/p&gt;
&lt;h2&gt;OFFSET 分頁效能差&lt;/h2&gt;
&lt;p&gt;在許多系統中，最常見的需求就是「分頁查詢」。假設我們有一個 &lt;code&gt;events&lt;/code&gt; 表，用來紀錄使用者的行為事件：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT * FROM events 
ORDER BY created_at DESC 
LIMIT 50 OFFSET 1000000;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;這個查詢看似正常，但隨著 OFFSET 變大，效能會急速下降。原因在於 ClickHouse 需要掃描並丟棄前面一百萬筆資料，才能回傳第 1000001 筆到 1000050 筆。&lt;/p&gt;
&lt;h3&gt;優化方案：Keyset Pagination（游標分頁）&lt;/h3&gt;
&lt;p&gt;改用「基於主鍵或排序欄位的分頁」：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT * FROM events 
WHERE created_at &amp;lt; &apos;2025-01-01 00:00:00&apos;
ORDER BY created_at DESC
LIMIT 50;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;這種方式直接從指定時間點往後查詢，不需要丟棄前面的資料，效能大幅提升。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;優化前&lt;/strong&gt;：數秒到數十秒&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;優化後&lt;/strong&gt;：數百毫秒甚至更快&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;這種方法在時間序列資料中特別有效，也符合 ClickHouse 的設計哲學：&lt;strong&gt;盡量掃描少量資料，而非掃描所有資料再過濾&lt;/strong&gt;。&lt;/p&gt;
&lt;h2&gt;WHERE 條件未使用索引&lt;/h2&gt;
&lt;p&gt;另一個常見問題是「查詢條件沒有命中索引」。假設我們要查詢某個使用者最近 7 天的紀錄：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT COUNT(*) 
FROM logs 
WHERE user_id = 123 
AND created_at &amp;gt;= today() - 7;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果表的排序鍵不是 &lt;code&gt;(user_id, created_at)&lt;/code&gt;，這個查詢就會全表掃描，效能極差。&lt;/p&gt;
&lt;h3&gt;優化方案：Primary Key + Partition&lt;/h3&gt;
&lt;p&gt;在建表時，我們應該考慮查詢模式，將常用的過濾條件設定為排序鍵或分區：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE logs
(
    user_id UInt64,
    created_at DateTime,
    event_type String,
    ...
)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(created_at)
ORDER BY (user_id, created_at);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;這樣，當我們查詢某個 &lt;code&gt;user_id&lt;/code&gt; 並限制時間區間時，ClickHouse 會自動進行 &lt;strong&gt;Partition Pruning&lt;/strong&gt;，只掃描必要的資料。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;優化前&lt;/strong&gt;：掃描數億筆資料&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;優化後&lt;/strong&gt;：僅掃描數百萬筆資料，速度提升 10 倍以上&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;重複資料導致彙總變慢&lt;/h2&gt;
&lt;p&gt;在實務上，我們常會遇到「批次匯入導致重複資料」的情境。例如，某些 ETL 程序每天匯入一份全量資料，但會包含重複紀錄。&lt;/p&gt;
&lt;p&gt;原始查詢需要先 &lt;code&gt;GROUP BY&lt;/code&gt; 去重，這對數億筆資料來說效能極差。&lt;/p&gt;
&lt;h3&gt;優化方案 1：ReplacingMergeTree 去重&lt;/h3&gt;
&lt;p&gt;使用 &lt;code&gt;ReplacingMergeTree&lt;/code&gt;，在合併階段自動去除重複紀錄：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE events
(
    id UInt64,
    user_id UInt64,
    event_type String,
    version UInt32
)
ENGINE = ReplacingMergeTree(version)
ORDER BY (id);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;這樣，查詢時就不需要再額外去重，效能大幅提升，這是我在實習過程中親身經歷的QQ，直接重建一張 table，把資料轉移到 &lt;code&gt;ReplacingMergeTree&lt;/code&gt;的 new table 上。&lt;/p&gt;
&lt;h3&gt;優化方案 2：Materialized View 預聚合&lt;/h3&gt;
&lt;p&gt;另一個作法是建立 &lt;strong&gt;MV (Materialized View)&lt;/strong&gt;，將原始資料預先彙總存入新表：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE MATERIALIZED VIEW events_mv 
ENGINE = SummingMergeTree()
ORDER BY (user_id, event_type)
AS
SELECT user_id, event_type, count() AS cnt
FROM events
GROUP BY user_id, event_type;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查詢時只需要對 &lt;code&gt;events_mv&lt;/code&gt; 做 SELECT，效能幾乎是秒殺級。&lt;/p&gt;
&lt;h2&gt;JOIN 效能不佳&lt;/h2&gt;
&lt;p&gt;ClickHouse 的 JOIN 並不像&lt;strong&gt;傳統關聯式資料庫那樣靈活&lt;/strong&gt;，若不小心，效能會很差。&lt;/p&gt;
&lt;p&gt;假設我們要將 &lt;code&gt;events&lt;/code&gt; 表與 &lt;code&gt;users&lt;/code&gt; 表做關聯：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT e.*, u.name 
FROM events e
JOIN users u ON e.user_id = u.id;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;若 &lt;code&gt;users&lt;/code&gt; 是一張大表，JOIN 效能會急速下降。&lt;/p&gt;
&lt;h3&gt;優化方案：Dictionary 加速&lt;/h3&gt;
&lt;p&gt;如果 &lt;code&gt;users&lt;/code&gt; 是一張小表，可以轉成 &lt;strong&gt;Dictionary&lt;/strong&gt;，放到記憶體中供查詢使用：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE DICTIONARY users_dict
(
    id UInt64,
    name String
)
PRIMARY KEY id
SOURCE(CLICKHOUSE(TABLE users))
LAYOUT(HASHED());
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查詢時就可以改寫成：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT e.*, dictGet(&apos;users_dict&apos;, &apos;name&apos;, toUInt64(e.user_id)) AS user_name
FROM events e;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;這種方式等於把 &lt;code&gt;users&lt;/code&gt; 變成一個高效快取，避免大表 JOIN。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;優化前&lt;/strong&gt;：JOIN 查詢數秒甚至數十秒&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;優化後&lt;/strong&gt;：查詢僅需數百毫秒&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;想學習 Dictionary 可參考 &lt;a href=&quot;https://clickhouse.com/docs/dictionary#:~:text=A%20dictionary%20in%20ClickHouse%20provides%20an%20in-memory%20key-value,external%20sources%2C%20optimizing%20for%20super-low%20latency%20lookup%20queries.&quot;&gt;官方文件&lt;/a&gt;， Dictionary 是一個專門給小表作為 cache 的特殊型別，讓你在高頻查詢中，避免每次都去 JOIN 大表，改用 快取好的 Key-Value 對應 來加速查詢時間。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;總結&lt;/h2&gt;
&lt;p&gt;從這些案例可以看到，ClickHouse 的查詢效能優化大致遵循以下原則：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;避免 OFFSET，改用 Keyset 分頁&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;設計良好的排序鍵與分區&lt;/strong&gt;，讓查詢能命中索引&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;使用 MergeTree 變種 (Replacing / Summing)&lt;/strong&gt; 來處理去重與聚合&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;善用 Materialized View&lt;/strong&gt; 預先計算，避免重複運算&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JOIN 最小化&lt;/strong&gt;：小表 JOIN 可以轉成 Dictionary，大表 JOIN 需慎用&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;盡量減少需要掃描的資料量&lt;/strong&gt;，而不是「事後再過濾」&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;透過查詢優化，我們能將原本數秒甚至數十秒的查詢縮短到毫秒級，這對即時分析、線上系統效能都有關鍵意義。&lt;/p&gt;
&lt;p&gt;希望這些案例能幫助大家在實務中更好地駕馭 ClickHouse！ 🚀&lt;/p&gt;
&lt;h4&gt;ClickHouse 系列持續更新中:&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/what-is-clickhouse/&quot;&gt;ClickHouse 系列：ClickHouse 是什麼？與傳統 OLAP/OLTP 資料庫的差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-column-row-based-storage/&quot;&gt;ClickHouse 系列：ClickHouse 為什麼選擇 Column-based 儲存？講解 Row-based 與 Column-based 的核心差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-engine&quot;&gt;ClickHouse 系列：ClickHouse 儲存引擎 - MergeTree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-compression-skipping-index/&quot;&gt;ClickHouse 系列：壓縮技術與 Data Skipping Indexes 如何大幅加速查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replacingmergetree-deduplication/&quot;&gt;ClickHouse 系列：ReplacingMergeTree 與資料去重機制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-summingmergetree-aggregation/&quot;&gt;ClickHouse 系列：SummingMergeTree 進行資料彙總的應用場景&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-materialized-view/&quot;&gt;ClickHouse 系列：Materialized Views 即時聚合查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-partition-pruning/&quot;&gt;ClickHouse 系列：分區策略與 Partition Pruning 原理解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-primary-sorting-key/&quot;&gt;ClickHouse 系列：Primary Key、Sorting Key 與 Granule 索引運作原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-collapsingmergetree/&quot;&gt;ClickHouse 系列：CollapsingMergeTree 與邏輯刪除的最佳實踐&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-versioned-collapsingmergetree/&quot;&gt;ClickHouse 系列：VersionedCollapsingMergeTree 版本控制與資料衝突解決&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-aggregatingmergetree/&quot;&gt;ClickHouse 系列：AggregatingMergeTree 實時指標統計的進階應用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-distributed-table-architecture/&quot;&gt;ClickHouse 系列：Distributed Table 與分布式查詢架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replication-failover/&quot;&gt;ClickHouse 系列：Replicated Tables 高可用性與零停機升級實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-kafka-data-streaming-pipeline/&quot;&gt;ClickHouse 系列：與 Kafka 整合打造即時 Data Streaming Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-batch-import/&quot;&gt;ClickHouse 系列：批次匯入最佳實踐 (CSV、Parquet、Native Format)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-external-data-integration/&quot;&gt;ClickHouse 系列：ClickHouse 與外部資料源整合（PostgreSQL）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-query-log-explain/&quot;&gt;ClickHouse 系列：如何提升查詢優化？system.query_log 與 EXPLAIN 用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-projections-optimization/&quot;&gt;ClickHouse 系列：Projections 進階查詢加速技術&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-sampling-statistics/&quot;&gt;ClickHouse 系列：Sampling 抽樣查詢與統計技術原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-ttl-storage-management/&quot;&gt;ClickHouse 系列：TTL 資料清理與儲存成本優化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-storage-policies/&quot;&gt;ClickHouse 系列：儲存政策（Storage Policies）與磁碟資源分層策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-schemas-storage-improvement/&quot;&gt;ClickHouse 系列：表格設計與儲存優化細節&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-grafana-dashboard/&quot;&gt;ClickHouse 系列：ClickHouse 系列：整合 Grafana 打造可視化監控&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-select-optimization/&quot;&gt;ClickHouse 系列：查詢優化案例&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-bi-integration/&quot;&gt;ClickHouse 系列：與 BI 工具整合（Power BI）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-cloud-vs-self-host/&quot;&gt;ClickHouse 系列：ClickHouse Cloud 與自建部署的優劣比較&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-security-rbac/&quot;&gt;ClickHouse 系列：資料庫安全性與權限管理（RBAC）實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-operator-kubernates/&quot;&gt;ClickHouse 系列：Kubernetes 部署分散式架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-sourcecode-introduction/&quot;&gt;ClickHouse 系列：從原始碼看 MergeTree 的六大核心機制&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>ClickHouse 系列：整合 Grafana 打造可視化監控</title><link>https://vicwen.app/posts/clickhouse-grafana-dashboard/</link><guid isPermaLink="true">https://vicwen.app/posts/clickhouse-grafana-dashboard/</guid><pubDate>Wed, 27 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在資料分析與系統監控場景中，「資料可視化」是將資料洞察的關鍵手段。ClickHouse 雖然提供強大的查詢與聚合能力，但若要打造即時、互動式的監控面板，則需搭配可視化工具。Grafana 作為業界廣泛使用的開源監控平台，與 ClickHouse 的整合能構建靈活且具彈性的監控儀表板。&lt;/p&gt;
&lt;p&gt;本次我們使用該 &lt;a href=&quot;https://github.com/viiccwen/kafka-clickhouse-data-streaming-pipeline/tree/grafana-clickhouse-dashboard&quot;&gt;Repository&lt;/a&gt; 來實作本篇文章內容。&lt;/p&gt;
&lt;h2&gt;Grafana + ClickHouse 架構介紹&lt;/h2&gt;
&lt;p&gt;整體架構如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Kafka / Log / API → ClickHouse → Grafana
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Grafana 作為前端查詢與呈現工具，透過 ClickHouse Plugin 連接 ClickHouse 資料庫，以 SQL 查詢方式讀取彙總後或原始資料，並即時顯示為圖表、儀表板。&lt;/p&gt;
&lt;h2&gt;整合步驟&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;前置作業：請先按照 &lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-kafka-data-streaming-pipeline/&quot;&gt;ClickHouse 系列：與 Kafka 整合打造即時 Data Streaming Pipeline&lt;/a&gt; 實作步驟，並讓 Producer 在背景執行。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;1. 部署 Grafana 與 ClickHouse&lt;/h3&gt;
&lt;p&gt;因為 Repository 已經使用 Docker-Compose 將服務都架設好了，就只展示新增的 Grafana 服務：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    ports:
      - &quot;3000:3000&quot;
    networks:
      - kafka-network
    volumes:
      - grafana-storage:/var/lib/grafana
    environment:
      - GF_INSTALL_PLUGINS=grafana-clickhouse-datasource
    depends_on:
      - clickhouse
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;GF_INSTALL_PLUGINS=grafana-clickhouse-datasource&lt;/code&gt; 代表已經預先安裝好了 ClickHouse Plugin。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;2. 設定資料來源&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/posts/clickhouse-grafana-dashboard/configure-datasource.png&quot; alt=&quot;Configure Datasource&quot; /&gt;&lt;/p&gt;
&lt;p&gt;進入 Grafana UI：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;點選左側的「connections → Data Sources → Add data source」&lt;/li&gt;
&lt;li&gt;搜尋並選擇「ClickHouse」&lt;/li&gt;
&lt;li&gt;填寫連線資訊：&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;項目&lt;/th&gt;
&lt;th&gt;值範例&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Server address&lt;/td&gt;
&lt;td&gt;&lt;code&gt;clickhouse&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server port&lt;/td&gt;
&lt;td&gt;&lt;code&gt;9000&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Credentials&lt;/td&gt;
&lt;td&gt;Username: &lt;code&gt;default&lt;/code&gt; / Password: &lt;code&gt;default&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;測試連線成功後儲存設定。&lt;/p&gt;
&lt;h3&gt;4. 建立儀表板與圖表&lt;/h3&gt;
&lt;h4&gt;查詢範例 1：每日事件數統計&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;SELECT
    toStartOfDay(EventDate) AS day,
    count() AS events
FROM user_events
GROUP BY day
ORDER BY day
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;建議搭配：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;圖表類型：Time Series&lt;/li&gt;
&lt;li&gt;Time field：&lt;code&gt;day&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Value：&lt;code&gt;events&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;查詢範例 2：各操作類型數量統計&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;SELECT
    Action,
    count(*) AS count
FROM user_events
GROUP BY Action
ORDER BY count DESC
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;建議搭配：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;圖表類型：Bar Chart 或 Pie Chart&lt;/li&gt;
&lt;li&gt;分類維度：&lt;code&gt;Action&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;數量值：&lt;code&gt;count&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;時間控制與資料刷新&lt;/h2&gt;
&lt;p&gt;Grafana 支援動態時間區段與自動刷新：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;常見區段：Last 1h、6h、24h、7d...&lt;/li&gt;
&lt;li&gt;自動刷新：10s、30s、1min 可調&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;每個面板（Panel）可自訂時間區段與刷新頻率，並支援全局時間同步。&lt;/p&gt;
&lt;h2&gt;建立警報條件（可選）&lt;/h2&gt;
&lt;p&gt;Grafana 可對每個查詢設定警報條件：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;設定閾值（如：事件數 &amp;gt; 100）&lt;/li&gt;
&lt;li&gt;通知整合：Slack、LINE、Webhook、Email...&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;適用於異常偵測、資源飽和警示等場景。&lt;/p&gt;
&lt;h2&gt;整合常見問題與排查建議&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;問題&lt;/th&gt;
&lt;th&gt;解法建議&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;查無資料&lt;/td&gt;
&lt;td&gt;確認 SQL 中時間欄位是否與 &lt;code&gt;$__timeFilter()&lt;/code&gt; 搭配&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;視覺化畫面為空&lt;/td&gt;
&lt;td&gt;檢查 Time field 與 Value 欄位設定&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;插件未載入 / 失效&lt;/td&gt;
&lt;td&gt;確認版本相容性、重新啟動 Grafana、檢查 plugin 設定&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;效能差&lt;/td&gt;
&lt;td&gt;建議結合 Materialized View 彙總後查詢，避免大表即時掃描&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;進階建議&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;策略&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;使用 Materialized View 預計算&lt;/td&gt;
&lt;td&gt;將複雜聚合查詢預先寫入，讓 Grafana 查詢小表快速回應&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;加入 Kafka + Materialized View 實現即時資料流&lt;/td&gt;
&lt;td&gt;整合 Kafka 寫入 ClickHouse，MV 寫入統計表，Grafana 查詢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;設定 Grafana 繪圖單位（Bytes, Count, % 等）&lt;/td&gt;
&lt;td&gt;提高圖表解讀性&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;使用 Panel Variables 增強互動性&lt;/td&gt;
&lt;td&gt;可讓使用者動態篩選頁面、日期、使用者等維度&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;總結&lt;/h2&gt;
&lt;p&gt;Grafana 是 ClickHouse 完美的可視化搭檔。藉由整合 Grafana，開發者能夠快速建構符合需求的動態 Dashboard，並搭配 ClickHouse 的高效查詢能力打造即時監控平台。&lt;/p&gt;
&lt;h4&gt;ClickHouse 系列持續更新中:&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/what-is-clickhouse/&quot;&gt;ClickHouse 系列：ClickHouse 是什麼？與傳統 OLAP/OLTP 資料庫的差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-column-row-based-storage/&quot;&gt;ClickHouse 系列：ClickHouse 為什麼選擇 Column-based 儲存？講解 Row-based 與 Column-based 的核心差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-engine&quot;&gt;ClickHouse 系列：ClickHouse 儲存引擎 - MergeTree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-compression-skipping-index/&quot;&gt;ClickHouse 系列：壓縮技術與 Data Skipping Indexes 如何大幅加速查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replacingmergetree-deduplication/&quot;&gt;ClickHouse 系列：ReplacingMergeTree 與資料去重機制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-summingmergetree-aggregation/&quot;&gt;ClickHouse 系列：SummingMergeTree 進行資料彙總的應用場景&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-materialized-view/&quot;&gt;ClickHouse 系列：Materialized Views 即時聚合查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-partition-pruning/&quot;&gt;ClickHouse 系列：分區策略與 Partition Pruning 原理解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-primary-sorting-key/&quot;&gt;ClickHouse 系列：Primary Key、Sorting Key 與 Granule 索引運作原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-collapsingmergetree/&quot;&gt;ClickHouse 系列：CollapsingMergeTree 與邏輯刪除的最佳實踐&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-versioned-collapsingmergetree/&quot;&gt;ClickHouse 系列：VersionedCollapsingMergeTree 版本控制與資料衝突解決&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-aggregatingmergetree/&quot;&gt;ClickHouse 系列：AggregatingMergeTree 實時指標統計的進階應用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-distributed-table-architecture/&quot;&gt;ClickHouse 系列：Distributed Table 與分布式查詢架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replication-failover/&quot;&gt;ClickHouse 系列：Replicated Tables 高可用性與零停機升級實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-kafka-data-streaming-pipeline/&quot;&gt;ClickHouse 系列：與 Kafka 整合打造即時 Data Streaming Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-batch-import/&quot;&gt;ClickHouse 系列：批次匯入最佳實踐 (CSV、Parquet、Native Format)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-external-data-integration/&quot;&gt;ClickHouse 系列：ClickHouse 與外部資料源整合（PostgreSQL）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-query-log-explain/&quot;&gt;ClickHouse 系列：如何提升查詢優化？system.query_log 與 EXPLAIN 用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-projections-optimization/&quot;&gt;ClickHouse 系列：Projections 進階查詢加速技術&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-sampling-statistics/&quot;&gt;ClickHouse 系列：Sampling 抽樣查詢與統計技術原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-ttl-storage-management/&quot;&gt;ClickHouse 系列：TTL 資料清理與儲存成本優化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-storage-policies/&quot;&gt;ClickHouse 系列：儲存政策（Storage Policies）與磁碟資源分層策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-schemas-storage-improvement/&quot;&gt;ClickHouse 系列：表格設計與儲存優化細節&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-grafana-dashboard/&quot;&gt;ClickHouse 系列：ClickHouse 系列：整合 Grafana 打造可視化監控&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-select-optimization/&quot;&gt;ClickHouse 系列：查詢優化案例&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-bi-integration/&quot;&gt;ClickHouse 系列：與 BI 工具整合（Power BI）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-cloud-vs-self-host/&quot;&gt;ClickHouse 系列：ClickHouse Cloud 與自建部署的優劣比較&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-security-rbac/&quot;&gt;ClickHouse 系列：資料庫安全性與權限管理（RBAC）實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-operator-kubernates/&quot;&gt;ClickHouse 系列：Kubernetes 部署分散式架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-sourcecode-introduction/&quot;&gt;ClickHouse 系列：從原始碼看 MergeTree 的六大核心機制&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>ClickHouse 系列：表格設計與儲存優化細節</title><link>https://vicwen.app/posts/clickhouse-schemas-storage-improvement/</link><guid isPermaLink="true">https://vicwen.app/posts/clickhouse-schemas-storage-improvement/</guid><pubDate>Tue, 26 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在 ClickHouse 中，表格設計並不是隨便定義欄位就好，因為不同的欄位屬性、型別選擇、壓縮策略，會直接影響 &lt;strong&gt;儲存空間&lt;/strong&gt; 與 &lt;strong&gt;查詢效能&lt;/strong&gt;。
今天我們就來深入探討實戰中最容易忽略的幾個細節，包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Default vs Nullable 的空間差異&lt;/li&gt;
&lt;li&gt;型別精簡化&lt;/li&gt;
&lt;li&gt;LowCardinality 的節省效果&lt;/li&gt;
&lt;li&gt;Codecs 壓縮技巧&lt;/li&gt;
&lt;li&gt;欄位設計最佳實踐&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Default Value vs Nullable&lt;/h2&gt;
&lt;p&gt;在 ClickHouse 中，&lt;code&gt;Nullable(T)&lt;/code&gt; 會額外為每列儲存一個 &lt;strong&gt;null bitmap&lt;/strong&gt;（每列 1 bit），即使大多數值不是 NULL，也會有空間消耗。
如果欄位幾乎不會是 NULL，改用 &lt;code&gt;DEFAULT&lt;/code&gt; 會更省空間。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;設定方式&lt;/th&gt;
&lt;th&gt;儲存空間&lt;/th&gt;
&lt;th&gt;查詢效能&lt;/th&gt;
&lt;th&gt;適用情境&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Nullable&lt;/td&gt;
&lt;td&gt;多 1 bit/列&lt;/td&gt;
&lt;td&gt;無明顯差異&lt;/td&gt;
&lt;td&gt;資料缺失頻繁&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Default&lt;/td&gt;
&lt;td&gt;無額外 bitmap&lt;/td&gt;
&lt;td&gt;無明顯差異&lt;/td&gt;
&lt;td&gt;欄位幾乎都有值&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;建議&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-- 不推薦
age Nullable(UInt8)

-- 推薦
age UInt8 DEFAULT 0
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;型別精簡化&lt;/h2&gt;
&lt;p&gt;ClickHouse 的 columnar 儲存讓我們可以用更小的型別節省大量空間。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;整數範圍選擇&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;UInt8&lt;/code&gt; → 0~255&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UInt16&lt;/code&gt; → 0~65535&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UInt32&lt;/code&gt; → 一般 ID&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UInt64&lt;/code&gt; → 需支援非常大的整數 ID&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;浮點與 Decimal&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;兩位小數金額 → &lt;code&gt;Decimal(9, 2)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;高精度科學計算 → &lt;code&gt;Float64&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;日期與時間&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Date&lt;/code&gt;（2 bytes）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DateTime&lt;/code&gt;（4 bytes）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;LowCardinality 優化字串&lt;/h2&gt;
&lt;p&gt;對於&lt;strong&gt;重複率高&lt;/strong&gt;的字串（如地區、狀態），&lt;code&gt;LowCardinality(String)&lt;/code&gt; 會使用字典映射，大幅減少重複儲存。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ALTER TABLE orders
MODIFY COLUMN status LowCardinality(String);
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;欄位類型&lt;/th&gt;
&lt;th&gt;重複率高時空間使用&lt;/th&gt;
&lt;th&gt;重複率低時空間使用&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;td&gt;高&lt;/td&gt;
&lt;td&gt;高&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LowCardinality(String)&lt;/td&gt;
&lt;td&gt;低&lt;/td&gt;
&lt;td&gt;反而略高&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;FixedString 的空間優化&lt;/h2&gt;
&lt;p&gt;適用固定長度欄位（如代碼、哈希值），壓縮效率高，但不足長度會補零，可能浪費空間。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;code FixedString(8)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Codecs 壓縮技巧&lt;/h2&gt;
&lt;p&gt;ClickHouse 可針對欄位設定壓縮方式，例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ZSTD&lt;/code&gt;：高壓縮比&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DoubleDelta&lt;/code&gt;：適合時間序列數字&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Delta&lt;/code&gt;：適合日期欄位&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE events
(
    id UInt32 CODEC(ZSTD(3)),
    date Date CODEC(DoubleDelta)
) ENGINE = MergeTree();
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;欄位設計最佳實踐&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;高基數欄位放前面&lt;/strong&gt; → 提高索引效率&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;稀疏欄位分表&lt;/strong&gt; → 減少空值空間浪費&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nested / Tuple&lt;/strong&gt; → 降低欄位數量與儲存 overhead&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;範例設計&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE user_events
(
    EventDate Date DEFAULT today(),
    UserID UInt32,
    Action LowCardinality(String),
    Version UInt8 DEFAULT 1
) ENGINE = MergeTree()
PARTITION BY toYYYYMM(EventDate)
ORDER BY (EventDate, UserID);
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;EventDate&lt;/code&gt; 用 Default，省掉 Nullable bitmap&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Action&lt;/code&gt; 用 LowCardinality 減少字串重複儲存&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Version&lt;/code&gt; 用 UInt8 並設定 Default&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;結語&lt;/h2&gt;
&lt;p&gt;表格設計是 ClickHouse 成本優化與效能的第一步。
透過合理選擇欄位屬性（Default、型別、LowCardinality、Codecs），可以在相同硬體資源下 &lt;strong&gt;省下數倍空間&lt;/strong&gt;，並讓查詢速度大幅提升。&lt;/p&gt;
&lt;h3&gt;ClickHouse 系列持續更新中:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/what-is-clickhouse/&quot;&gt;ClickHouse 系列：ClickHouse 是什麼？與傳統 OLAP/OLTP 資料庫的差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-column-row-based-storage/&quot;&gt;ClickHouse 系列：ClickHouse 為什麼選擇 Column-based 儲存？講解 Row-based 與 Column-based 的核心差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-engine&quot;&gt;ClickHouse 系列：ClickHouse 儲存引擎 - MergeTree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-compression-skipping-index/&quot;&gt;ClickHouse 系列：壓縮技術與 Data Skipping Indexes 如何大幅加速查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replacingmergetree-deduplication/&quot;&gt;ClickHouse 系列：ReplacingMergeTree 與資料去重機制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-summingmergetree-aggregation/&quot;&gt;ClickHouse 系列：SummingMergeTree 進行資料彙總的應用場景&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-materialized-view/&quot;&gt;ClickHouse 系列：Materialized Views 即時聚合查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-partition-pruning/&quot;&gt;ClickHouse 系列：分區策略與 Partition Pruning 原理解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-primary-sorting-key/&quot;&gt;ClickHouse 系列：Primary Key、Sorting Key 與 Granule 索引運作原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-collapsingmergetree/&quot;&gt;ClickHouse 系列：CollapsingMergeTree 與邏輯刪除的最佳實踐&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-versioned-collapsingmergetree/&quot;&gt;ClickHouse 系列：VersionedCollapsingMergeTree 版本控制與資料衝突解決&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-aggregatingmergetree/&quot;&gt;ClickHouse 系列：AggregatingMergeTree 實時指標統計的進階應用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-distributed-table-architecture/&quot;&gt;ClickHouse 系列：Distributed Table 與分布式查詢架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replication-failover/&quot;&gt;ClickHouse 系列：Replicated Tables 高可用性與零停機升級實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-kafka-data-streaming-pipeline/&quot;&gt;ClickHouse 系列：與 Kafka 整合打造即時 Data Streaming Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-batch-import/&quot;&gt;ClickHouse 系列：批次匯入最佳實踐 (CSV、Parquet、Native Format)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-external-data-integration/&quot;&gt;ClickHouse 系列：ClickHouse 與外部資料源整合（PostgreSQL）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-query-log-explain/&quot;&gt;ClickHouse 系列：如何提升查詢優化？system.query_log 與 EXPLAIN 用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-projections-optimization/&quot;&gt;ClickHouse 系列：Projections 進階查詢加速技術&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-sampling-statistics/&quot;&gt;ClickHouse 系列：Sampling 抽樣查詢與統計技術原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-ttl-storage-management/&quot;&gt;ClickHouse 系列：TTL 資料清理與儲存成本優化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-storage-policies/&quot;&gt;ClickHouse 系列：儲存政策（Storage Policies）與磁碟資源分層策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-schemas-storage-improvement/&quot;&gt;ClickHouse 系列：表格設計與儲存優化細節&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-grafana-dashboard/&quot;&gt;ClickHouse 系列：ClickHouse 系列：整合 Grafana 打造可視化監控&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-select-optimization/&quot;&gt;ClickHouse 系列：查詢優化案例&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-bi-integration/&quot;&gt;ClickHouse 系列：與 BI 工具整合（Power BI）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-cloud-vs-self-host/&quot;&gt;ClickHouse 系列：ClickHouse Cloud 與自建部署的優劣比較&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-security-rbac/&quot;&gt;ClickHouse 系列：資料庫安全性與權限管理（RBAC）實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-operator-kubernates/&quot;&gt;ClickHouse 系列：Kubernetes 部署分散式架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-sourcecode-introduction/&quot;&gt;ClickHouse 系列：從原始碼看 MergeTree 的六大核心機制&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>ClickHouse 系列：儲存政策（Storage Policies）與磁碟資源分層策略</title><link>https://vicwen.app/posts/clickhouse-storage-policies/</link><guid isPermaLink="true">https://vicwen.app/posts/clickhouse-storage-policies/</guid><pubDate>Mon, 25 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;當你的 ClickHouse 資料規模從 GB、TB 成長到 PB 時，&lt;strong&gt;如何妥善分配 SSD、HDD、甚至雲端冷儲存資源&lt;/strong&gt;，變得至關重要。ClickHouse 透過 &lt;strong&gt;Storage Policies (儲存政策)&lt;/strong&gt;，提供了極為靈活的磁碟分層架構，不僅能優化查詢效能，也能大幅降低儲存成本。&lt;/p&gt;
&lt;h2&gt;Storage Policies 是什麼？&lt;/h2&gt;
&lt;p&gt;Storage Policies 是 ClickHouse 內部管理資料儲存位置與分層邏輯的配置機制。它將磁碟資源劃分為不同層級（Volumes），並根據資料大小、TTL、Merge 條件等動態將資料移動至不同層級磁碟。&lt;/p&gt;
&lt;p&gt;你可以做到：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;熱資料存在 SSD，冷資料自動移至 HDD 或雲端 S3。&lt;/li&gt;
&lt;li&gt;根據 Data Part 大小動態調度存儲位置。&lt;/li&gt;
&lt;li&gt;搭配 TTL 策略，實現資料生命週期全自動管理。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Storage Policies 結構&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;storage_configuration&amp;gt;
    &amp;lt;disks&amp;gt;
        &amp;lt;disk_ssd&amp;gt;
            &amp;lt;path&amp;gt;/var/lib/clickhouse/ssd/&amp;lt;/path&amp;gt;
        &amp;lt;/disk_ssd&amp;gt;
        &amp;lt;disk_hdd&amp;gt;
            &amp;lt;path&amp;gt;/var/lib/clickhouse/hdd/&amp;lt;/path&amp;gt;
        &amp;lt;/disk_hdd&amp;gt;
        &amp;lt;disk_s3&amp;gt;
            &amp;lt;type&amp;gt;s3&amp;lt;/type&amp;gt;
            &amp;lt;endpoint&amp;gt;https://s3.amazonaws.com/your-bucket/&amp;lt;/endpoint&amp;gt;
            &amp;lt;access_key_id&amp;gt;YOUR_KEY&amp;lt;/access_key_id&amp;gt;
            &amp;lt;secret_access_key&amp;gt;YOUR_SECRET&amp;lt;/secret_access_key&amp;gt;
        &amp;lt;/disk_s3&amp;gt;
    &amp;lt;/disks&amp;gt;
    &amp;lt;policies&amp;gt;
        &amp;lt;tiered_policy&amp;gt;
            &amp;lt;volumes&amp;gt;
                &amp;lt;hot&amp;gt;
                    &amp;lt;disk&amp;gt;disk_ssd&amp;lt;/disk&amp;gt;
                &amp;lt;/hot&amp;gt;
                &amp;lt;cold&amp;gt;
                    &amp;lt;disk&amp;gt;disk_hdd&amp;lt;/disk&amp;gt;
                    &amp;lt;max_data_part_size_bytes&amp;gt;5000000000&amp;lt;/max_data_part_size_bytes&amp;gt;
                &amp;lt;/cold&amp;gt;
                &amp;lt;archive&amp;gt;
                    &amp;lt;disk&amp;gt;disk_s3&amp;lt;/disk&amp;gt;
                &amp;lt;/archive&amp;gt;
            &amp;lt;/volumes&amp;gt;
        &amp;lt;/tiered_policy&amp;gt;
    &amp;lt;/policies&amp;gt;
&amp;lt;/storage_configuration&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;磁碟分層（Volumes）設計原則&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;層級&lt;/th&gt;
&lt;th&gt;磁碟類型&lt;/th&gt;
&lt;th&gt;適用資料&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hot&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SSD&lt;/td&gt;
&lt;td&gt;近 7 天高頻查詢資料&lt;/td&gt;
&lt;td&gt;保證讀取速度與低延遲&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cold&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;HDD&lt;/td&gt;
&lt;td&gt;歷史數據或低頻查詢資料&lt;/td&gt;
&lt;td&gt;儲存成本較低，適合冷資料&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Archive&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;S3&lt;/td&gt;
&lt;td&gt;歸檔資料、不常查詢但需長期保留&lt;/td&gt;
&lt;td&gt;跨區域備份、儲存無上限、成本最低&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;配合 TTL 實現自動熱冷資料分層&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE user_logs
(
    EventDate DateTime,
    UserID UInt64,
    Action String
) ENGINE = MergeTree
ORDER BY (UserID, EventDate)
SETTINGS storage_policy = &apos;tiered_policy&apos;
TTL EventDate + INTERVAL 7 DAY TO VOLUME &apos;cold&apos;,
    EventDate + INTERVAL 30 DAY TO VOLUME &apos;archive&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;這樣設計後：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;近 7 天資料會在 SSD。&lt;/li&gt;
&lt;li&gt;7 天到 30 天資料移動到 HDD。&lt;/li&gt;
&lt;li&gt;超過 30 天資料會自動移到 S3 儲存。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;觀察資料分佈情況&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;SELECT
    name AS table_name,
    disk_name,
    count() AS parts
FROM system.parts
WHERE active AND table = &apos;user_logs&apos;
GROUP BY table_name, disk_name;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;能夠即時掌握資料是儲存在 SSD、HDD，還是 S3。&lt;/p&gt;
&lt;h2&gt;Storage Policies 與 MergeTree 的互動&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;新資料寫入 → Hot Volume&lt;/strong&gt;（除非超過 Part Size 限制）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;背景合併 (Merge) 時會根據 Part 大小、TTL 將資料移動至下層 Volume&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;儲存層級的選擇與分配完全由 Storage Policies 決定&lt;/strong&gt;，不需手動干預。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;儲存政策最佳實踐&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;分層設計要「能自動運作」&lt;/strong&gt; → 不需手動移動資料。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;選擇適合的磁碟路徑與掛載點&lt;/strong&gt; → SSD 用於熱資料、HDD 為冷資料、S3 作為歷史歸檔。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;搭配 TTL 做時間序列資料管理&lt;/strong&gt; → 自動清理與降層儲存。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;監控系統.parts 與.part_log&lt;/strong&gt; → 定期檢查 Part 移動情況與執行效能。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;進階：監控 Storage Policies 實際運作情況&lt;/h2&gt;
&lt;p&gt;ClickHouse 提供了 &lt;code&gt;system.storage_policies&lt;/code&gt; 系統表，讓你能夠隨時檢查 &lt;strong&gt;Storage Policies 與 Volume 配置&lt;/strong&gt;，並了解磁碟資源的優先順序與分層邏輯。&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;system.storage_policies&lt;/code&gt; 欄位解讀&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;欄位名稱&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;policy_name&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;儲存政策名稱。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;volume_name&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;所屬 Volume 名稱。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;volume_priority&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Volume 優先順序，數字越小優先度越高 (0 最優先)。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;disks&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;該 Volume 內包含的磁碟列表。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;volume_type&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Volume 類型 (JBOD, SINGLE_DISK, UNKNOWN)。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;max_data_part_size&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;該 Volume 可儲存的最大 Data Part 大小 (0 代表無上限)。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;move_factor&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;當 Volume 剩餘空間不足 (Free Space Ratio 超過此值) 時，ClickHouse 會將資料移動到下一個 Volume。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;prefer_not_to_merge&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;是否避免在該 Volume 進行 Merge（理論上不建議啟用）。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;perform_ttl_move_on_insert&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;插入資料時若符合 TTL 規則是否立即執行移動。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;load_balancing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;當 Volume 中包含多顆磁碟時，資料寫入的負載平衡策略（ROUND_ROBIN 或 LEAST_USED）。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;檢查儲存政策與 Volume 配置&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;SELECT
    policy_name,
    volume_name,
    volume_priority,
    disks,
    volume_type,
    max_data_part_size,
    move_factor,
    load_balancing
FROM system.storage_policies
WHERE policy_name = &apos;tiered_policy&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;結語&lt;/h2&gt;
&lt;p&gt;ClickHouse 的 Storage Policies 不僅僅是磁碟資源管理工具，更是「大規模數據儲存成本優化策略」的核心武器。只要正確設計，就能讓你的 ClickHouse 叢集具備自動儲存分層、效能與成本兼顧的絕佳特性。&lt;/p&gt;
&lt;h3&gt;ClickHouse 系列持續更新中:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/what-is-clickhouse/&quot;&gt;ClickHouse 系列：ClickHouse 是什麼？與傳統 OLAP/OLTP 資料庫的差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-column-row-based-storage/&quot;&gt;ClickHouse 系列：ClickHouse 為什麼選擇 Column-based 儲存？講解 Row-based 與 Column-based 的核心差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-engine&quot;&gt;ClickHouse 系列：ClickHouse 儲存引擎 - MergeTree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-compression-skipping-index/&quot;&gt;ClickHouse 系列：壓縮技術與 Data Skipping Indexes 如何大幅加速查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replacingmergetree-deduplication/&quot;&gt;ClickHouse 系列：ReplacingMergeTree 與資料去重機制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-summingmergetree-aggregation/&quot;&gt;ClickHouse 系列：SummingMergeTree 進行資料彙總的應用場景&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-materialized-view/&quot;&gt;ClickHouse 系列：Materialized Views 即時聚合查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-partition-pruning/&quot;&gt;ClickHouse 系列：分區策略與 Partition Pruning 原理解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-primary-sorting-key/&quot;&gt;ClickHouse 系列：Primary Key、Sorting Key 與 Granule 索引運作原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-collapsingmergetree/&quot;&gt;ClickHouse 系列：CollapsingMergeTree 與邏輯刪除的最佳實踐&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-versioned-collapsingmergetree/&quot;&gt;ClickHouse 系列：VersionedCollapsingMergeTree 版本控制與資料衝突解決&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-aggregatingmergetree/&quot;&gt;ClickHouse 系列：AggregatingMergeTree 實時指標統計的進階應用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-distributed-table-architecture/&quot;&gt;ClickHouse 系列：Distributed Table 與分布式查詢架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replication-failover/&quot;&gt;ClickHouse 系列：Replicated Tables 高可用性與零停機升級實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-kafka-data-streaming-pipeline/&quot;&gt;ClickHouse 系列：與 Kafka 整合打造即時 Data Streaming Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-batch-import/&quot;&gt;ClickHouse 系列：批次匯入最佳實踐 (CSV、Parquet、Native Format)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-external-data-integration/&quot;&gt;ClickHouse 系列：ClickHouse 與外部資料源整合（PostgreSQL）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-query-log-explain/&quot;&gt;ClickHouse 系列：如何提升查詢優化？system.query_log 與 EXPLAIN 用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-projections-optimization/&quot;&gt;ClickHouse 系列：Projections 進階查詢加速技術&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-sampling-statistics/&quot;&gt;ClickHouse 系列：Sampling 抽樣查詢與統計技術原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-ttl-storage-management/&quot;&gt;ClickHouse 系列：TTL 資料清理與儲存成本優化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-storage-policies/&quot;&gt;ClickHouse 系列：儲存政策（Storage Policies）與磁碟資源分層策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-schemas-storage-improvement/&quot;&gt;ClickHouse 系列：表格設計與儲存優化細節&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-grafana-dashboard/&quot;&gt;ClickHouse 系列：ClickHouse 系列：整合 Grafana 打造可視化監控&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-select-optimization/&quot;&gt;ClickHouse 系列：查詢優化案例&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-bi-integration/&quot;&gt;ClickHouse 系列：與 BI 工具整合（Power BI）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-cloud-vs-self-host/&quot;&gt;ClickHouse 系列：ClickHouse Cloud 與自建部署的優劣比較&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-security-rbac/&quot;&gt;ClickHouse 系列：資料庫安全性與權限管理（RBAC）實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-operator-kubernates/&quot;&gt;ClickHouse 系列：Kubernetes 部署分散式架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-sourcecode-introduction/&quot;&gt;ClickHouse 系列：從原始碼看 MergeTree 的六大核心機制&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>ClickHouse 系列：TTL 資料清理與儲存成本優化</title><link>https://vicwen.app/posts/clickhouse-ttl-storage-management/</link><guid isPermaLink="true">https://vicwen.app/posts/clickhouse-ttl-storage-management/</guid><pubDate>Sun, 24 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;隨著時間的資料量成長，如何免去手工、使用自動化進行過期資料清理與儲存成本控制，成為大型數據系統設計中不可忽視的一環。ClickHouse 提供了 &lt;strong&gt;TTL（Time To Live）資料清理機制&lt;/strong&gt;，不僅能自動刪除過期資料，還能將資料移動至冷儲存 (如 S3、HDD)，有效降低儲存成本。&lt;/p&gt;
&lt;h2&gt;什麼是 TTL？&lt;/h2&gt;
&lt;p&gt;TTL (Time To Live) 是指設定資料的「生命週期」，當資料達到指定條件時，ClickHouse 會自動進行清理（刪除）或儲存層級移動 (Move to Volume)。&lt;/p&gt;
&lt;p&gt;TTL 可以套用在：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;整行資料 (Row TTL)&lt;/strong&gt; → 自動刪除過期資料。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;欄位資料 (Column TTL)&lt;/strong&gt; → 針對指定欄位進行清理。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Volume TTL&lt;/strong&gt; → 將資料從 SSD 移至 HDD/S3 等不同 Volume 以降低儲存成本。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;範例&lt;/h2&gt;
&lt;h3&gt;1. Row TTL：自動刪除過期資料&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE events
(
    EventDate DateTime,
    UserID UInt64,
    Action String
) ENGINE = MergeTree
PARTITION BY toYYYYMM(EventDate)
ORDER BY (UserID, EventDate)
TTL EventDate + INTERVAL 7 DAY;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;這會讓資料在 &lt;code&gt;EventDate&lt;/code&gt; 超過 7 天後，自動被清除。&lt;/p&gt;
&lt;h3&gt;2. Column TTL：指定欄位過期清除&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE logs
(
    EventDate DateTime,
    UserID UInt64,
    Action String,
    TempField String TTL EventDate + INTERVAL 1 DAY
) ENGINE = MergeTree
ORDER BY UserID;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;TempField&lt;/code&gt; 欄位資料會在 1 天後被清除，但行資料仍會保留。&lt;/p&gt;
&lt;h3&gt;3. Volume TTL：自動分層存儲 (Hot → Cold Storage)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;// config.xml
&amp;lt;storage_configuration&amp;gt;
    &amp;lt;disks&amp;gt;
        &amp;lt;disk_ssd&amp;gt;
            &amp;lt;path&amp;gt;/var/lib/clickhouse/ssd/&amp;lt;/path&amp;gt;
        &amp;lt;/disk_ssd&amp;gt;
        &amp;lt;disk_hdd&amp;gt;
            &amp;lt;path&amp;gt;/var/lib/clickhouse/hdd/&amp;lt;/path&amp;gt;
        &amp;lt;/disk_hdd&amp;gt;
    &amp;lt;/disks&amp;gt;
    &amp;lt;policies&amp;gt;
        &amp;lt;tiered_policy&amp;gt;
            &amp;lt;volumes&amp;gt;
                &amp;lt;hot&amp;gt;
                    &amp;lt;disk&amp;gt;disk_ssd&amp;lt;/disk&amp;gt;
                &amp;lt;/hot&amp;gt;
                &amp;lt;cold&amp;gt;
                    &amp;lt;disk&amp;gt;disk_hdd&amp;lt;/disk&amp;gt;
                    &amp;lt;max_data_part_size_bytes&amp;gt;5000000000&amp;lt;/max_data_part_size_bytes&amp;gt;
                &amp;lt;/cold&amp;gt;
            &amp;lt;/volumes&amp;gt;
        &amp;lt;/tiered_policy&amp;gt;
    &amp;lt;/policies&amp;gt;
&amp;lt;/storage_configuration&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE events_tiered
(
    EventDate DateTime,
    UserID UInt64,
    Action String
) ENGINE = MergeTree
ORDER BY (UserID, EventDate)
SETTINGS storage_policy = &apos;tiered_policy&apos;
TTL EventDate + INTERVAL 7 DAY TO VOLUME &apos;cold&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;前 7 天資料會放在 SSD (Hot)&lt;/li&gt;
&lt;li&gt;超過 7 天後資料會自動移動到 HDD (Cold)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;TTL 清理原理與執行時機&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;觸發時機&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;背景合併 (Merge)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;TTL 清理與 Volume 移動是在 Merge 階段一併處理。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ALTER TABLE FREEZE&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;可以手動強制觸發。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;清理非即時性&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;TTL 並非即時刪除，取決於背景合併頻率與資源情況。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;建議：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;調整 &lt;strong&gt;merge_with_ttl_timeout&lt;/strong&gt; 與 &lt;strong&gt;merge_with_recompression_ttl_timeout&lt;/strong&gt; 設定，縮短 TTL 觸發時間。&lt;/li&gt;
&lt;li&gt;查看 &lt;strong&gt;system.part_log&lt;/strong&gt; 可追蹤哪些 Data Part 進行了 TTL 動作。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;案例&lt;/h2&gt;
&lt;h3&gt;即時行為數據保留 7 天&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE user_behavior
(
    EventDate DateTime,
    UserID UInt64,
    Action String
) ENGINE = MergeTree
ORDER BY (UserID, EventDate)
TTL EventDate + INTERVAL 7 DAY;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;詳細 Log 移至冷存儲，保留近 3 天的熱資料&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE logs_tiered
(
    EventDate DateTime,
    LogID UUID,
    Details String
) ENGINE = MergeTree
ORDER BY (LogID, EventDate)
SETTINGS storage_policy = &apos;tiered_policy&apos;
TTL EventDate + INTERVAL 3 DAY TO VOLUME &apos;cold&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;監控與驗證 TTL 執行情況&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;查詢資料片段 TTL 狀態：&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;SELECT table, partition_id, min(min_ttl), min(max_ttl)
FROM system.parts
WHERE active = 1
GROUP BY table, partition_id;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;強制清理 (不建議頻繁使用)：&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;OPTIMIZE TABLE events FINAL;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;追蹤 Part 變化紀錄 (&lt;code&gt;system.part_log&lt;/code&gt;)：&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;SELECT * FROM system.part_log WHERE event_type = &apos;MergeParts&apos; AND table = &apos;events&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;結語&lt;/h2&gt;
&lt;p&gt;TTL 不只是過期資料清理，更是&lt;strong&gt;控制 ClickHouse 儲存資源分層 (SSD → HDD → S3)&lt;/strong&gt; 的重要利器。適當設計 TTL 策略，能幫助你在效能與成本之間取得最佳平衡。（老闆也會很愛你的）&lt;/p&gt;
&lt;h3&gt;ClickHouse 系列持續更新中:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/what-is-clickhouse/&quot;&gt;ClickHouse 系列：ClickHouse 是什麼？與傳統 OLAP/OLTP 資料庫的差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-column-row-based-storage/&quot;&gt;ClickHouse 系列：ClickHouse 為什麼選擇 Column-based 儲存？講解 Row-based 與 Column-based 的核心差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-engine&quot;&gt;ClickHouse 系列：ClickHouse 儲存引擎 - MergeTree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-compression-skipping-index/&quot;&gt;ClickHouse 系列：壓縮技術與 Data Skipping Indexes 如何大幅加速查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replacingmergetree-deduplication/&quot;&gt;ClickHouse 系列：ReplacingMergeTree 與資料去重機制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-summingmergetree-aggregation/&quot;&gt;ClickHouse 系列：SummingMergeTree 進行資料彙總的應用場景&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-materialized-view/&quot;&gt;ClickHouse 系列：Materialized Views 即時聚合查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-partition-pruning/&quot;&gt;ClickHouse 系列：分區策略與 Partition Pruning 原理解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-primary-sorting-key/&quot;&gt;ClickHouse 系列：Primary Key、Sorting Key 與 Granule 索引運作原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-collapsingmergetree/&quot;&gt;ClickHouse 系列：CollapsingMergeTree 與邏輯刪除的最佳實踐&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-versioned-collapsingmergetree/&quot;&gt;ClickHouse 系列：VersionedCollapsingMergeTree 版本控制與資料衝突解決&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-aggregatingmergetree/&quot;&gt;ClickHouse 系列：AggregatingMergeTree 實時指標統計的進階應用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-distributed-table-architecture/&quot;&gt;ClickHouse 系列：Distributed Table 與分布式查詢架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replication-failover/&quot;&gt;ClickHouse 系列：Replicated Tables 高可用性與零停機升級實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-kafka-data-streaming-pipeline/&quot;&gt;ClickHouse 系列：與 Kafka 整合打造即時 Data Streaming Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-batch-import/&quot;&gt;ClickHouse 系列：批次匯入最佳實踐 (CSV、Parquet、Native Format)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-external-data-integration/&quot;&gt;ClickHouse 系列：ClickHouse 與外部資料源整合（PostgreSQL）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-query-log-explain/&quot;&gt;ClickHouse 系列：如何提升查詢優化？system.query_log 與 EXPLAIN 用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-projections-optimization/&quot;&gt;ClickHouse 系列：Projections 進階查詢加速技術&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-sampling-statistics/&quot;&gt;ClickHouse 系列：Sampling 抽樣查詢與統計技術原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-ttl-storage-management/&quot;&gt;ClickHouse 系列：TTL 資料清理與儲存成本優化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-storage-policies/&quot;&gt;ClickHouse 系列：儲存政策（Storage Policies）與磁碟資源分層策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-schemas-storage-improvement/&quot;&gt;ClickHouse 系列：表格設計與儲存優化細節&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-grafana-dashboard/&quot;&gt;ClickHouse 系列：ClickHouse 系列：整合 Grafana 打造可視化監控&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-select-optimization/&quot;&gt;ClickHouse 系列：查詢優化案例&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-bi-integration/&quot;&gt;ClickHouse 系列：與 BI 工具整合（Power BI）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-cloud-vs-self-host/&quot;&gt;ClickHouse 系列：ClickHouse Cloud 與自建部署的優劣比較&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-security-rbac/&quot;&gt;ClickHouse 系列：資料庫安全性與權限管理（RBAC）實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-operator-kubernates/&quot;&gt;ClickHouse 系列：Kubernetes 部署分散式架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-sourcecode-introduction/&quot;&gt;ClickHouse 系列：從原始碼看 MergeTree 的六大核心機制&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>ClickHouse 系列：Sampling 抽樣查詢與統計技術原理</title><link>https://vicwen.app/posts/clickhouse-sampling-statistics/</link><guid isPermaLink="true">https://vicwen.app/posts/clickhouse-sampling-statistics/</guid><pubDate>Sat, 23 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;當面對 PB 級大數據查詢時，如何在不影響統計結論的前提下，快速獲得近似結果？ClickHouse 提供了高效的 &lt;strong&gt;Sampling 抽樣查詢技術&lt;/strong&gt;，讓你能夠用「&lt;strong&gt;1% 的資料，取得 95% 準確度的結果&lt;/strong&gt;」。&lt;/p&gt;
&lt;h2&gt;什麼是 Sampling？&lt;/h2&gt;
&lt;p&gt;Sampling 是一種讓查詢只掃描部分資料進行統計預估的技術，主要應用於：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dashboard 即時指標大盤&lt;/li&gt;
&lt;li&gt;PB 級大數據近似統計查詢&lt;/li&gt;
&lt;li&gt;全表掃描耗時過久的場景&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ClickHouse 透過「Sampling Key」來實現有序與隨機性兼具的抽樣機制。&lt;/p&gt;
&lt;h2&gt;工作原理&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;SAMPLE BY&lt;/strong&gt; 欄位為 Hash 分布基準。&lt;/li&gt;
&lt;li&gt;查詢時可透過 &lt;strong&gt;SAMPLE K&lt;/strong&gt; 讓 ClickHouse 只掃描 K 百分比的資料。&lt;/li&gt;
&lt;li&gt;抽樣是&lt;strong&gt;確定性&lt;/strong&gt;的，對同一條件查詢結果不會改變。&lt;/li&gt;
&lt;li&gt;跨表 Sampling Key 一致時，可支援 JOIN/IN 子查詢下的抽樣一致性。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;SAMPLE 語法用法與差異&lt;/h2&gt;
&lt;h3&gt;1. SAMPLE k&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;k 為 0 到 1 的浮點數。&lt;/li&gt;
&lt;li&gt;查詢會隨機挑選約 k 比例的資料片段 (Granules) 進行處理。&lt;/li&gt;
&lt;li&gt;聚合值需手動乘上 K 倍來還原近似統計結果。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;SELECT Action, count() * 10 AS cnt
FROM user_events
SAMPLE 0.1
GROUP BY Action;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;這段 SQL 會只讀取 10% 資料，查詢結果再乘上 10 還原。&lt;/p&gt;
&lt;h3&gt;2. SAMPLE N&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;N 為目標處理的行數 (近似值)。&lt;/li&gt;
&lt;li&gt;ClickHouse 會掃描至少 N 筆資料的顆粒 (Granules)。&lt;/li&gt;
&lt;li&gt;使用 &lt;strong&gt;_sample_factor&lt;/strong&gt; 虛擬欄位來自動估算放大倍率。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;SELECT sum(PageViews * _sample_factor)
FROM visits
SAMPLE 10000000;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;SELECT sum(_sample_factor)
FROM visits
SAMPLE 10000000;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3. SAMPLE k OFFSET m&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;k: 取樣比例&lt;/li&gt;
&lt;li&gt;m: 取樣偏移量 (0~1 之間)&lt;/li&gt;
&lt;li&gt;可用於避免不同查詢 sample 重疊相同資料區塊。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;SELECT *
FROM visits
SAMPLE 0.1 OFFSET 0.5;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;建表時指定 Sampling Key&lt;/h2&gt;
&lt;p&gt;僅 &lt;strong&gt;MergeTree 家族表引擎&lt;/strong&gt; 支援 Sampling，且建表時需指定 Sampling Key。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE user_events
(
    EventDate DateTime,
    UserID UInt64,
    Action String
) ENGINE = MergeTree()
PARTITION BY toYYYYMM(EventDate)
ORDER BY (UserID, EventDate)
SAMPLE BY intHash64(UserID);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;選擇高 Cardinality 且分佈均勻的欄位 (如 UserID) 作為 SAMPLE BY 是關鍵。&lt;/p&gt;
&lt;h2&gt;範例：從 20 秒降到 2 秒&lt;/h2&gt;
&lt;h3&gt;原始查詢 (全表掃描)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;SELECT Action, count() FROM user_events GROUP BY Action;
-- 查詢花了：20 秒
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;抽樣查詢 (SAMPLE 0.1)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;SELECT Action, count() * 10 FROM user_events SAMPLE 0.1 GROUP BY Action;
-- 查詢花了：2 秒
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;相較於全表掃描，抽樣查詢時間縮短 10 倍，且統計結果的誤差率維持在 5% 以內。&lt;/p&gt;
&lt;h2&gt;Sampling 查詢驗證&lt;/h2&gt;
&lt;p&gt;透過 EXPLAIN ESTIMATE 可預估查詢將掃描的資料量。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;EXPLAIN ESTIMATE SELECT * FROM user_events SAMPLE 0.1;
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;parts&lt;/th&gt;
&lt;th&gt;marks&lt;/th&gt;
&lt;th&gt;rows&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;10/10&lt;/td&gt;
&lt;td&gt;100/10&lt;/td&gt;
&lt;td&gt;100,000,000 / 10,000,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;常見問題與誤區&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;問題&lt;/th&gt;
&lt;th&gt;解決建議&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SAMPLE 查詢無效 → 還是全表掃描&lt;/td&gt;
&lt;td&gt;建表時必須指定 SAMPLE BY Key。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;抽樣比例選得太小 → 統計結果誤差大&lt;/td&gt;
&lt;td&gt;建議 SAMPLE 0.05~0.2 之間較佳。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SAMPLE BY 欄位選錯 → 抽樣效果失真&lt;/td&gt;
&lt;td&gt;選擇分佈均勻的欄位 (如 UserID) 來避免偏倚。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;結語&lt;/h2&gt;
&lt;p&gt;Sampling 是 ClickHouse 面對大數據場景中極具威力的查詢加速技術，只需簡單設定 SAMPLE BY 與 SAMPLE 百分比，即可輕鬆取得秒級的近似查詢結果，大幅減輕系統 I/O 與計算壓力。&lt;/p&gt;
&lt;h3&gt;ClickHouse 系列持續更新中:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/what-is-clickhouse/&quot;&gt;ClickHouse 系列：ClickHouse 是什麼？與傳統 OLAP/OLTP 資料庫的差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-column-row-based-storage/&quot;&gt;ClickHouse 系列：ClickHouse 為什麼選擇 Column-based 儲存？講解 Row-based 與 Column-based 的核心差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-engine&quot;&gt;ClickHouse 系列：ClickHouse 儲存引擎 - MergeTree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-compression-skipping-index/&quot;&gt;ClickHouse 系列：壓縮技術與 Data Skipping Indexes 如何大幅加速查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replacingmergetree-deduplication/&quot;&gt;ClickHouse 系列：ReplacingMergeTree 與資料去重機制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-summingmergetree-aggregation/&quot;&gt;ClickHouse 系列：SummingMergeTree 進行資料彙總的應用場景&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-materialized-view/&quot;&gt;ClickHouse 系列：Materialized Views 即時聚合查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-partition-pruning/&quot;&gt;ClickHouse 系列：分區策略與 Partition Pruning 原理解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-primary-sorting-key/&quot;&gt;ClickHouse 系列：Primary Key、Sorting Key 與 Granule 索引運作原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-collapsingmergetree/&quot;&gt;ClickHouse 系列：CollapsingMergeTree 與邏輯刪除的最佳實踐&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-versioned-collapsingmergetree/&quot;&gt;ClickHouse 系列：VersionedCollapsingMergeTree 版本控制與資料衝突解決&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-aggregatingmergetree/&quot;&gt;ClickHouse 系列：AggregatingMergeTree 實時指標統計的進階應用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-distributed-table-architecture/&quot;&gt;ClickHouse 系列：Distributed Table 與分布式查詢架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replication-failover/&quot;&gt;ClickHouse 系列：Replicated Tables 高可用性與零停機升級實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-kafka-data-streaming-pipeline/&quot;&gt;ClickHouse 系列：與 Kafka 整合打造即時 Data Streaming Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-batch-import/&quot;&gt;ClickHouse 系列：批次匯入最佳實踐 (CSV、Parquet、Native Format)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-external-data-integration/&quot;&gt;ClickHouse 系列：ClickHouse 與外部資料源整合（PostgreSQL）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-query-log-explain/&quot;&gt;ClickHouse 系列：如何提升查詢優化？system.query_log 與 EXPLAIN 用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-projections-optimization/&quot;&gt;ClickHouse 系列：Projections 進階查詢加速技術&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-sampling-statistics/&quot;&gt;ClickHouse 系列：Sampling 抽樣查詢與統計技術原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-ttl-storage-management/&quot;&gt;ClickHouse 系列：TTL 資料清理與儲存成本優化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-storage-policies/&quot;&gt;ClickHouse 系列：儲存政策（Storage Policies）與磁碟資源分層策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-schemas-storage-improvement/&quot;&gt;ClickHouse 系列：表格設計與儲存優化細節&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-grafana-dashboard/&quot;&gt;ClickHouse 系列：ClickHouse 系列：整合 Grafana 打造可視化監控&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-select-optimization/&quot;&gt;ClickHouse 系列：查詢優化案例&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-bi-integration/&quot;&gt;ClickHouse 系列：與 BI 工具整合（Power BI）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-cloud-vs-self-host/&quot;&gt;ClickHouse 系列：ClickHouse Cloud 與自建部署的優劣比較&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-security-rbac/&quot;&gt;ClickHouse 系列：資料庫安全性與權限管理（RBAC）實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-operator-kubernates/&quot;&gt;ClickHouse 系列：Kubernetes 部署分散式架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-sourcecode-introduction/&quot;&gt;ClickHouse 系列：從原始碼看 MergeTree 的六大核心機制&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>ClickHouse 系列：Projections 進階查詢加速技術</title><link>https://vicwen.app/posts/clickhouse-projections-optimization/</link><guid isPermaLink="true">https://vicwen.app/posts/clickhouse-projections-optimization/</guid><pubDate>Fri, 22 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在處理大規模資料聚合查詢時，ClickHouse 除了靠 Partition Pruning、Data Skipping Index 來加速查詢（&lt;s&gt;忘了再回去複習&lt;/s&gt;），還有一個極具威力的查詢優化武器 — &lt;strong&gt;Projections&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;Projections 能夠透過預先排序、聚合或重組資料結構，讓查詢執行路徑變得&lt;strong&gt;更短、更快&lt;/strong&gt;。&lt;/p&gt;
&lt;h2&gt;什麼是 Projection？&lt;/h2&gt;
&lt;p&gt;Projection 是一種儲存在 Table Parts 內部的物化結構 (Internal Materialized View)，會針對特定查詢場景提前建立排序或聚合結果。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;屬於 Table 的一部分&lt;/td&gt;
&lt;td&gt;Projection 資料與 Table 共存在同一個資料片段 (Part)。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;查詢時自動命中&lt;/td&gt;
&lt;td&gt;不需修改查詢語法，ClickHouse 會自動選擇最小掃描量的 Projection。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;可建立多個 Projection&lt;/td&gt;
&lt;td&gt;針對不同查詢需求定義不同 Projections。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Projection 的優勢&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;減少掃描資料量&lt;/strong&gt; → 只讀 Projection 部分資料，不需掃描全表。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;加快聚合計算&lt;/strong&gt; → 預先計算好的聚合結果，查詢時直接使用。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;降低 I/O 負載&lt;/strong&gt; → 磁碟讀取量大幅下降，查詢延遲降低。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;範例&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE user_events
(
    EventDate Date,
    UserID UInt64,
    Action String,
    Version UInt32
) ENGINE = MergeTree()
PARTITION BY toYYYYMM(EventDate)
ORDER BY (UserID, EventDate)
SETTINGS index_granularity = 8192
AS
SELECT * FROM source_table;

ALTER TABLE user_events
ADD PROJECTION daily_user_action_counts
(
    SELECT
        EventDate,
        Action,
        count() AS ActionCount
    GROUP BY
        EventDate,
        Action
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;執行 &lt;code&gt;OPTIMIZE TABLE user_events FINAL&lt;/code&gt; 後，ClickHouse 會在背景將 Projection 資料寫入 table parts 中。&lt;/p&gt;
&lt;h2&gt;查詢時自動命中 Projection&lt;/h2&gt;
&lt;p&gt;只要查詢符合 Projection 結構，ClickHouse 會自動使用 Projection 來加速查詢：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT EventDate, Action, count() 
FROM user_events 
WHERE EventDate = &apos;2025-08-10&apos; 
GROUP BY EventDate, Action;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;透過 EXPLAIN PLAN 可以看到查詢使用的是 Projection：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;EXPLAIN PLAN SELECT EventDate, Action, count() FROM user_events WHERE EventDate = &apos;2025-08-10&apos; GROUP BY EventDate, Action;

Projection: daily_user_action_counts
ReadFromMergeTree (using projection)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;實戰：Projections 加速 10 倍的案例&lt;/h2&gt;
&lt;p&gt;假設 user_events 有 10 億筆資料，執行以下查詢：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT EventDate, Action, count() 
FROM user_events 
WHERE EventDate &amp;gt;= &apos;2025-08-01&apos; 
GROUP BY EventDate, Action;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;未使用 Projection&lt;/strong&gt;：需掃描完整 10 億筆資料，耗時 20 秒。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;使用 Projection&lt;/strong&gt;：只需掃描 1 千萬筆 Projection 資料，查詢僅需 &lt;strong&gt;2 秒&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;這種場景特別適合 BI 報表、Dashboard 上的高頻聚合查詢。&lt;/p&gt;
&lt;h2&gt;注意事項與限制&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;限制項目&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Projection 設計需事先規劃&lt;/td&gt;
&lt;td&gt;Projection 一旦定義後，其結構無法修改。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;INSERT 會同時寫入 Projection&lt;/td&gt;
&lt;td&gt;寫入時會增加一些 CPU 運算負擔。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OPTIMIZE TABLE 需執行 Projection Merge&lt;/td&gt;
&lt;td&gt;Projection 資料寫入後，需執行 Optimize 來合併 Projection Parts。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;結語&lt;/h2&gt;
&lt;p&gt;Projections 是 ClickHouse 針對大規模聚合查詢加速的核心武器，透過適當的 Projection 設計，可以讓你的查詢效能瞬間提升數倍。&lt;/p&gt;
&lt;p&gt;在需要報表統計、即時 Dashboard 的場景中，合理運用 Projections，能大幅降低系統負載與查詢延遲，成為大數據分析中的關鍵利器。&lt;/p&gt;
&lt;h3&gt;ClickHouse 系列持續更新中:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/what-is-clickhouse/&quot;&gt;ClickHouse 系列：ClickHouse 是什麼？與傳統 OLAP/OLTP 資料庫的差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-column-row-based-storage/&quot;&gt;ClickHouse 系列：ClickHouse 為什麼選擇 Column-based 儲存？講解 Row-based 與 Column-based 的核心差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-engine&quot;&gt;ClickHouse 系列：ClickHouse 儲存引擎 - MergeTree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-compression-skipping-index/&quot;&gt;ClickHouse 系列：壓縮技術與 Data Skipping Indexes 如何大幅加速查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replacingmergetree-deduplication/&quot;&gt;ClickHouse 系列：ReplacingMergeTree 與資料去重機制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-summingmergetree-aggregation/&quot;&gt;ClickHouse 系列：SummingMergeTree 進行資料彙總的應用場景&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-materialized-view/&quot;&gt;ClickHouse 系列：Materialized Views 即時聚合查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-partition-pruning/&quot;&gt;ClickHouse 系列：分區策略與 Partition Pruning 原理解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-primary-sorting-key/&quot;&gt;ClickHouse 系列：Primary Key、Sorting Key 與 Granule 索引運作原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-collapsingmergetree/&quot;&gt;ClickHouse 系列：CollapsingMergeTree 與邏輯刪除的最佳實踐&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-versioned-collapsingmergetree/&quot;&gt;ClickHouse 系列：VersionedCollapsingMergeTree 版本控制與資料衝突解決&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-aggregatingmergetree/&quot;&gt;ClickHouse 系列：AggregatingMergeTree 實時指標統計的進階應用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-distributed-table-architecture/&quot;&gt;ClickHouse 系列：Distributed Table 與分布式查詢架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replication-failover/&quot;&gt;ClickHouse 系列：Replicated Tables 高可用性與零停機升級實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-kafka-data-streaming-pipeline/&quot;&gt;ClickHouse 系列：與 Kafka 整合打造即時 Data Streaming Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-batch-import/&quot;&gt;ClickHouse 系列：批次匯入最佳實踐 (CSV、Parquet、Native Format)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-external-data-integration/&quot;&gt;ClickHouse 系列：ClickHouse 與外部資料源整合（PostgreSQL）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-query-log-explain/&quot;&gt;ClickHouse 系列：如何提升查詢優化？system.query_log 與 EXPLAIN 用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-projections-optimization/&quot;&gt;ClickHouse 系列：Projections 進階查詢加速技術&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-sampling-statistics/&quot;&gt;ClickHouse 系列：Sampling 抽樣查詢與統計技術原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-ttl-storage-management/&quot;&gt;ClickHouse 系列：TTL 資料清理與儲存成本優化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-storage-policies/&quot;&gt;ClickHouse 系列：儲存政策（Storage Policies）與磁碟資源分層策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-schemas-storage-improvement/&quot;&gt;ClickHouse 系列：表格設計與儲存優化細節&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-grafana-dashboard/&quot;&gt;ClickHouse 系列：ClickHouse 系列：整合 Grafana 打造可視化監控&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-select-optimization/&quot;&gt;ClickHouse 系列：查詢優化案例&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-bi-integration/&quot;&gt;ClickHouse 系列：與 BI 工具整合（Power BI）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-cloud-vs-self-host/&quot;&gt;ClickHouse 系列：ClickHouse Cloud 與自建部署的優劣比較&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-security-rbac/&quot;&gt;ClickHouse 系列：資料庫安全性與權限管理（RBAC）實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-operator-kubernates/&quot;&gt;ClickHouse 系列：Kubernetes 部署分散式架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-sourcecode-introduction/&quot;&gt;ClickHouse 系列：從原始碼看 MergeTree 的六大核心機制&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>ClickHouse 系列：如何提升查詢優化？system.query_log 與 EXPLAIN 用法</title><link>https://vicwen.app/posts/clickhouse-query-log-explain/</link><guid isPermaLink="true">https://vicwen.app/posts/clickhouse-query-log-explain/</guid><pubDate>Thu, 21 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在資料量日益龐大的場景下，&lt;strong&gt;如何優化查詢效能&lt;/strong&gt; 是每一位開發者必須具備的核心能力。本篇將帶你實戰演練 ClickHouse 中兩個查詢優化利器：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;system.query_log&lt;/strong&gt; → 查詢歷史執行效能&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;EXPLAIN&lt;/strong&gt; → 預估查詢路徑與資源使用&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;什麼是 system.query_log？&lt;/h2&gt;
&lt;p&gt;system.query_log 是 ClickHouse 內建的查詢歷史紀錄表，它會紀錄每一筆查詢的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;啟動時間、執行耗時&lt;/li&gt;
&lt;li&gt;資源使用量 (讀取行數、記憶體用量)&lt;/li&gt;
&lt;li&gt;查詢錯誤與異常&lt;/li&gt;
&lt;li&gt;使用者、來源 IP、Client 資訊&lt;/li&gt;
&lt;li&gt;查詢使用的 Storage、Functions、Events&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;:::warning
這邊只紀錄「查詢執行的資訊」，並不會紀錄查詢的結果資料。
:::&lt;/p&gt;
&lt;h3&gt;查詢最近 100 筆 SELECT 查詢執行紀錄&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;SELECT
    query_start_time,
    query_duration_ms,
    read_rows,
    result_rows,
    query
FROM system.query_log
WHERE event_time &amp;gt; now() - INTERVAL 10 MINUTE
AND type = &apos;QueryFinish&apos;
AND query LIKE &apos;SELECT%&apos;
ORDER BY query_start_time DESC
LIMIT 100;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;常見欄位解讀&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;欄位&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;query_start_time&lt;/td&gt;
&lt;td&gt;查詢開始時間&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;query_duration_ms&lt;/td&gt;
&lt;td&gt;查詢耗時 (毫秒)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;read_rows&lt;/td&gt;
&lt;td&gt;查詢過程中讀取的 row 數量&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;result_rows&lt;/td&gt;
&lt;td&gt;查詢結果輸出的 row 數量&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;memory_usage&lt;/td&gt;
&lt;td&gt;查詢執行時的記憶體使用量 (Byte)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;type 值&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;QueryStart&lt;/code&gt; = 1&lt;/td&gt;
&lt;td&gt;查詢開始執行時紀錄&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;QueryFinish&lt;/code&gt; = 2&lt;/td&gt;
&lt;td&gt;查詢成功完成時紀錄&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ExceptionBeforeStart&lt;/code&gt; = 3&lt;/td&gt;
&lt;td&gt;查詢還沒執行就錯誤時紀錄&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ExceptionWhileProcessing&lt;/code&gt;=4&lt;/td&gt;
&lt;td&gt;查詢執行中發生錯誤時紀錄&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;如何找出「慢查詢」？&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;SELECT
    query_start_time,
    query_duration_ms,
    read_rows,
    memory_usage,
    query
FROM system.query_log
WHERE event_time &amp;gt; now() - INTERVAL 1 HOUR
AND type = &apos;QueryFinish&apos;
AND query_duration_ms &amp;gt; 500  -- 大於 500ms
ORDER BY query_duration_ms DESC;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;什麼是 EXPLAIN ？&lt;/h2&gt;
&lt;p&gt;ClickHouse 提供 EXPLAIN 語法，讓你在查詢前預測 &lt;strong&gt;查詢路徑、掃描資料量、JOIN 策略&lt;/strong&gt; 等細節。&lt;/p&gt;
&lt;h3&gt;EXPLAIN SYNTAX：&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;EXPLAIN [AST | SYNTAX | QUERY TREE | PLAN | PIPELINE | ESTIMATE | TABLE OVERRIDE] [settings]
SELECT ...
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;模式&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AST&lt;/td&gt;
&lt;td&gt;顯示查詢的抽象語法樹 (Abstract Syntax Tree)。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SYNTAX&lt;/td&gt;
&lt;td&gt;顯示經過語法優化後的查詢結構。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QUERY TREE&lt;/td&gt;
&lt;td&gt;顯示查詢邏輯樹，反映優化器進行後的結構。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PLAN&lt;/td&gt;
&lt;td&gt;查詢的執行計畫路徑（含掃描表、JOIN 策略等）。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PIPELINE&lt;/td&gt;
&lt;td&gt;查詢的執行階段與並行度資訊 (執行緒、流水線處理器等)。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ESTIMATE&lt;/td&gt;
&lt;td&gt;預估查詢將掃描的資料量（rows、marks、parts）。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TABLE OVERRIDE&lt;/td&gt;
&lt;td&gt;驗證 table function 的 schema 覆寫是否正確。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;EXPLAIN 範例&lt;/h2&gt;
&lt;h3&gt;SYNTAX - 經語法優化後的查詢&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;EXPLAIN SYNTAX SELECT * FROM system.numbers WHERE number &amp;lt; 10;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;SELECT *
FROM system.numbers
WHERE number &amp;lt; 10
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;QUERY TREE — 最終查詢邏輯結構&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;EXPLAIN QUERY TREE SELECT id, value FROM test_table;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;QUERY id: 0
  PROJECTION COLUMNS
    id UInt64
    value String
  JOIN TREE
    TABLE id: 3, table_name: default.test_table
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;這能讓你清楚知道查詢會如何去 Join Tables、哪些欄位會被投影出來。&lt;/p&gt;
&lt;h3&gt;PLAN - 執行計畫步驟&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;EXPLAIN PLAN SELECT sum(number) FROM numbers(1000) GROUP BY number % 4;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;Union
 Expression (Projection)
  Aggregating
   ReadFromStorage (SystemNumbers)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;你可以看到從讀取資料到聚合的整個查詢執行路徑。&lt;/p&gt;
&lt;h3&gt;ESTIMATE — 查詢預估讀取量&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;EXPLAIN ESTIMATE SELECT * FROM large_table WHERE date &amp;gt;= &apos;2024-01-01&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;┌─database─┬─table──────┬─parts─┬─rows───┬─marks─┐
│ default  │ large_table│     2 │ 500000 │    32 │
└──────────┴────────────┴───────┴────────┴───────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;進階：優化一個慢查詢&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;先用 &lt;strong&gt;system.query_log&lt;/strong&gt; 找到最近慢查詢。&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;SELECT
    query_start_time,
    query_duration_ms,
    read_rows,
    read_bytes,
    memory_usage,
    query
FROM system.query_log
WHERE event_time &amp;gt; now() - INTERVAL 1 HOUR
AND type = &apos;QueryFinish&apos;
AND query LIKE &apos;%order_summary%&apos;
ORDER BY query_duration_ms DESC
LIMIT 5;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;query_duration_ms: 4500ms
read_rows: 100000000
query: SELECT region, SUM(amount) FROM order_summary GROUP BY region;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;把該 SQL 用 &lt;strong&gt;EXPLAIN PLAN&lt;/strong&gt; 預測路徑與資料量。&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;EXPLAIN PLAN SELECT region, SUM(amount) FROM order_summary GROUP BY region;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;Expression (Projection)
 Aggregating
  ReadFromMergeTree (order_summary)
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;全表掃描！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;檢查是否：
&lt;ul&gt;
&lt;li&gt;有全表掃描 (資料區塊過大)。&lt;/li&gt;
&lt;li&gt;有不必要的 JOIN → 可否轉 Materialized View。&lt;/li&gt;
&lt;li&gt;缺少 Partition Pruning、索引無法生效。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;查詢條件沒有加上 Partition Key (date)。&lt;/li&gt;
&lt;li&gt;order_summary 按 (date, region) 分區，但查詢沒帶 date 範圍 → 全表掃描。&lt;/li&gt;
&lt;li&gt;可考慮將 region 聚合寫入 Materialized View 預先計算。&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;調整查詢條件（如加 Partition Key 範圍、Data Skipping Index）。&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;SELECT region, SUM(amount)
FROM order_summary
WHERE date = today() - 1
GROUP BY region;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;再次觀察 &lt;strong&gt;query_log → 查詢耗時是否下降&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;SELECT query_duration_ms FROM system.query_log
WHERE query LIKE &apos;%order_summary%&apos;
AND event_time &amp;gt; now() - INTERVAL 5 MINUTE
AND type = &apos;QueryFinish&apos;
ORDER BY query_start_time DESC
LIMIT 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;query_duration_ms: 300ms
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;4500ms&lt;/code&gt; -&amp;gt; &lt;code&gt;300ms&lt;/code&gt; (Nice Try Diddy)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;進階：優化一個全局掃描&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;SELECT user_id, COUNT(*) FROM user_events GROUP BY user_id;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;執行 EXPLAIN PLAN → 確認是否使用了 Primary Key Index。&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;EXPLAIN PLAN SELECT user_id, COUNT(*) FROM user_events GROUP BY user_id;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;Expression (Projection)
 Aggregating
  ReadFromMergeTree (user_events)
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;完全沒有 Index 篩選，直接全表掃描。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;若未使用 → 加入 Partition Pruning 條件。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;user_events 的 Partition Key 是 EventDate，所以我們加上日期範圍：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT user_id, COUNT(*)
FROM user_events
WHERE EventDate &amp;gt;= today() - 7
GROUP BY user_id;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;使用 EXPLAIN ESTIMATE 檢查掃描量是否下降。&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;EXPLAIN ESTIMATE
SELECT user_id, COUNT(*)
FROM user_events
WHERE EventDate &amp;gt;= today() - 7;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;┌─database─┬─table────────┬─parts─┬─rows──────┬─marks─┐
│ default  │ user_events  │     3 │ 10000000  │   800 │
└──────────┴──────────────┴───────┴───────────┴───────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;原本未加條件時掃描了 1 億筆 rows，現在僅掃描 1 千萬筆，資料量明顯下降。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;檢查 PIPELINE 是否有並行處理。&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;EXPLAIN PIPELINE
SELECT user_id, COUNT(*)
FROM user_events
WHERE EventDate &amp;gt;= today() - 7
GROUP BY user_id;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;確認查詢能夠使用多個 AggregatingTransform 節點平行處理。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;再次查詢 system.query_log 驗證查詢耗時是否下降。&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;SELECT query_duration_ms FROM system.query_log
WHERE query LIKE &apos;%user_events%&apos;
AND event_time &amp;gt; now() - INTERVAL 5 MINUTE
AND type = &apos;QueryFinish&apos;
ORDER BY query_start_time DESC
LIMIT 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;query_duration_ms: 600ms
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;結語&lt;/h2&gt;
&lt;p&gt;EXPLAIN 是 ClickHouse 優化查詢性能的核心工具，透過不同模式，你可以：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;了解查詢的執行結構&lt;/li&gt;
&lt;li&gt;預測查詢的資源消耗&lt;/li&gt;
&lt;li&gt;找出瓶頸進行針對性優化&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;將 EXPLAIN 納入你的查詢開發流程，能讓你從「憑經驗寫查詢」升級為「數據驅動的效能優化高手」。&lt;/p&gt;
&lt;h3&gt;ClickHouse 系列持續更新中:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/what-is-clickhouse/&quot;&gt;ClickHouse 系列：ClickHouse 是什麼？與傳統 OLAP/OLTP 資料庫的差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-column-row-based-storage/&quot;&gt;ClickHouse 系列：ClickHouse 為什麼選擇 Column-based 儲存？講解 Row-based 與 Column-based 的核心差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-engine&quot;&gt;ClickHouse 系列：ClickHouse 儲存引擎 - MergeTree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-compression-skipping-index/&quot;&gt;ClickHouse 系列：壓縮技術與 Data Skipping Indexes 如何大幅加速查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replacingmergetree-deduplication/&quot;&gt;ClickHouse 系列：ReplacingMergeTree 與資料去重機制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-summingmergetree-aggregation/&quot;&gt;ClickHouse 系列：SummingMergeTree 進行資料彙總的應用場景&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-materialized-view/&quot;&gt;ClickHouse 系列：Materialized Views 即時聚合查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-partition-pruning/&quot;&gt;ClickHouse 系列：分區策略與 Partition Pruning 原理解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-primary-sorting-key/&quot;&gt;ClickHouse 系列：Primary Key、Sorting Key 與 Granule 索引運作原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-collapsingmergetree/&quot;&gt;ClickHouse 系列：CollapsingMergeTree 與邏輯刪除的最佳實踐&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-versioned-collapsingmergetree/&quot;&gt;ClickHouse 系列：VersionedCollapsingMergeTree 版本控制與資料衝突解決&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-aggregatingmergetree/&quot;&gt;ClickHouse 系列：AggregatingMergeTree 實時指標統計的進階應用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-distributed-table-architecture/&quot;&gt;ClickHouse 系列：Distributed Table 與分布式查詢架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replication-failover/&quot;&gt;ClickHouse 系列：Replicated Tables 高可用性與零停機升級實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-kafka-data-streaming-pipeline/&quot;&gt;ClickHouse 系列：與 Kafka 整合打造即時 Data Streaming Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-batch-import/&quot;&gt;ClickHouse 系列：批次匯入最佳實踐 (CSV、Parquet、Native Format)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-external-data-integration/&quot;&gt;ClickHouse 系列：ClickHouse 與外部資料源整合（PostgreSQL）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-query-log-explain/&quot;&gt;ClickHouse 系列：如何提升查詢優化？system.query_log 與 EXPLAIN 用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-projections-optimization/&quot;&gt;ClickHouse 系列：Projections 進階查詢加速技術&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-sampling-statistics/&quot;&gt;ClickHouse 系列：Sampling 抽樣查詢與統計技術原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-ttl-storage-management/&quot;&gt;ClickHouse 系列：TTL 資料清理與儲存成本優化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-storage-policies/&quot;&gt;ClickHouse 系列：儲存政策（Storage Policies）與磁碟資源分層策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-schemas-storage-improvement/&quot;&gt;ClickHouse 系列：表格設計與儲存優化細節&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-grafana-dashboard/&quot;&gt;ClickHouse 系列：ClickHouse 系列：整合 Grafana 打造可視化監控&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-select-optimization/&quot;&gt;ClickHouse 系列：查詢優化案例&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-bi-integration/&quot;&gt;ClickHouse 系列：與 BI 工具整合（Power BI）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-cloud-vs-self-host/&quot;&gt;ClickHouse 系列：ClickHouse Cloud 與自建部署的優劣比較&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-security-rbac/&quot;&gt;ClickHouse 系列：資料庫安全性與權限管理（RBAC）實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-operator-kubernates/&quot;&gt;ClickHouse 系列：Kubernetes 部署分散式架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-sourcecode-introduction/&quot;&gt;ClickHouse 系列：從原始碼看 MergeTree 的六大核心機制&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>ClickHouse 系列：ClickHouse 與外部資料源整合（PostgreSQL）</title><link>https://vicwen.app/posts/clickhouse-external-data-integration/</link><guid isPermaLink="true">https://vicwen.app/posts/clickhouse-external-data-integration/</guid><pubDate>Wed, 20 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在實際數據平台架構中，ClickHouse 通常不是唯一的資料庫，而是與其他資料源（如 MySQL、PostgreSQL、S3、Kafka 等）整合，扮演 &lt;strong&gt;高效查詢與分析層&lt;/strong&gt; 的角色。&lt;/p&gt;
&lt;p&gt;本篇將示範如何透過 &lt;strong&gt;PostgreSQL Table Engine&lt;/strong&gt; 和 &lt;strong&gt;MaterializedPostgreSQL Database Engine (experimental)&lt;/strong&gt;，讓 ClickHouse 直接查詢 PostgreSQL 資料，實現跨庫即時分析。&lt;/p&gt;
&lt;h2&gt;為什麼要整合 PostgreSQL？&lt;/h2&gt;
&lt;p&gt;在許多應用場景中，PostgreSQL 作為 &lt;strong&gt;OLTP 系統&lt;/strong&gt; 儲存業務資料（如交易、用戶、訂單），但在報表分析時遇到以下挑戰：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;OLTP 查詢性能無法滿足大量聚合分析&lt;/li&gt;
&lt;li&gt;避免 ETL 搬運延遲帶來的資料不一致&lt;/li&gt;
&lt;li&gt;不想複製全量資料，只需要即時查詢部分資料&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ClickHouse 的 &lt;strong&gt;PostgreSQL Table Engine&lt;/strong&gt; 可直接連線 PostgreSQL，並以類似「外部表」的方式查詢資料，適合快速整合多方資料源。&lt;/p&gt;
&lt;h2&gt;PostgreSQL Table Engine — 即時雙向查詢與插入&lt;/h2&gt;
&lt;h3&gt;適用情境&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;不需全量同步，只想即時查詢 PostgreSQL 資料&lt;/li&gt;
&lt;li&gt;需要在 ClickHouse 直接插入資料回 PostgreSQL&lt;/li&gt;
&lt;li&gt;資料量相對較小、即時性需求高&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;PostgreSQL 端設定&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;允許網路連線&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# postgresql.conf
listen_addresses = &apos;*&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;建立使用者&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE ROLE clickhouse_user SUPERUSER LOGIN PASSWORD &apos;ClickHouse_123&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;建立資料庫與表&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE DATABASE db_in_psg;

CREATE TABLE table1 (
    id integer primary key,
    column1 varchar(10)
);

INSERT INTO table1 VALUES (1, &apos;abc&apos;), (2, &apos;def&apos;);
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;設定連線權限&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# pg_hba.conf
host    db_in_psg  clickhouse_user  192.168.1.0/24  password
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;重新載入設定&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pg_ctl reload
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h3&gt;ClickHouse 端設定&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;建立資料庫&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE DATABASE db_in_ch;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;建立連線表&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE db_in_ch.table1
(
    id UInt64,
    column1 String
)
ENGINE = PostgreSQL(
    &apos;postgres-host.domain.com:5432&apos;,
    &apos;db_in_psg&apos;,
    &apos;table1&apos;,
    &apos;clickhouse_user&apos;,
    &apos;ClickHouse_123&apos;
);
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;測試查詢&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT * FROM db_in_ch.table1;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;雙向測試&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在 PostgreSQL 新增資料，ClickHouse 查得到&lt;/li&gt;
&lt;li&gt;在 ClickHouse 新增資料，PostgreSQL 查得到&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;MaterializedPostgreSQL Database Engine — 持續資料同步（CDC）&lt;/h2&gt;
&lt;h3&gt;適用情境&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;需要將 PostgreSQL 整個資料庫或多個表持續同步到 ClickHouse&lt;/li&gt;
&lt;li&gt;資料更新頻率高&lt;/li&gt;
&lt;li&gt;適合報表與即時分析&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;注意事項&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;實驗功能&lt;/strong&gt;，需啟用設定&lt;/li&gt;
&lt;li&gt;不支援 ClickHouse 直接修改同步表（避免與 CDC 衝突）&lt;/li&gt;
&lt;li&gt;適合用於 &lt;strong&gt;只讀分析&lt;/strong&gt; 場景&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;PostgreSQL 端設定&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;開啟複製功能&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# postgresql.conf
listen_addresses = &apos;*&apos;
max_replication_slots = 10
wal_level = logical
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;建立使用者與資料庫&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE ROLE clickhouse_user SUPERUSER LOGIN PASSWORD &apos;ClickHouse_123&apos;;
CREATE DATABASE db1;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;建立表與資料&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;\connect db1
CREATE TABLE table1 (
    id integer primary key,
    column1 varchar(10)
);
INSERT INTO table1 VALUES (1, &apos;abc&apos;), (2, &apos;def&apos;);
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;設定權限&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# pg_hba.conf
host    db1  clickhouse_user  192.168.1.0/24  password
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h3&gt;ClickHouse 端設定&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;啟用實驗功能&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SET allow_experimental_database_materialized_postgresql=1;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;建立同步資料庫&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE DATABASE db1_postgres
ENGINE = MaterializedPostgreSQL(
    &apos;postgres-host.domain.com:5432&apos;,
    &apos;db1&apos;,
    &apos;clickhouse_user&apos;,
    &apos;ClickHouse_123&apos;
)
SETTINGS materialized_postgresql_tables_list = &apos;table1&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;驗證資料&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT * FROM db1_postgres.table1;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;測試同步&lt;/strong&gt;
在 PostgreSQL 新增資料，ClickHouse 會自動更新。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;選擇策略建議&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;PostgreSQL Table Engine&lt;/th&gt;
&lt;th&gt;MaterializedPostgreSQL&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;存取方式&lt;/td&gt;
&lt;td&gt;即時查詢與寫入&lt;/td&gt;
&lt;td&gt;持續複製（只讀）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;適合資料量&lt;/td&gt;
&lt;td&gt;小批量、查詢即時&lt;/td&gt;
&lt;td&gt;大批量、長期分析&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;延遲&lt;/td&gt;
&lt;td&gt;查詢即時（依 PostgreSQL 響應）&lt;/td&gt;
&lt;td&gt;低延遲（CDC 同步）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;使用限制&lt;/td&gt;
&lt;td&gt;受限於 PostgreSQL 性能&lt;/td&gt;
&lt;td&gt;實驗功能、不可寫入&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;運作機制與限制&lt;/h2&gt;
&lt;h3&gt;優點&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;即時查詢 PostgreSQL，不需先 ETL&lt;/li&gt;
&lt;li&gt;可與 ClickHouse 原生表 Join&lt;/li&gt;
&lt;li&gt;適合低延遲資料整合需求&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;限制&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;查詢效能受限於 PostgreSQL 回應速度&lt;/li&gt;
&lt;li&gt;大量資料掃描時延遲較高&lt;/li&gt;
&lt;li&gt;適合即時查詢小批量資料，不適合全量大表分析
（建議使用 &lt;code&gt;clickhouse-copier&lt;/code&gt; 或 ETL 工具將歷史資料導入 ClickHouse）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;總結&lt;/h2&gt;
&lt;p&gt;透過 PostgreSQL Table Engine，ClickHouse 可以直接訪問 PostgreSQL 的即時資料，實現跨系統分析，特別適合混合查詢與即時 BI 報表需求。&lt;/p&gt;
&lt;p&gt;在實務中，建議：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;大表做 ETL 導入 ClickHouse&lt;/li&gt;
&lt;li&gt;小表 / 最新資料透過外部表查詢&lt;/li&gt;
&lt;li&gt;結合 Materialized View 進行即時彙總&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;ClickHouse 系列持續更新中:&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/what-is-clickhouse/&quot;&gt;ClickHouse 系列：ClickHouse 是什麼？與傳統 OLAP/OLTP 資料庫的差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-column-row-based-storage/&quot;&gt;ClickHouse 系列：ClickHouse 為什麼選擇 Column-based 儲存？講解 Row-based 與 Column-based 的核心差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-engine&quot;&gt;ClickHouse 系列：ClickHouse 儲存引擎 - MergeTree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-compression-skipping-index/&quot;&gt;ClickHouse 系列：壓縮技術與 Data Skipping Indexes 如何大幅加速查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replacingmergetree-deduplication/&quot;&gt;ClickHouse 系列：ReplacingMergeTree 與資料去重機制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-summingmergetree-aggregation/&quot;&gt;ClickHouse 系列：SummingMergeTree 進行資料彙總的應用場景&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-materialized-view/&quot;&gt;ClickHouse 系列：Materialized Views 即時聚合查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-partition-pruning/&quot;&gt;ClickHouse 系列：分區策略與 Partition Pruning 原理解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-primary-sorting-key/&quot;&gt;ClickHouse 系列：Primary Key、Sorting Key 與 Granule 索引運作原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-collapsingmergetree/&quot;&gt;ClickHouse 系列：CollapsingMergeTree 與邏輯刪除的最佳實踐&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-versioned-collapsingmergetree/&quot;&gt;ClickHouse 系列：VersionedCollapsingMergeTree 版本控制與資料衝突解決&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-aggregatingmergetree/&quot;&gt;ClickHouse 系列：AggregatingMergeTree 實時指標統計的進階應用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-distributed-table-architecture/&quot;&gt;ClickHouse 系列：Distributed Table 與分布式查詢架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replication-failover/&quot;&gt;ClickHouse 系列：Replicated Tables 高可用性與零停機升級實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-kafka-data-streaming-pipeline/&quot;&gt;ClickHouse 系列：與 Kafka 整合打造即時 Data Streaming Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-batch-import/&quot;&gt;ClickHouse 系列：批次匯入最佳實踐 (CSV、Parquet、Native Format)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-external-data-integration/&quot;&gt;ClickHouse 系列：ClickHouse 與外部資料源整合（PostgreSQL）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-query-log-explain/&quot;&gt;ClickHouse 系列：如何提升查詢優化？system.query_log 與 EXPLAIN 用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-projections-optimization/&quot;&gt;ClickHouse 系列：Projections 進階查詢加速技術&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-sampling-statistics/&quot;&gt;ClickHouse 系列：Sampling 抽樣查詢與統計技術原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-ttl-storage-management/&quot;&gt;ClickHouse 系列：TTL 資料清理與儲存成本優化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-storage-policies/&quot;&gt;ClickHouse 系列：儲存政策（Storage Policies）與磁碟資源分層策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-schemas-storage-improvement/&quot;&gt;ClickHouse 系列：表格設計與儲存優化細節&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-grafana-dashboard/&quot;&gt;ClickHouse 系列：ClickHouse 系列：整合 Grafana 打造可視化監控&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-select-optimization/&quot;&gt;ClickHouse 系列：查詢優化案例&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-bi-integration/&quot;&gt;ClickHouse 系列：與 BI 工具整合（Power BI）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-cloud-vs-self-host/&quot;&gt;ClickHouse 系列：ClickHouse Cloud 與自建部署的優劣比較&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-security-rbac/&quot;&gt;ClickHouse 系列：資料庫安全性與權限管理（RBAC）實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-operator-kubernates/&quot;&gt;ClickHouse 系列：Kubernetes 部署分散式架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-sourcecode-introduction/&quot;&gt;ClickHouse 系列：從原始碼看 MergeTree 的六大核心機制&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>ClickHouse 系列：批次匯入最佳實踐 (CSV、Parquet、Native Format)</title><link>https://vicwen.app/posts/clickhouse-batch-import/</link><guid isPermaLink="true">https://vicwen.app/posts/clickhouse-batch-import/</guid><pubDate>Tue, 19 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在實務的資料分析與數倉場景中，批次匯入（Batch Import）是 ClickHouse 最常見的資料導入方式之一。
根據資料量、來源與格式的不同，選擇合適的匯入方法與檔案格式，能大幅提升匯入速度並降低資源消耗。&lt;/p&gt;
&lt;p&gt;本篇將介紹三種常見格式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CSV（通用文字格式）&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Parquet（列式壓縮格式）&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ClickHouse Native Format（原生二進位格式）&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;匯入基本流程&lt;/h2&gt;
&lt;p&gt;在 ClickHouse 中，批次匯入最常透過以下方式進行：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;clickhouse-client&lt;/strong&gt; 命令行匯入（我是只有除錯用過啦...）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HTTP 接口&lt;/strong&gt; (&lt;code&gt;INSERT INTO ... FORMAT&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;外部工具&lt;/strong&gt;（如 &lt;code&gt;clickhouse-local&lt;/code&gt;、ETL 工具、Python SDK）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;無論使用哪種方式，都必須：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;先建立目標表（Schema 必須與檔案結構一致）&lt;/li&gt;
&lt;li&gt;使用適合的表引擎（MergeTree 系列最常用）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;匯入 CSV 格式&lt;/h2&gt;
&lt;h3&gt;適用場景&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;來源系統匯出為純文字檔&lt;/li&gt;
&lt;li&gt;跨平台通用、容易查看與修改&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不追求極致效能&lt;/strong&gt;，但需要易於整合&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;範例&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE events_csv
(
    event_date Date,
    user_id UInt64,
    action String
) ENGINE = MergeTree()
ORDER BY (event_date, user_id);
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;clickhouse-client --query=&quot;INSERT INTO events_csv FORMAT CSV&quot; &amp;lt; events.csv
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;性能建議&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;加上 &lt;code&gt;--max_insert_block_size=1000000&lt;/code&gt; 增加批次寫入大小&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;對於大檔案，可搭配 &lt;code&gt;gzip&lt;/code&gt; 壓縮：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;zcat events.csv.gz | clickhouse-client --query=&quot;INSERT INTO events_csv FORMAT CSV&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;匯入 Parquet 格式&lt;/h2&gt;
&lt;h3&gt;適用場景&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;上游系統（Spark、Flink、Hive）已輸出列式格式&lt;/li&gt;
&lt;li&gt;大型批量資料（GB～TB 級別）&lt;/li&gt;
&lt;li&gt;希望保留類型資訊與壓縮率&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;範例&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE events_parquet
(
    event_date Date,
    user_id UInt64,
    action String
) ENGINE = MergeTree()
ORDER BY (event_date, user_id);
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;clickhouse-client --query=&quot;INSERT INTO events_parquet FORMAT Parquet&quot; &amp;lt; events.parquet
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;性能建議&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Parquet 已壓縮，匯入時 CPU 壓力較高但 I/O 成本低&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;避免匯入過多小檔案（可先合併再匯入）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;若檔案在雲端（S3），可直接用 &lt;code&gt;s3()&lt;/code&gt; 函數載入：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;INSERT INTO events_parquet
SELECT * FROM s3(&apos;https://bucket/file.parquet&apos;, &apos;AWS_KEY&apos;, &apos;AWS_SECRET&apos;, &apos;Parquet&apos;);
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;匯入 ClickHouse Native Format&lt;/h2&gt;
&lt;h3&gt;適用場景&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;ClickHouse 與 ClickHouse 之間的高速傳輸&lt;/li&gt;
&lt;li&gt;追求最高效能（無需格式解析）&lt;/li&gt;
&lt;li&gt;適合資料備份與還原&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;範例&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;clickhouse-client --query=&quot;SELECT * FROM events_csv FORMAT Native&quot; &amp;gt; events.native
clickhouse-client --query=&quot;INSERT INTO events_native FORMAT Native&quot; &amp;lt; events.native
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;性能建議&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;比 CSV/Parquet 更快，適合機器間資料搬遷&lt;/li&gt;
&lt;li&gt;不適用於多資料源系統（僅限 ClickHouse）&lt;/li&gt;
&lt;li&gt;可搭配 &lt;code&gt;clickhouse-backup&lt;/code&gt; 工具全庫搬遷&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;效能最佳化策略&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;策略&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;大批量一次性匯入&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;避免多次小批量 INSERT，減少寫入壓力&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;排序鍵對齊&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;匯入資料順序與 &lt;code&gt;ORDER BY&lt;/code&gt; 鍵一致，可降低 Merge 開銷&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;關閉索引檢查&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;批次匯入前可暫時停用部分檢查（如 &lt;code&gt;constraints&lt;/code&gt;）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;分區分批匯入&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;按日期或業務鍵切分檔案，平行匯入&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;匯入方式比較&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;格式&lt;/th&gt;
&lt;th&gt;優點&lt;/th&gt;
&lt;th&gt;缺點&lt;/th&gt;
&lt;th&gt;適用場景&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CSV&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;通用、易讀、易產生&lt;/td&gt;
&lt;td&gt;體積大、解析慢&lt;/td&gt;
&lt;td&gt;系統間交換、小規模匯入&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Parquet&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;壓縮率高、列式存儲&lt;/td&gt;
&lt;td&gt;解析較耗 CPU&lt;/td&gt;
&lt;td&gt;大資料批量匯入&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Native&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;匯入最快、無解析&lt;/td&gt;
&lt;td&gt;僅 ClickHouse 可用&lt;/td&gt;
&lt;td&gt;備份還原、集群遷移&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;總結&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;小量資料&lt;/strong&gt; → CSV，方便整合與檢查&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;大資料批次匯入&lt;/strong&gt; → Parquet，減少 I/O 與存儲空間&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ClickHouse 內部傳輸&lt;/strong&gt; → Native，最高效能&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;若批次匯入頻繁，可以搭配 &lt;strong&gt;分區策略（Partitioning）&lt;/strong&gt; 與 &lt;strong&gt;分批併發匯入&lt;/strong&gt;，進一步縮短導入時間。&lt;/p&gt;
&lt;h3&gt;ClickHouse 系列持續更新中:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/what-is-clickhouse/&quot;&gt;ClickHouse 系列：ClickHouse 是什麼？與傳統 OLAP/OLTP 資料庫的差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-column-row-based-storage/&quot;&gt;ClickHouse 系列：ClickHouse 為什麼選擇 Column-based 儲存？講解 Row-based 與 Column-based 的核心差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-engine&quot;&gt;ClickHouse 系列：ClickHouse 儲存引擎 - MergeTree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-compression-skipping-index/&quot;&gt;ClickHouse 系列：壓縮技術與 Data Skipping Indexes 如何大幅加速查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replacingmergetree-deduplication/&quot;&gt;ClickHouse 系列：ReplacingMergeTree 與資料去重機制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-summingmergetree-aggregation/&quot;&gt;ClickHouse 系列：SummingMergeTree 進行資料彙總的應用場景&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-materialized-view/&quot;&gt;ClickHouse 系列：Materialized Views 即時聚合查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-partition-pruning/&quot;&gt;ClickHouse 系列：分區策略與 Partition Pruning 原理解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-primary-sorting-key/&quot;&gt;ClickHouse 系列：Primary Key、Sorting Key 與 Granule 索引運作原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-collapsingmergetree/&quot;&gt;ClickHouse 系列：CollapsingMergeTree 與邏輯刪除的最佳實踐&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-versioned-collapsingmergetree/&quot;&gt;ClickHouse 系列：VersionedCollapsingMergeTree 版本控制與資料衝突解決&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-aggregatingmergetree/&quot;&gt;ClickHouse 系列：AggregatingMergeTree 實時指標統計的進階應用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-distributed-table-architecture/&quot;&gt;ClickHouse 系列：Distributed Table 與分布式查詢架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replication-failover/&quot;&gt;ClickHouse 系列：Replicated Tables 高可用性與零停機升級實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-kafka-data-streaming-pipeline/&quot;&gt;ClickHouse 系列：與 Kafka 整合打造即時 Data Streaming Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-batch-import/&quot;&gt;ClickHouse 系列：批次匯入最佳實踐 (CSV、Parquet、Native Format)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-external-data-integration/&quot;&gt;ClickHouse 系列：ClickHouse 與外部資料源整合（PostgreSQL）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-query-log-explain/&quot;&gt;ClickHouse 系列：如何提升查詢優化？system.query_log 與 EXPLAIN 用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-projections-optimization/&quot;&gt;ClickHouse 系列：Projections 進階查詢加速技術&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-sampling-statistics/&quot;&gt;ClickHouse 系列：Sampling 抽樣查詢與統計技術原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-ttl-storage-management/&quot;&gt;ClickHouse 系列：TTL 資料清理與儲存成本優化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-storage-policies/&quot;&gt;ClickHouse 系列：儲存政策（Storage Policies）與磁碟資源分層策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-schemas-storage-improvement/&quot;&gt;ClickHouse 系列：表格設計與儲存優化細節&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-grafana-dashboard/&quot;&gt;ClickHouse 系列：ClickHouse 系列：整合 Grafana 打造可視化監控&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-select-optimization/&quot;&gt;ClickHouse 系列：查詢優化案例&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-bi-integration/&quot;&gt;ClickHouse 系列：與 BI 工具整合（Power BI）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-cloud-vs-self-host/&quot;&gt;ClickHouse 系列：ClickHouse Cloud 與自建部署的優劣比較&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-security-rbac/&quot;&gt;ClickHouse 系列：資料庫安全性與權限管理（RBAC）實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-operator-kubernates/&quot;&gt;ClickHouse 系列：Kubernetes 部署分散式架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-sourcecode-introduction/&quot;&gt;ClickHouse 系列：從原始碼看 MergeTree 的六大核心機制&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>ClickHouse 系列：與 Kafka 整合打造即時 Data Streaming Pipeline</title><link>https://vicwen.app/posts/clickhouse-kafka-data-streaming-pipeline/</link><guid isPermaLink="true">https://vicwen.app/posts/clickhouse-kafka-data-streaming-pipeline/</guid><pubDate>Mon, 18 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在大規模資料場景下，企業越來越需要能夠 &lt;strong&gt;實時處理與分析 Data Streaming&lt;/strong&gt; 的技術架構。
而 ClickHouse 天生與 Kafka 的整合，提供了一條高效能的 &lt;strong&gt;Event Streaming → Real-Time Analytics&lt;/strong&gt; Data Pipeline，讓資料從產生到分析僅需「秒級延遲」。&lt;/p&gt;
&lt;p&gt;今天我們會透過專案帶各位了解 Kafka + ClickHouse 的即時 Data Streaming Pipeline，各位可以先將 &lt;a href=&quot;https://github.com/viiccwen/kafka-clickhouse-data-streaming-pipeline&quot;&gt;Repository Clone&lt;/a&gt; 下來，接下來我們都會用檔案內的內容來教學。&lt;/p&gt;
&lt;h2&gt;架構概念：Kafka + ClickHouse Data Streaming Pipeline&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;流程階段&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Producers&lt;/td&gt;
&lt;td&gt;產生 Data Streaming 的上游系統（如 Web 行為、IoT、APM 等）。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka Topics&lt;/td&gt;
&lt;td&gt;接收 Data Streaming 的 Message Queue，具備高吞吐、可重播特性。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka Connect / ClickHouse Kafka Engine&lt;/td&gt;
&lt;td&gt;負責將 Kafka Topic 資料持續寫入 ClickHouse。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ClickHouse Tables&lt;/td&gt;
&lt;td&gt;儲存與實時分析，可搭配 Materialized View、Partitioning 設計加速查詢。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;ClickHouse 與 Kafka 整合方式&lt;/h2&gt;
&lt;h3&gt;方式一：Kafka Engine (ClickHouse 原生)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;ClickHouse 內建支援 Kafka Engine，可將 Kafka Topic 當作虛擬表來查詢。&lt;/li&gt;
&lt;li&gt;適合 &lt;strong&gt;低延遲場景&lt;/strong&gt;，資料可以邊讀邊寫入 ClickHouse MergeTree 表格。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;方式二：Kafka Connect + ClickHouse Sink Connector&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;透過 Kafka Connect 架設 ETL Pipeline，使用 ClickHouse Sink Connector 自動將資料流寫入 ClickHouse。&lt;/li&gt;
&lt;li&gt;適合 &lt;strong&gt;Data Streaming轉管道標準化需求&lt;/strong&gt; (與其他資料平台共用 Kafka Connect 架構)。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;語法 &amp;amp; 範例 &amp;amp; 參數定義&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
    name1 [type1] [ALIAS expr1],
    name2 [type2] [ALIAS expr2],
    ...
) ENGINE = Kafka()
SETTINGS
    kafka_broker_list = &apos;host:port&apos;,
    kafka_topic_list = &apos;topic1,topic2,...&apos;,
    kafka_group_name = &apos;group_name&apos;,
    kafka_format = &apos;data_format&apos;[,]
    [kafka_security_protocol = &apos;&apos;,]
    [kafka_sasl_mechanism = &apos;&apos;,]
    [kafka_sasl_username = &apos;&apos;,]
    [kafka_sasl_password = &apos;&apos;,]
    [kafka_schema = &apos;&apos;,]
    [kafka_num_consumers = N,]
    [kafka_max_block_size = 0,]
    [kafka_skip_broken_messages = N,]
    [kafka_commit_every_batch = 0,]
    [kafka_client_id = &apos;&apos;,]
    [kafka_poll_timeout_ms = 0,]
    [kafka_poll_max_batch_size = 0,]
    [kafka_flush_interval_ms = 0,]
    [kafka_thread_per_consumer = 0,]
    [kafka_handle_error_mode = &apos;default&apos;,]
    [kafka_commit_on_select = false,]
    [kafka_max_rows_per_message = 1];
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;必要參數&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;參數名&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;kafka_broker_list&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Kafka Broker 位址與 Port，支援多節點（逗號分隔）。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;kafka_topic_list&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;監聽的 Kafka Topic 名稱，支援多個 Topic（逗號分隔）。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;kafka_group_name&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Consumer Group 名稱，ClickHouse 會以這個 Group 協調消費者進度。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;kafka_format&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;資料格式，如 JSONEachRow、CSV、Avro、Protobuf 等。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;安全性認證設定&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;參數名&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;kafka_security_protocol&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;設定連線安全協定 (如 PLAINTEXT, SSL, SASL_SSL 等)。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;kafka_sasl_mechanism&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SASL 認證方式 (如 PLAIN, SCRAM-SHA-256, SCRAM-SHA-512)。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;kafka_sasl_username&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SASL 認證用戶名稱。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;kafka_sasl_password&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SASL 認證密碼。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;kafka_schema&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;若使用 Avro/Protobuf/Cap’n Proto 格式時，需指定 Schema 檔案路徑。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;效能與吞吐量控制&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;參數名&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;kafka_num_consumers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;這張表會啟用的 Consumer 數量。建議設定不超過 Topic Partition 數量與伺服器 CPU 核心數。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;kafka_max_block_size&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;每次 Poll Kafka 拉取的最大訊息數量 (預設為 ClickHouse max_insert_block_size)。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;kafka_poll_max_batch_size&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;每次 Kafka poll 最大訊息批次量。與 kafka_max_block_size 類似，雙方限制取較小者。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;kafka_poll_timeout_ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Kafka poll 的超時時間。預設為 stream_poll_timeout_ms。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;kafka_flush_interval_ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Flush 資料到 ClickHouse 的間隔時間 (毫秒)。預設為 stream_flush_interval_ms。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;kafka_thread_per_consumer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;設為 1 時，會為每個 Consumer 啟動獨立執行緒，並行寫入，適合多 Partition 場景。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;容錯與錯誤處理&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;參數名&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;kafka_skip_broken_messages&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;允許每個批次 (Block) 最多跳過 N 筆無法解析的錯誤訊息 (格式錯誤等)。預設為 0。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;kafka_handle_error_mode&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;錯誤處理模式：default (直接拋錯)、stream (錯誤記錄在虛擬欄位 _error)、dead_letter_queue (寫入 system.dead_letter_queue)。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;kafka_commit_every_batch&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;每處理完一個 Batch 就 Commit Offset，預設為 0 (整個 Block 完成才 Commit)。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;kafka_commit_on_select&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;是否在 SELECT 查詢時 Commit Offset，預設為 false。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;資料處理細節&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;參數名&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;kafka_max_rows_per_message&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;針對 row-based 格式，每個 Kafka 訊息最大可包含幾筆資料。預設為 1。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;一些實務上的設定&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;場景&lt;/th&gt;
&lt;th&gt;推薦設定&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;高吞吐量 (大量 Partition, 高頻率)&lt;/td&gt;
&lt;td&gt;kafka_num_consumers = Partition 數，kafka_thread_per_consumer = 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;敏感錯誤容忍 (格式錯誤需記錄)&lt;/td&gt;
&lt;td&gt;kafka_handle_error_mode = &apos;stream&apos;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;大批次穩定寫入&lt;/td&gt;
&lt;td&gt;kafka_max_block_size 與 kafka_poll_max_batch_size 設為 10萬或更多&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;跨 DC 資料同步&lt;/td&gt;
&lt;td&gt;適當延長 kafka_poll_timeout_ms 與 kafka_flush_interval_ms，確保網路環境適應性&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;實作環節&lt;/h2&gt;
&lt;p&gt;該專案已經使用 Docker Compose 將所有服務和設定都處理好了，各位可以簡單使用。&lt;/p&gt;
&lt;p&gt;首先我們看到 &lt;code&gt;create_tables.sql&lt;/code&gt;，這是我們建立 Kafka + ClickHouse 的關鍵。&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;create_tables.sql&lt;/code&gt;&lt;/h3&gt;
&lt;h4&gt;1. 資料表清理&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// create_tables.sql

-- ClickHouse Tables Setup
DROP TABLE IF EXISTS default.user_events;
DROP TABLE IF EXISTS default.kafka_user_events;
DROP TABLE IF EXISTS default.kafka_to_events_mv;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2. 建立主資料表 &lt;code&gt;user_events&lt;/code&gt;&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// create_tables.sql
-- Main Events Table
CREATE TABLE IF NOT EXISTS default.user_events
(
    UserID UInt64,
    Action String,
    EventDate DateTime,
) ENGINE = MergeTree()
PARTITION BY toYYYYMM(EventDate)
ORDER BY (UserID, EventDate);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3. 建立 Kafka 接收表 &lt;code&gt;kafka_user_events&lt;/code&gt;&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// create_tables.sql
-- Kafka Engine Table
CREATE TABLE IF NOT EXISTS default.kafka_user_events
(
    UserID UInt64,
    Action String,
    EventDate DateTime,
) ENGINE = Kafka()
SETTINGS
    kafka_broker_list = &apos;kafka:29092&apos;,
    kafka_topic_list = &apos;user_events_topic&apos;,
    kafka_group_name = &apos;clickhouse_consumer_v3&apos;,
    kafka_format = &apos;JSONEachRow&apos;,
    kafka_num_consumers = 1,
    kafka_thread_per_consumer = 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;這是一張 Kafka Engine Table，&lt;strong&gt;本身不會儲存資料&lt;/strong&gt;，而是作為 ClickHouse 消費 Kafka 訊息的入口&lt;/p&gt;
&lt;p&gt;:::warning
Kafka 表本身無法直接查詢。你必須透過 Materialized View 將資料寫入實體表才能存取。
:::&lt;/p&gt;
&lt;h4&gt;4. 建立 Materialized View&lt;/h4&gt;
&lt;p&gt;這是整個 Streaming Pipeline 的關鍵橋樑。Materialized View 會監聽 kafka_user_events，並將其每筆資料&lt;strong&gt;自動寫入&lt;/strong&gt;目標表 user_events。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用 &lt;code&gt;TO default.user_events&lt;/code&gt; 表示這是一個「推送型」 MV。&lt;/li&gt;
&lt;li&gt;SELECT 子句決定要寫入的資料欄位，需與目標表 schema 相符。&lt;/li&gt;
&lt;li&gt;不需要手動執行 INSERT，資料會&lt;strong&gt;自動同步&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;// create_tables.sql
-- Materialized View to stream data from Kafka to main table
CREATE MATERIALIZED VIEW IF NOT EXISTS default.kafka_to_events_mv TO default.user_events AS
SELECT 
    UserID,
    Action,
    EventDate,
FROM default.kafka_user_events; 
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;code&gt;kafka_producer.py&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import json
import time
from kafka import KafkaProducer
from datetime import datetime
import random

# Kafka Config
KAFKA_BROKER = &apos;localhost:9092&apos;
TOPIC = &apos;user_events_topic&apos;

# Initialize Kafka Producer
producer = KafkaProducer(
    bootstrap_servers=[KAFKA_BROKER],
    value_serializer=lambda v: json.dumps(v).encode(&apos;utf-8&apos;)
)

# Generate Random Events
def generate_event():
    return {
        &quot;EventDate&quot;: datetime.now().strftime(&apos;%Y-%m-%d %H:%M:%S&apos;),
        &quot;UserID&quot;: random.randint(0, 10),
        &quot;Action&quot;: random.choice([&quot;click&quot;, &quot;view&quot;, &quot;purchase&quot;]),
        &quot;Version&quot;: 1
    }

# Produce Events Continuously
def produce():
    print(&quot;Starting Kafka Producer...&quot;)
    try:
        while True:
            event = generate_event()
            producer.send(TOPIC, value=event)
            producer.flush() # NOTEICED: it&apos;ll be cache default, so we flush it
            print(f&quot;Produced: {event}&quot;)
            time.sleep(1)  # Send 1 message per second (adjust as needed)
    except KeyboardInterrupt:
        print(&quot;Stopped Producer.&quot;)
    finally:
        producer.close()

if __name__ == &quot;__main__&quot;:
    produce()
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;每秒送出一筆事件資料到 Kafka。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;producer.flush()&lt;/code&gt; 確保訊息立即送出（預設會暫存）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;time.sleep(1)&lt;/code&gt; 控制發送頻率，可改成更快或更慢。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;clickhouse_query.py&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import clickhouse_connect

# ClickHouse Config
CLICKHOUSE_HOST = &apos;localhost&apos;
CLICKHOUSE_PORT = 8123

def connect_clickhouse():
    &quot;&quot;&quot;connect to ClickHouse&quot;&quot;&quot;
    try:
        client = clickhouse_connect.get_client(
            host=CLICKHOUSE_HOST,
            port=CLICKHOUSE_PORT,
            username=&apos;default&apos;,
            password=&apos;default&apos;
        )
        print(&quot;connected to ClickHouse!&quot;)
        return client
    except Exception as e:
        print(f&quot;connect to ClickHouse failed: {e}&quot;)
        return None

def check_tables(client):
    &quot;&quot;&quot;check tables&quot;&quot;&quot;
    try:
        result = client.query(&quot;SHOW TABLES&quot;)
        print(&quot;existing tables:&quot;)
        for row in result.result_rows:
            print(f&quot;  - {row[0]}&quot;)
        return True
    except Exception as e:
        print(f&quot;check tables failed: {e}&quot;)
        return False

def query_data(client):
    &quot;&quot;&quot;query data&quot;&quot;&quot;
    try:
        # query total records
        count_result = client.query(&quot;SELECT COUNT(*) FROM default.user_events&quot;)
        total_count = count_result.result_rows[0][0]
        print(f&quot;\ntotal records: {total_count}&quot;)
        
        if total_count &amp;gt; 0:
            # query recent 10 records
            recent_result = client.query(&quot;&quot;&quot;
                SELECT EventDate, UserID, Action 
                FROM default.user_events 
                ORDER BY EventDate DESC 
                LIMIT 10
            &quot;&quot;&quot;)
            
            print(&quot;\nrecent 10 records:&quot;)
            print(&quot;-&quot; * 60)
            for row in recent_result.result_rows:
                print(f&quot;  {row[0]} | UserID: {row[1]} | Action: {row[2]}&quot;)
        
        # group by Action
        action_stats = client.query(&quot;&quot;&quot;
            SELECT Action, COUNT(*) as count 
            FROM default.user_events 
            GROUP BY Action 
            ORDER BY count DESC
        &quot;&quot;&quot;)
        
        print(&quot;\ngroup by Action:&quot;)
        print(&quot;-&quot; * 30)
        for row in action_stats.result_rows:
            print(f&quot;  {row[0]}: {row[1]} records&quot;)
            
    except Exception as e:
        print(f&quot;query data failed: {e}&quot;)

def main():
    print(&quot;ClickHouse data query tool&quot;)
    print(&quot;=&quot; * 40)
    
    client = connect_clickhouse()
    if client:
        check_tables(client)
        query_data(client)
        client.close()

if __name__ == &quot;__main__&quot;:
    main() 
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;預設使用 HTTP port 8123&lt;/li&gt;
&lt;li&gt;用戶名稱與密碼皆為 default，若 ClickHouse 有啟用 RBAC，這邊要對應調整&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;:::note
我們會在後面的文章中講到 RBAC
:::&lt;/p&gt;
&lt;p&gt;接著我們使用 SQL Query 取得被 MV 轉發的資料。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-- 查詢最近 10 筆
SELECT EventDate, UserID, Action 
FROM default.user_events 
ORDER BY EventDate DESC 
LIMIT 10

-- Group by 統計
SELECT Action, COUNT(*) 
FROM default.user_events 
GROUP BY Action 
ORDER BY count DESC
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;結語&lt;/h2&gt;
&lt;p&gt;ClickHouse 與 Kafka 的整合，使我們能夠在毫秒級時間內，將龐大的事件資料流進行儲存、轉換與查詢分析。
透過 Materialized View 與 Kafka Engine，從資料進來到 BI 報表呈現，整個過程都能保持高效能且可擴展的設計。&lt;/p&gt;
&lt;h3&gt;ClickHouse 系列持續更新中:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/what-is-clickhouse/&quot;&gt;ClickHouse 系列：ClickHouse 是什麼？與傳統 OLAP/OLTP 資料庫的差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-column-row-based-storage/&quot;&gt;ClickHouse 系列：ClickHouse 為什麼選擇 Column-based 儲存？講解 Row-based 與 Column-based 的核心差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-engine&quot;&gt;ClickHouse 系列：ClickHouse 儲存引擎 - MergeTree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-compression-skipping-index/&quot;&gt;ClickHouse 系列：壓縮技術與 Data Skipping Indexes 如何大幅加速查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replacingmergetree-deduplication/&quot;&gt;ClickHouse 系列：ReplacingMergeTree 與資料去重機制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-summingmergetree-aggregation/&quot;&gt;ClickHouse 系列：SummingMergeTree 進行資料彙總的應用場景&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-materialized-view/&quot;&gt;ClickHouse 系列：Materialized Views 即時聚合查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-partition-pruning/&quot;&gt;ClickHouse 系列：分區策略與 Partition Pruning 原理解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-primary-sorting-key/&quot;&gt;ClickHouse 系列：Primary Key、Sorting Key 與 Granule 索引運作原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-collapsingmergetree/&quot;&gt;ClickHouse 系列：CollapsingMergeTree 與邏輯刪除的最佳實踐&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-versioned-collapsingmergetree/&quot;&gt;ClickHouse 系列：VersionedCollapsingMergeTree 版本控制與資料衝突解決&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-aggregatingmergetree/&quot;&gt;ClickHouse 系列：AggregatingMergeTree 實時指標統計的進階應用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-distributed-table-architecture/&quot;&gt;ClickHouse 系列：Distributed Table 與分布式查詢架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replication-failover/&quot;&gt;ClickHouse 系列：Replicated Tables 高可用性與零停機升級實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-kafka-data-streaming-pipeline/&quot;&gt;ClickHouse 系列：與 Kafka 整合打造即時 Data Streaming Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-batch-import/&quot;&gt;ClickHouse 系列：批次匯入最佳實踐 (CSV、Parquet、Native Format)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-external-data-integration/&quot;&gt;ClickHouse 系列：ClickHouse 與外部資料源整合（PostgreSQL）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-query-log-explain/&quot;&gt;ClickHouse 系列：如何提升查詢優化？system.query_log 與 EXPLAIN 用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-projections-optimization/&quot;&gt;ClickHouse 系列：Projections 進階查詢加速技術&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-sampling-statistics/&quot;&gt;ClickHouse 系列：Sampling 抽樣查詢與統計技術原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-ttl-storage-management/&quot;&gt;ClickHouse 系列：TTL 資料清理與儲存成本優化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-storage-policies/&quot;&gt;ClickHouse 系列：儲存政策（Storage Policies）與磁碟資源分層策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-schemas-storage-improvement/&quot;&gt;ClickHouse 系列：表格設計與儲存優化細節&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-grafana-dashboard/&quot;&gt;ClickHouse 系列：ClickHouse 系列：整合 Grafana 打造可視化監控&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-select-optimization/&quot;&gt;ClickHouse 系列：查詢優化案例&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-bi-integration/&quot;&gt;ClickHouse 系列：與 BI 工具整合（Power BI）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-cloud-vs-self-host/&quot;&gt;ClickHouse 系列：ClickHouse Cloud 與自建部署的優劣比較&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-security-rbac/&quot;&gt;ClickHouse 系列：資料庫安全性與權限管理（RBAC）實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-operator-kubernates/&quot;&gt;ClickHouse 系列：Kubernetes 部署分散式架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-sourcecode-introduction/&quot;&gt;ClickHouse 系列：從原始碼看 MergeTree 的六大核心機制&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>ClickHouse 系列：Replicated Tables 高可用性與零停機升級實作</title><link>https://vicwen.app/posts/clickhouse-replication-failover/</link><guid isPermaLink="true">https://vicwen.app/posts/clickhouse-replication-failover/</guid><pubDate>Sun, 17 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在實務中，資料庫節點可能因硬體故障、軟體升級或網路問題而離線。如何確保資料不遺失、查詢不中斷，並且能夠在線進行升級與維護，&lt;strong&gt;高可用性 (High Availability, HA)&lt;/strong&gt; 架構成為核心需求。&lt;/p&gt;
&lt;p&gt;ClickHouse 提供了 &lt;strong&gt;Replicated Tables&lt;/strong&gt; 機制，透過 &lt;strong&gt;主從副本同步（Replication）與自動 Failover 機制&lt;/strong&gt;，實現資料一致性與讀取負載平衡，讓叢集具備「零停機升級」的能力。&lt;/p&gt;
&lt;h2&gt;什麼是 Replicated Tables？&lt;/h2&gt;
&lt;p&gt;Replicated Tables 是 ClickHouse 內建的一種資料複製技術，依賴 &lt;strong&gt;ClickHouse Keeper / ZooKeeper&lt;/strong&gt; 作為協調器，實現以下功能：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;資料副本同步 (Replication)&lt;/strong&gt;：將資料自動複製到多個節點（Replica）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;故障容錯 (Failover)&lt;/strong&gt;：當主節點故障時，自動切換至其他 Replica。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;讀取負載平衡 (Load Balancing Reads)&lt;/strong&gt;：查詢時可自動將請求分散到多個 Replica 節點上。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;無鎖定的線上擴容與升級 (Zero Downtime Scaling)&lt;/strong&gt;：允許新增副本節點、替換節點而不影響資料可用性。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Replicated 支援哪些 MergeTree 引擎？&lt;/h2&gt;
&lt;p&gt;大多數的 MergeTree 家庭成員都支援，包含：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ReplicatedMergeTree&lt;/li&gt;
&lt;li&gt;ReplicatedReplacingMergeTree&lt;/li&gt;
&lt;li&gt;ReplicatedSummingMergeTree&lt;/li&gt;
&lt;li&gt;ReplicatedAggregatingMergeTree&lt;/li&gt;
&lt;li&gt;ReplicatedCollapsingMergeTree&lt;/li&gt;
&lt;li&gt;ReplicatedVersionedCollapsingMergeTree&lt;/li&gt;
&lt;li&gt;ReplicatedGraphiteMergeTree&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;這些引擎會在建立表格時透過 &lt;code&gt;Replicated*MergeTree&lt;/code&gt; 來定義，並指定 ZooKeeper/ClickHouse Keeper 路徑與 Replica 名稱來實現資料副本同步。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;基本語法&lt;/h2&gt;
&lt;h3&gt;1. 建立 ReplicatedReplacingMergeTree&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE table_name
(
    EventDate DateTime,
    CounterID UInt32,
    UserID UInt32,
    ver UInt16
)
ENGINE = ReplicatedReplacingMergeTree(
    &apos;/clickhouse/tables/{layer}-{shard}/table_name&apos;,
    &apos;{replica}&apos;,
    ver
)
PARTITION BY toYYYYMM(EventDate)
ORDER BY (CounterID, EventDate, intHash32(UserID))
SAMPLE BY intHash32(UserID);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;參數解釋：&lt;/h3&gt;
&lt;p&gt;上述功能：當同一筆資料出現多個版本時（以 Primary Key 為基準），ClickHouse 會以 ver 欄位數值較大的版本作為最終結果（去重邏輯發生於 Merge 階段）。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;什麼？忘記了，罰你回去看：&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replacingmergetree-deduplication/&quot;&gt;ClickHouse 系列：ReplacingMergeTree 與資料去重機制&lt;/a&gt; 🫵&lt;/p&gt;
&lt;/blockquote&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;參數&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/clickhouse/tables/{layer}-{shard}/table_name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;這是 Zookeeper/ClickHouse Keeper 中的 Metadata 路徑，每個 Replica 會註冊到這個路徑下。 &lt;code&gt;{layer}&lt;/code&gt; 與 &lt;code&gt;{shard}&lt;/code&gt; 會從 macros.xml 讀取。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;{replica}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;代表該節點的 Replica 名稱 (例如 replica1, replica2)，同樣從 macros.xml 讀取。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;2. macros.xml 每個節點需設定 shard 與 replica 名稱&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;yandex&amp;gt;
  &amp;lt;macros&amp;gt;
    &amp;lt;shard&amp;gt;01&amp;lt;/shard&amp;gt;
    &amp;lt;replica&amp;gt;replica1&amp;lt;/replica&amp;gt;
  &amp;lt;/macros&amp;gt;
&amp;lt;/yandex&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;每個 Replica 節點都必須設定對應的 &lt;code&gt;{shard}&lt;/code&gt; 與 &lt;code&gt;{replica}&lt;/code&gt;，以便在 ZooKeeper/Keeper 上正確標註其節點身份。&lt;/p&gt;
&lt;h2&gt;技術原理與注意事項&lt;/h2&gt;
&lt;h3&gt;1. &lt;strong&gt;Replication 是「以資料表為單位」進行，不是以整個伺服器為單位&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;一台 ClickHouse Server 上可以同時存在 &lt;strong&gt;Replicated Tables&lt;/strong&gt; 與 &lt;strong&gt;Non-Replicated Tables&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;複製 (Replication) 與分片 (Sharding) 是兩個獨立的概念，Replication 只影響表格副本同步，不影響資料如何分片。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. &lt;strong&gt;Sharding 與 Replication 的獨立性&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;每個 Shard 之間的資料是獨立的，不會互相複製。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shard 內的副本 (Replica)&lt;/strong&gt; 會透過 ReplicatedMergeTree 系列引擎進行同步。&lt;/li&gt;
&lt;li&gt;分片是「水平切分資料量」，副本是「確保高可用性與故障容錯」。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. &lt;strong&gt;INSERT 與 ALTER 的操作會複製 (同步)&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;INSERT&lt;/strong&gt;：寫入時資料會被同步到同一 Shard 內的所有 Replica 節點。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ALTER&lt;/strong&gt;：如資料表結構變更 (ADD COLUMN 等)，也會將這些變更同步到 Replica。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ALTER 操作支援非阻塞 (non-blocking) 機制&lt;/strong&gt;，能夠在線進行不影響線上查詢。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4. &lt;strong&gt;CREATE / DROP / ATTACH / DETACH / RENAME 等 DDL 操作不會自動複製&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;CREATE TABLE&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在某節點執行 CREATE 時，會在 Keeper/ZooKeeper 註冊為一個新的 Replica。&lt;/li&gt;
&lt;li&gt;若其他節點上已存在該表，則會自動建立一個新的 Replica 參與同步。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;DROP TABLE&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;只會刪除執行該指令的節點上的 Replica，不會影響其他 Replica。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;ATTACH / DETACH TABLE&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;操作的僅是該節點本機上的表，不會影響其他 Replica。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;RENAME TABLE&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;RENAME 只會影響當前節點的表名稱，Replica 之間的表名可以不同（數據仍同步）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;所以 DDL 操作需由開發者自己控制 ON CLUSTER 或手動同步到每個 Replica 節點執行。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;5. &lt;strong&gt;Keeper / ZooKeeper 為 Replica Metadata 協調服務&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Replica 間的同步資訊（例如：目前有哪些 Replica、哪個是 Primary、同步進度、Leader 選舉）都會儲存在 ClickHouse Keeper 或 ZooKeeper 中。&lt;/li&gt;
&lt;li&gt;ClickHouse 官方推薦使用 &lt;strong&gt;ClickHouse Keeper&lt;/strong&gt;（ClickHouse 自家實作的輕量版協調器），相較於 ZooKeeper 更輕量穩定。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;6. &lt;strong&gt;設定 Replication 需要配置 Keeper 區段&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;在 ClickHouse 設定檔中 (config.xml)，需定義 ZooKeeper / Keeper 協調器：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;zookeeper&amp;gt;
    &amp;lt;node&amp;gt;
        &amp;lt;host&amp;gt;zk1&amp;lt;/host&amp;gt;
        &amp;lt;port&amp;gt;2181&amp;lt;/port&amp;gt;
    &amp;lt;/node&amp;gt;
    &amp;lt;node&amp;gt;
        &amp;lt;host&amp;gt;zk2&amp;lt;/host&amp;gt;
        &amp;lt;port&amp;gt;2181&amp;lt;/port&amp;gt;
    &amp;lt;/node&amp;gt;
    &amp;lt;node&amp;gt;
        &amp;lt;host&amp;gt;zk3&amp;lt;/host&amp;gt;
        &amp;lt;port&amp;gt;2181&amp;lt;/port&amp;gt;
    &amp;lt;/node&amp;gt;
&amp;lt;/zookeeper&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;或者如果使用 ClickHouse Keeper：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;keeper_server&amp;gt;
    ...
&amp;lt;/keeper_server&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;零停機升級 (Zero Downtime Upgrade) 流程&lt;/h2&gt;
&lt;h3&gt;升級 Replica 節點步驟：&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;將待升級節點標記為 &lt;strong&gt;僅讀取流量 (停止寫入該節點)&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;停止該 Replica 節點的 ClickHouse 服務。&lt;/li&gt;
&lt;li&gt;升級 ClickHouse 版本，修改配置檔等作業。&lt;/li&gt;
&lt;li&gt;重啟節點後，自動從其他副本同步缺失的資料 Part。&lt;/li&gt;
&lt;li&gt;節點完成升級並重新加入叢集，恢復寫入與查詢。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;完整升級叢集時：&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;依序升級 Replica 節點。&lt;/li&gt;
&lt;li&gt;確保任意時刻至少有一個 Primary 節點與副本處於線上狀態。&lt;/li&gt;
&lt;li&gt;若需要升級 Shard 節點，應先將流量導向其他 Shard 或 Replica。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;故障容錯 (Failover) 行為&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;故障場景&lt;/th&gt;
&lt;th&gt;ClickHouse 行為&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Replica (非主節點) 故障&lt;/td&gt;
&lt;td&gt;該節點不會參與查詢，查詢自動切換至其他 Replica，不影響查詢可用性。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Primary Replica 故障&lt;/td&gt;
&lt;td&gt;其他 Replica 會透過 Keeper 選舉新的 Primary，確保寫入不中斷。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Keeper 故障 (單節點失效)&lt;/td&gt;
&lt;td&gt;若為 Keeper 叢集，會自動選擇 Leader，除非多數節點故障才會影響副本同步。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;設計建議&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;設計策略&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;使用奇數數量的 Keeper 節點 (3 或 5 節點)&lt;/td&gt;
&lt;td&gt;確保故障容忍度達到 n/2，可支撐單點或雙點故障仍維持叢集協調運作。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;分散 Replica 於不同可用區 (AZ)&lt;/td&gt;
&lt;td&gt;提升容災能力，確保單一區域失效不會影響整體叢集可用性。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;設定 insert_quorum=2 以強化寫入一致性&lt;/td&gt;
&lt;td&gt;可設定 Quorum Write 機制，保證至少 N 個副本成功寫入才返回成功。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;使用 Distributed Table + ReplicatedMergeTree&lt;/td&gt;
&lt;td&gt;確保資料分片與副本同步結合，實現橫向擴展與高可用性並存的架構設計。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;ClickHouse Cloud 與自行部署的差異&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;ClickHouse Cloud&lt;/th&gt;
&lt;th&gt;自行部署 ClickHouse&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Replicated Table 建立&lt;/td&gt;
&lt;td&gt;不需手動設定 Zookeeper，雲端服務自動協調。&lt;/td&gt;
&lt;td&gt;需自行架設 ClickHouse Keeper / ZooKeeper。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;副本與分片擴容&lt;/td&gt;
&lt;td&gt;雲端服務可動態擴容，擴容過程對使用者透明。&lt;/td&gt;
&lt;td&gt;自行部署時需手動加入節點並同步資料。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;升級與維運&lt;/td&gt;
&lt;td&gt;由雲端服務自動完成節點升級與容錯。&lt;/td&gt;
&lt;td&gt;需自行規劃升級排程與 Failover 流程。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;更高控制權 (細部參數調校)&lt;/td&gt;
&lt;td&gt;雲端部分功能會被封裝起來，無法自訂複雜參數。&lt;/td&gt;
&lt;td&gt;可完整控制 clusters.xml、macros、Keeper 設定細節。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;進階：運作細節 &amp;amp; 性能影響&lt;/h2&gt;
&lt;h3&gt;1. &lt;strong&gt;ZooKeeper/ClickHouse Keeper 是副本協調的基礎&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;每個 Replicated Table 會在 ZooKeeper/Keeper 上有對應的路徑 (目錄) 來協調副本狀態與同步。&lt;/li&gt;
&lt;li&gt;若 config.xml 未設定 ZooKeeper/Keeper 連線，則無法建立 Replicated Table，現有的 Replicated Table 也只能讀取，無法寫入。&lt;/li&gt;
&lt;li&gt;你可以使用同一個 ZooKeeper 叢集管理多個 Shard 的副本協調，但在大型叢集 (300+ 節點) 仍不建議拆分 ZooKeeper 叢集，因為 ClickHouse 預設的協調設計已能有效運作。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. &lt;strong&gt;Replication 不影響 SELECT 查詢效能&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;讀取查詢 (SELECT)&lt;/strong&gt; 時並不會透過 ZooKeeper 進行協調，因此查詢效能與非複製表無異。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Distributed + Replicated 表查詢時，可透過以下參數控制 Replica 行為：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;參數名&lt;/th&gt;
&lt;th&gt;功能&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;max_replica_delay_for_distributed_queries&lt;/td&gt;
&lt;td&gt;設定允許查詢的 Replica 最多可以落後多久（以秒為單位）。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fallback_to_stale_replicas_for_distributed_queries&lt;/td&gt;
&lt;td&gt;當同步 Replica 不可用時，是否允許回退至資料落後的 Replica 查詢。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. &lt;strong&gt;INSERT 會增加 ZooKeeper 的負擔 (約 10 次 Transaction / Data Parts)&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;每次 INSERT（或每個 Data Parts，預設最大 1,048,576 rows），都會在 ZooKeeper 執行約 10 次 Transaction 來協調副本同步。&lt;/li&gt;
&lt;li&gt;這會讓 INSERT 相較於非複製表有些微延遲，但若遵循 ClickHouse 的建議（每秒不要超過 1 次 INSERT），在 Production Environment 下並不會造成實際問題。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;每秒數百次 INSERTs (以 Data Part 計算) 是實務上能穩定支援的規模。&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;你可能會想說：Vic，每秒不要超過 1 次 INSERT也太弱了吧？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我一開始也是這樣認為，但 ClickHouse 的意思是：「&lt;strong&gt;小批次（小 Data Part）高頻率 INSERT&lt;/strong&gt;」的情況。若每秒進行&lt;strong&gt;大量筆數極少（幾百、幾千行）的 INSERT&lt;/strong&gt;，會導致每次 INSERT 都需要進行 ZooKeeper 協調、Data Part 的寫入與同步，這會嚴重增加系統負載（ZooKeeper 會被灌爆 Bruh）。&lt;/p&gt;
&lt;p&gt;建議還是將資料聚合成「大批次寫入」(例如：1秒寫1次，每次數十萬筆)，降低 Data Part 產生數量，避免 ZooKeeper 壓力。&lt;/p&gt;
&lt;h3&gt;4. &lt;strong&gt;Replication 是非同步且支援 Multi-Master 寫入&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;INSERT 與 ALTER 可發送至任意可用 Replica 節點，資料會寫入本地，然後再同步至其他 Replica。&lt;/li&gt;
&lt;li&gt;同步是非同步進行的，因此資料在其他副本出現會有些微延遲，這取決於網路傳輸時間與同步處理量。&lt;/li&gt;
&lt;li&gt;若某些 Replica 暫時離線，當它們重新上線時會自動將缺失的資料補齊。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5. &lt;strong&gt;複製只同步原始資料塊，後續合併 (Merge) 是各 Replica 獨立執行的&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;INSERT 時僅會傳送壓縮後的 Data Part 到其他 Replica。&lt;/li&gt;
&lt;li&gt;合併 (Merge) 操作會由各 Replica 自行執行，但合併的順序與結果會保持一致（由 Keeper 協調）。&lt;/li&gt;
&lt;li&gt;這種設計大幅降低了網路傳輸量，即使 Replica 分布於跨資料中心 (Cross-DC) 也能高效運作。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;6. &lt;strong&gt;INSERT 預設僅等待一個 Replica 寫入成功 (非 Quorum Write)&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;預設情況下，INSERT 只會等其中一個 Replica 確認寫入成功後就返回成功訊號。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;這意味著若該 Replica 發生永久性故障，該筆資料有遺失風險。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;若需保障寫入至少多個副本成功才能回應，應啟用：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SET insert_quorum = 2;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;這樣只有當至少 2 個 Replica 確認成功寫入才會回傳成功。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;7. &lt;strong&gt;INSERT 是原子性的、具冪等性且支援去重 (Deduplication)&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;每個 Data Part 都是以原子性方式寫入。&lt;/li&gt;
&lt;li&gt;若因網路異常導致客戶端重複發送相同資料，ClickHouse 會自動檢查並去除重複 Data Part（根據資料塊內容與順序）。&lt;/li&gt;
&lt;li&gt;INSERT 操作是冪等的，無論送到哪個 Replica，結果皆一致。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;8. &lt;strong&gt;背景同步與擷取 (Fetch) 任務可透過參數調校&lt;/strong&gt;&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;參數名稱&lt;/th&gt;
&lt;th&gt;功能&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;background_schedule_pool_size&lt;/td&gt;
&lt;td&gt;控制執行背景合併 (Merge)、壓縮 (Mutations) 等任務的執行緒數量。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;background_fetches_pool_size&lt;/td&gt;
&lt;td&gt;控制 Replicated Table 資料擷取 (Fetch) 任務的執行緒數量，需重啟伺服器才會生效。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;實務建議&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;項目&lt;/th&gt;
&lt;th&gt;建議&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;INSERT 頻率&lt;/td&gt;
&lt;td&gt;建議每秒不超過 1 次 INSERT 操作，並將大量資料批次寫入 (Batch Inserts)。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ZooKeeper 負載&lt;/td&gt;
&lt;td&gt;若 INSERT 頻率極高，可考慮將不同 Shard 使用不同 ZooKeeper 叢集。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;跨資料中心同步&lt;/td&gt;
&lt;td&gt;Replicated Tables 特別適合用於跨資料中心的高可用同步場景。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;一致性要求高的寫入&lt;/td&gt;
&lt;td&gt;啟用 &lt;code&gt;insert_quorum&lt;/code&gt; 參數來保障寫入多副本一致性。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;結語&lt;/h2&gt;
&lt;p&gt;透過 ClickHouse Replicated Tables 機制，我們能夠實現資料一致性、查詢負載平衡與真正的零停機升級 (Zero Downtime Upgrade)。
對於處理高頻寫入、大規模查詢且又要求高可用性的數據平台，這是不可或缺的核心架構設計。&lt;/p&gt;
&lt;h3&gt;ClickHouse 系列持續更新中:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/what-is-clickhouse/&quot;&gt;ClickHouse 系列：ClickHouse 是什麼？與傳統 OLAP/OLTP 資料庫的差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-column-row-based-storage/&quot;&gt;ClickHouse 系列：ClickHouse 為什麼選擇 Column-based 儲存？講解 Row-based 與 Column-based 的核心差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-engine&quot;&gt;ClickHouse 系列：ClickHouse 儲存引擎 - MergeTree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-compression-skipping-index/&quot;&gt;ClickHouse 系列：壓縮技術與 Data Skipping Indexes 如何大幅加速查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replacingmergetree-deduplication/&quot;&gt;ClickHouse 系列：ReplacingMergeTree 與資料去重機制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-summingmergetree-aggregation/&quot;&gt;ClickHouse 系列：SummingMergeTree 進行資料彙總的應用場景&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-materialized-view/&quot;&gt;ClickHouse 系列：Materialized Views 即時聚合查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-partition-pruning/&quot;&gt;ClickHouse 系列：分區策略與 Partition Pruning 原理解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-primary-sorting-key/&quot;&gt;ClickHouse 系列：Primary Key、Sorting Key 與 Granule 索引運作原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-collapsingmergetree/&quot;&gt;ClickHouse 系列：CollapsingMergeTree 與邏輯刪除的最佳實踐&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-versioned-collapsingmergetree/&quot;&gt;ClickHouse 系列：VersionedCollapsingMergeTree 版本控制與資料衝突解決&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-aggregatingmergetree/&quot;&gt;ClickHouse 系列：AggregatingMergeTree 實時指標統計的進階應用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-distributed-table-architecture/&quot;&gt;ClickHouse 系列：Distributed Table 與分布式查詢架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replication-failover/&quot;&gt;ClickHouse 系列：Replicated Tables 高可用性與零停機升級實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-kafka-data-streaming-pipeline/&quot;&gt;ClickHouse 系列：與 Kafka 整合打造即時 Data Streaming Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-batch-import/&quot;&gt;ClickHouse 系列：批次匯入最佳實踐 (CSV、Parquet、Native Format)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-external-data-integration/&quot;&gt;ClickHouse 系列：ClickHouse 與外部資料源整合（PostgreSQL）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-query-log-explain/&quot;&gt;ClickHouse 系列：如何提升查詢優化？system.query_log 與 EXPLAIN 用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-projections-optimization/&quot;&gt;ClickHouse 系列：Projections 進階查詢加速技術&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-sampling-statistics/&quot;&gt;ClickHouse 系列：Sampling 抽樣查詢與統計技術原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-ttl-storage-management/&quot;&gt;ClickHouse 系列：TTL 資料清理與儲存成本優化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-storage-policies/&quot;&gt;ClickHouse 系列：儲存政策（Storage Policies）與磁碟資源分層策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-schemas-storage-improvement/&quot;&gt;ClickHouse 系列：表格設計與儲存優化細節&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-grafana-dashboard/&quot;&gt;ClickHouse 系列：ClickHouse 系列：整合 Grafana 打造可視化監控&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-select-optimization/&quot;&gt;ClickHouse 系列：查詢優化案例&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-bi-integration/&quot;&gt;ClickHouse 系列：與 BI 工具整合（Power BI）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-cloud-vs-self-host/&quot;&gt;ClickHouse 系列：ClickHouse Cloud 與自建部署的優劣比較&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-security-rbac/&quot;&gt;ClickHouse 系列：資料庫安全性與權限管理（RBAC）實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-operator-kubernates/&quot;&gt;ClickHouse 系列：Kubernetes 部署分散式架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-sourcecode-introduction/&quot;&gt;ClickHouse 系列：從原始碼看 MergeTree 的六大核心機制&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>ClickHouse 系列：Distributed Table 與分布式查詢架構</title><link>https://vicwen.app/posts/clickhouse-distributed-table-architecture/</link><guid isPermaLink="true">https://vicwen.app/posts/clickhouse-distributed-table-architecture/</guid><pubDate>Sat, 16 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;:::warning
本文會稍微講述分散式系統的基本定義和概念，所以請各位放心食用（小孩愛吃）。
:::&lt;/p&gt;
&lt;p&gt;隨著資料量從 TB 級別成長至 PB 級別，單機架構顯然無法應付現代資料分析與即時查詢的需求。ClickHouse 透過 &lt;strong&gt;Distributed Table 與分布式查詢架構&lt;/strong&gt;，讓資料能夠橫向擴展到&lt;strong&gt;數十、數百台節點&lt;/strong&gt;，並在龐大資料量下依然維持秒級查詢回應。&lt;/p&gt;
&lt;h2&gt;什麼是 Distributed Table？&lt;/h2&gt;
&lt;p&gt;Distributed Table 並不是&lt;strong&gt;實際存放資料的表&lt;/strong&gt;，而是 ClickHouse 提供的 &lt;strong&gt;查詢路由代理 (Query Router)&lt;/strong&gt;。它的作用是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;接收分布式查詢請求。&lt;/li&gt;
&lt;li&gt;將查詢根據分片 (Shard) 規則分派至對應的資料節點 (Shard / Replica)。&lt;/li&gt;
&lt;li&gt;收集各分片節點的查詢結果後，進行合併 (Merge) 回傳給使用者。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;核心概念：&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;名稱&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Shard&lt;/td&gt;
&lt;td&gt;水平切分資料的單位，每個分片儲存資料的子集。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Replica&lt;/td&gt;
&lt;td&gt;同一 Shard 的多個副本，保證高可用性與讀取負載平衡。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distributed Table&lt;/td&gt;
&lt;td&gt;路由器角色，負責將查詢請求分派至正確的 Shard/Replica 節點。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remote Table&lt;/td&gt;
&lt;td&gt;真正儲存資料的表 (通常為 MergeTree 或其變種引擎)。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/posts/clickhouse-distributed-table-architecture/distributed-architecture.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;自己做的&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;語法與配置&lt;/h2&gt;
&lt;h3&gt;1. 建立 Remote Table (實際儲存資料的表)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE cluster_shard.local_visits
(
    Date Date,
    UserID UInt64,
    PageViews UInt32
) ENGINE = MergeTree()
ORDER BY (Date, UserID);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2. 建立 Distributed Table (分布式查詢路由表)&lt;/h3&gt;
&lt;p&gt;:::warning
在 ClickHouse Cloud 中，若要建立 distributed table，是不支援 &lt;strong&gt;Distributed(...)&lt;/strong&gt; 的，請改使用 &lt;a href=&quot;https://clickhouse.com/docs/sql-reference/table-functions/remote&quot;&gt;remote/remoteSecure&lt;/a&gt; 來實作。
:::&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster] AS [db2.]name2 ENGINE = Distributed(cluster, database, table[, sharding_key[, policy_name]]) [SETTINGS name=value, ...]
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE cluster_shard.distributed_visits
AS cluster_shard.local_visits
ENGINE = Distributed(cluster_name, &apos;cluster_shard&apos;, local_visits, rand());
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;參數說明：&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;參數&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cluster_name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;定義於 clusters.xml 的叢集名稱。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cluster_shard&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;資料庫名稱 (Database)。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;local_visits&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remote Table 名稱 (目標資料表)。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rand()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;分片選擇策略，常見還有 &lt;code&gt;sharding_key()&lt;/code&gt; 用於精確控制分片邏輯。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;分片策略設計&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;策略&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rand()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;隨機分片，適用於無明確切分邏輯、但需均衡寫入負載的場景。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cityHash64(UserID) % N&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;根據 UserID 做 Hash 切分，保證相同 UserID 資料會落在同一 Shard 上。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;toYYYYMM(Date) % N&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;根據時間維度做切分，適合時間序列資料，如日誌分析、感測器資料。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sharding Expression (任意欄位)&lt;/td&gt;
&lt;td&gt;可自由設定複合欄位作為分片鍵，根據業務邏輯優化查詢裁剪 (Data Skipping)。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;設計建議：&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;維持分片數量與資料量均衡 (避免資料傾斜)。&lt;/li&gt;
&lt;li&gt;分片鍵選擇應盡量與&lt;strong&gt;查詢 WHERE 條件一致&lt;/strong&gt;，提升 Data Skipping 效率。&lt;/li&gt;
&lt;li&gt;盡可能避免查詢跨所有 Shard (如無分片條件的全表掃描)。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;分片與副本架構&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;結構元件&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Shard&lt;/td&gt;
&lt;td&gt;水平切分資料的單位，資料只儲存在該分片內的副本節點上。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Replica&lt;/td&gt;
&lt;td&gt;同一 Shard 的多個副本，儲存完全相同的資料，支援高可用性與讀負載均衡。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zookeeper / ClickHouse Keeper&lt;/td&gt;
&lt;td&gt;負責 Replica 間的同步與叢集協調 (如故障轉移)。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;分布式查詢的運作流程&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Client 發起查詢 (Distributed Table)&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;Distributed Table 根據 Sharding Key (或 rand()) 決定分派至哪些 Shard。&lt;/li&gt;
&lt;li&gt;每個 Shard 中的 Replica Node 會由 ClickHouse 根據負載自動選擇 (或讀取任意 Replica)。&lt;/li&gt;
&lt;li&gt;每個節點執行查詢，將部分結果回傳至 Distributed Table 節點。&lt;/li&gt;
&lt;li&gt;Distributed Table 節點將來自各 Shard 的結果進行合併 (Merge)，並回傳給 Client。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Replica 設計策略&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;策略&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;高可用性&lt;/td&gt;
&lt;td&gt;當 Primary 節點故障時，自動切換至其他 Replica 節點，保證查詢不中斷。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;讀取負載平衡&lt;/td&gt;
&lt;td&gt;分散 Read Query 至不同 Replica，減少單點負載壓力。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;寫入一致性 (Quorum Writes)&lt;/td&gt;
&lt;td&gt;重要資料場景可啟用 Quorum，確保寫入時多數副本確認後才視為成功。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;優化方向&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;優化方向&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;查詢裁剪 (Data Skipping)&lt;/td&gt;
&lt;td&gt;設計良好的 Partition Key 與 Sharding Key，避免全表掃描。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;All-replicas 選項調整&lt;/td&gt;
&lt;td&gt;控制查詢時是否讀取所有副本或只讀其中一個，避免不必要的副本同步壓力。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distributed_aggregation_memory_efficient&lt;/td&gt;
&lt;td&gt;启用記憶體優化的分布式聚合方式，將聚合結果分批合併，降低記憶體消耗。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;設定 preferred_replica 訪問策略&lt;/td&gt;
&lt;td&gt;可透過設定指定節點優先回應查詢，避免每次隨機讀取造成 Cache Miss。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;額外補充&lt;/h2&gt;
&lt;p&gt;由於內容較多，若有興趣可查看&lt;a href=&quot;https://clickhouse.com/docs/engines/table-engines/special/distributed&quot;&gt;官方文件&lt;/a&gt;，以下是一些額外補充：&lt;/p&gt;
&lt;h3&gt;1. INSERT, SELECT&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;操作&lt;/th&gt;
&lt;th&gt;行為說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;INSERT&lt;/td&gt;
&lt;td&gt;依據 Sharding Key 決定資料寫入到對應 Shard，若 internal_replication=true，則僅寫入一個 Replica，其餘 Replica 透過同步完成。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SELECT&lt;/td&gt;
&lt;td&gt;Distributed Table 會並行查詢所有符合條件的 Shard，並合併結果回傳。若條件中有 Sharding Key，則會優化查詢僅對應特定 Shard。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;2. &lt;strong&gt;Distributed Tables 中的 local 節點優先讀取 (prefer_localhost_replica)&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;如果 Distributed Table 與 remote_servers 中的 Replica 節點為同一台機器，則查詢預設會優先從 Local 節點讀取，減少網路傳輸。&lt;/li&gt;
&lt;li&gt;這行為由 &lt;strong&gt;prefer_localhost_replica&lt;/strong&gt; 控制，可強制或禁止此優先行為。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. &lt;strong&gt;Data Skipping with Distributed Table&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;若 Distributed Table 中的 Remote Table (如 MergeTree) 設計了 Partition / Primary Key 索引，ClickHouse 仍能在分布式查詢時利用這些索引進行 Data Skipping (裁剪)。&lt;/li&gt;
&lt;li&gt;前提是 Distributed Table 查詢條件需能對應到分片鍵或分區條件，否則會掃描所有 Shard。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4. &lt;strong&gt;insert_distributed_sync 設定 (強制同步寫入)&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;預設 Distributed Table 的 INSERT 操作是非同步的，當資料送至 Remote Shard 後即視為成功。&lt;/li&gt;
&lt;li&gt;若希望寫入時能等待所有 Shard 回應後才返回成功，可將 &lt;code&gt;insert_distributed_sync&lt;/code&gt; 設為 1，確保資料一致性。&lt;/li&gt;
&lt;li&gt;這會影響寫入延遲，但能保證寫入成功時資料已在所有節點完成寫入。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5. &lt;strong&gt;支持分佈式 DDL (ON CLUSTER 語法)&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;若 clusters.xml 中設定 &lt;code&gt;allow_distributed_ddl_queries = true&lt;/code&gt;，可使用：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE my_table ON CLUSTER my_cluster ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;讓表結構自動同步到叢集內所有節點。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;適用於建立 Distributed Table 需要跨節點一致的場景。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;6. &lt;strong&gt;Distributed_aggregation_memory_efficient&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;分布式查詢的聚合 (Group By) 過程中，若資料量極大會導致記憶體壓力過高。&lt;/li&gt;
&lt;li&gt;開啟 &lt;code&gt;distributed_aggregation_memory_efficient = 1&lt;/code&gt; 後，ClickHouse 會讓各 Shard 返回部分聚合結果，再分批進行聚合 Merge，降低 Driver Node 的記憶體負擔。&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;功能&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;distributed_product_mode&lt;/td&gt;
&lt;td&gt;控制 Distributed 表與其他 Distributed 表之間的 Join 行為 (如 All, Deny, Local)。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;prefer_localhost_replica&lt;/td&gt;
&lt;td&gt;控制 Distributed Table 查詢時是否優先從本地 Replica 讀取。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;connect_timeout_with_failover_ms&lt;/td&gt;
&lt;td&gt;Distributed 查詢時若 Replica 失敗，切換到其他 Replica 的 timeout 設定。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;ClickHouse 系列持續更新中:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/what-is-clickhouse/&quot;&gt;ClickHouse 系列：ClickHouse 是什麼？與傳統 OLAP/OLTP 資料庫的差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-column-row-based-storage/&quot;&gt;ClickHouse 系列：ClickHouse 為什麼選擇 Column-based 儲存？講解 Row-based 與 Column-based 的核心差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-engine&quot;&gt;ClickHouse 系列：ClickHouse 儲存引擎 - MergeTree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-compression-skipping-index/&quot;&gt;ClickHouse 系列：壓縮技術與 Data Skipping Indexes 如何大幅加速查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replacingmergetree-deduplication/&quot;&gt;ClickHouse 系列：ReplacingMergeTree 與資料去重機制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-summingmergetree-aggregation/&quot;&gt;ClickHouse 系列：SummingMergeTree 進行資料彙總的應用場景&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-materialized-view/&quot;&gt;ClickHouse 系列：Materialized Views 即時聚合查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-partition-pruning/&quot;&gt;ClickHouse 系列：分區策略與 Partition Pruning 原理解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-primary-sorting-key/&quot;&gt;ClickHouse 系列：Primary Key、Sorting Key 與 Granule 索引運作原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-collapsingmergetree/&quot;&gt;ClickHouse 系列：CollapsingMergeTree 與邏輯刪除的最佳實踐&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-versioned-collapsingmergetree/&quot;&gt;ClickHouse 系列：VersionedCollapsingMergeTree 版本控制與資料衝突解決&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-aggregatingmergetree/&quot;&gt;ClickHouse 系列：AggregatingMergeTree 實時指標統計的進階應用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-distributed-table-architecture/&quot;&gt;ClickHouse 系列：Distributed Table 與分布式查詢架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replication-failover/&quot;&gt;ClickHouse 系列：Replicated Tables 高可用性與零停機升級實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-kafka-data-streaming-pipeline/&quot;&gt;ClickHouse 系列：與 Kafka 整合打造即時 Data Streaming Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-batch-import/&quot;&gt;ClickHouse 系列：批次匯入最佳實踐 (CSV、Parquet、Native Format)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-external-data-integration/&quot;&gt;ClickHouse 系列：ClickHouse 與外部資料源整合（PostgreSQL）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-query-log-explain/&quot;&gt;ClickHouse 系列：如何提升查詢優化？system.query_log 與 EXPLAIN 用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-projections-optimization/&quot;&gt;ClickHouse 系列：Projections 進階查詢加速技術&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-sampling-statistics/&quot;&gt;ClickHouse 系列：Sampling 抽樣查詢與統計技術原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-ttl-storage-management/&quot;&gt;ClickHouse 系列：TTL 資料清理與儲存成本優化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-storage-policies/&quot;&gt;ClickHouse 系列：儲存政策（Storage Policies）與磁碟資源分層策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-schemas-storage-improvement/&quot;&gt;ClickHouse 系列：表格設計與儲存優化細節&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-grafana-dashboard/&quot;&gt;ClickHouse 系列：ClickHouse 系列：整合 Grafana 打造可視化監控&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-select-optimization/&quot;&gt;ClickHouse 系列：查詢優化案例&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-bi-integration/&quot;&gt;ClickHouse 系列：與 BI 工具整合（Power BI）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-cloud-vs-self-host/&quot;&gt;ClickHouse 系列：ClickHouse Cloud 與自建部署的優劣比較&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-security-rbac/&quot;&gt;ClickHouse 系列：資料庫安全性與權限管理（RBAC）實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-operator-kubernates/&quot;&gt;ClickHouse 系列：Kubernetes 部署分散式架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-sourcecode-introduction/&quot;&gt;ClickHouse 系列：從原始碼看 MergeTree 的六大核心機制&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>ClickHouse 系列：AggregatingMergeTree 實時指標統計的進階應用</title><link>https://vicwen.app/posts/clickhouse-aggregatingmergetree-aggregation/</link><guid isPermaLink="true">https://vicwen.app/posts/clickhouse-aggregatingmergetree-aggregation/</guid><pubDate>Fri, 15 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在大型資料分析系統中，隨著資料規模與查詢複雜度提升，單純依賴 SELECT 聚合查詢（如 SUM、COUNT、AVG）將無法滿足即時回應的需求。ClickHouse 針對高效聚合查詢場景，提供了 &lt;strong&gt;AggregatingMergeTree&lt;/strong&gt; 儲存引擎，透過預計算 (Pre-Aggregation) 與聚合函數壓縮 (AggregateFunction 型別)，大幅降低查詢延遲。&lt;/p&gt;
&lt;h3&gt;什麼是 AggregatingMergeTree？&lt;/h3&gt;
&lt;p&gt;AggregatingMergeTree 是 ClickHouse MergeTree 家族中的「聚合專用儲存引擎」，其特點是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;資料寫入時儲存為 &lt;strong&gt;AggregateFunction 型別&lt;/strong&gt;（預計算的聚合狀態）。&lt;/li&gt;
&lt;li&gt;Merge 階段進行 &lt;strong&gt;狀態合併 (State Merging)&lt;/strong&gt;，將多筆相同 Primary Key 的聚合結果進行再彙總。&lt;/li&gt;
&lt;li&gt;查詢時透過 &lt;strong&gt;聚合狀態還原 (State Finalization)&lt;/strong&gt;，將狀態轉為最終數值結果。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;這種設計特別適合高頻寫入 + 聚合查詢的場景，如：網站流量統計、實時 KPI 指標儀表板、IoT 裝置資料彙總。&lt;/p&gt;
&lt;h3&gt;與 SummingMergeTree 的差異比較&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;SummingMergeTree&lt;/th&gt;
&lt;th&gt;AggregatingMergeTree&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;支援聚合函數&lt;/td&gt;
&lt;td&gt;只支援 SUM&lt;/td&gt;
&lt;td&gt;支援所有 AggregateFunction (如 SUM, AVG, COUNT, MIN, MAX)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;資料儲存型別&lt;/td&gt;
&lt;td&gt;普通數值欄位 (UInt, Float)&lt;/td&gt;
&lt;td&gt;AggregateFunction 型別 (儲存聚合狀態)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Merge 時是否會聚合&lt;/td&gt;
&lt;td&gt;會針對 Primary Key 相同的數值欄位做加總&lt;/td&gt;
&lt;td&gt;會將相同 Primary Key 的聚合狀態進行合併&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;查詢時是否需特殊處理&lt;/td&gt;
&lt;td&gt;不需特殊處理，直接 SELECT 查詢即可&lt;/td&gt;
&lt;td&gt;查詢時需使用聚合函數將狀態還原（如 sumMerge(col), avgMerge(col)）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;適用場景&lt;/td&gt;
&lt;td&gt;計數器類型（PV、點擊數、金額彙總）&lt;/td&gt;
&lt;td&gt;複雜聚合運算（平均值、去重計數、分位數、標準差等統計需求）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;語法與基本範例&lt;/h3&gt;
&lt;h4&gt;建立 Source Table, AggregatingMergeTree Table, MV Table：&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;-- Source Table
CREATE TABLE visits (
    StartDate DateTime64 NOT NULL,
    CounterID UInt64,
    Sign Nullable(Int32),
    UserID Nullable(Int32)
) ENGINE = MergeTree ORDER BY (StartDate, CounterID);

-- AggregatingMergeTree Table
CREATE TABLE agg_visits (
    StartDate DateTime64 NOT NULL,
    CounterID UInt64,
    Visits AggregateFunction(sum, Nullable(Int32)),
    Users AggregateFunction(uniq, Nullable(Int32))
)
ENGINE = AggregatingMergeTree()
ORDER BY (StartDate, CounterID);

-- Materialized View Table
CREATE MATERIALIZED VIEW visits_mv TO agg_visits
AS SELECT
    StartDate,
    CounterID,
    sumState(Sign) AS Visits,
    uniqState(UserID) AS Users
FROM visits
GROUP BY StartDate, CounterID;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;寫入資料 (以聚合狀態寫入)：&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;INSERT INTO visits (StartDate, CounterID, Sign, UserID)
 VALUES (1667446031000, 1, 3, 4), (1667446031000, 1, 6, 3);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;資料會同時寫入 &lt;code&gt;visits&lt;/code&gt; 和 &lt;code&gt;agg_visits&lt;/code&gt; Table&lt;/p&gt;
&lt;h4&gt;查詢聚合結果 (需用 Merge 函數還原狀態)：&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;SELECT
    StartDate,
    sumMerge(Visits) AS Visits,
    uniqMerge(Users) AS Users
FROM agg_visits
GROUP BY StartDate
ORDER BY StartDate;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;┌───────────────StartDate─┬─Visits─┬─Users─┐
│ 2022-11-03 03:27:11.000 │      9 │     2 │
└─────────────────────────┴────────┴───────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;運作流程&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;資料寫入 (AggregateFunction 狀態寫入)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;透過 &lt;code&gt;sumState()&lt;/code&gt;, &lt;code&gt;avgState()&lt;/code&gt;, &lt;code&gt;uniqExactState()&lt;/code&gt; 等函數將資料寫入為「聚合狀態」。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;背景 Merge 操作 (State Merging)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;當相同 Primary Key 的資料進行合併時，ClickHouse 會將 AggregateFunction 狀態進行「狀態合併」。&lt;/li&gt;
&lt;li&gt;這個過程發生於背景合併 (Merge)，不影響寫入吞吐量。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;查詢時還原聚合結果 (State Finalization)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;查詢時需透過 &lt;code&gt;sumMerge()&lt;/code&gt;, &lt;code&gt;avgMerge()&lt;/code&gt;, &lt;code&gt;uniqExactMerge()&lt;/code&gt; 等函數將聚合狀態還原為最終數值。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;應用場景&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;應用場景&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;即時流量與使用者指標統計 (PV / UV 報表)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;大量寫入使用者行為事件，彙總統計每天每頁面瀏覽量與不重複訪客數 (uniqExact)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;IoT 裝置資料彙總與即時狀態統計&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;實時收集 IoT 感測器資料，進行彙總平均值、最大最小值、分位數計算&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;行為事件流去重與複雜指標計算&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;如多指標 KPI 彙總、Session Duration 平均值等需進階統計場景&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;資料層級預聚合 (Pre-Aggregation)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;針對查詢頻繁的聚合結果進行預先計算，減少查詢時的 CPU 負載與 I/O 操作&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;改進：延遲聚合至 Merge 階段&lt;/h3&gt;
&lt;p&gt;為了將聚合計算成本從 INSERT 時移至 Merge 階段，可以這麼做：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SET optimize_on_insert = 0;

CREATE MATERIALIZED VIEW visits_mv TO agg_visits
AS SELECT
    StartDate,
    CounterID,
    initializeAggregation(&apos;sum&apos;, Sign) AS Visits,
    initializeAggregation(&apos;uniqExact&apos;, UserID) AS Users
FROM visits;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;透過以下兩個關鍵步驟：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;initializeAggregation()&lt;/code&gt;：將原始值轉為聚合狀態寫入，無需 Group By。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;optimize_on_insert = 0&lt;/code&gt;：關閉插入時的自動聚合優化，將聚合推遲到後續 Merge 操作進行。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;可以帶來：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;減少 INSERT 時的計算壓力 (極大化寫入吞吐)&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;傳統 Materialized View 的預聚合邏輯：每次寫入 (INSERT) 都需要先做 Group By 聚合計算，這會對 CPU 與 Memory 造成即時壓力，寫入高峰時，會拖慢寫入速度。&lt;/li&gt;
&lt;li&gt;initializeAggregation + optimize_on_insert = 0：每筆資料直接以「聚合狀態」寫入，無需即時計算聚合結果，讓 INSERT 成本變成「純寫檔案 + 生成狀態」的低成本操作。&lt;/li&gt;
&lt;li&gt;特別適合每秒數萬、數十萬筆的高頻寫入場景，如 IoT、網站點擊流、使用者行為追蹤。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;聚合計算的成本被「平滑化」到背景 Merge&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;傳統的預聚合方式會把所有聚合計算都集中在 INSERT 時進行，當寫入高峰時，資源使用率會瞬間飆升。&lt;/li&gt;
&lt;li&gt;initializeAggregation 模式將聚合邏輯延遲到 Merge 階段，讓這些計算能夠由 ClickHouse 以 &lt;strong&gt;批次、自動、分散式&lt;/strong&gt;的方式在背景進行。所以背景 Merge 可以根據系統負載動態調整，不會對線上寫入與查詢造成即時衝擊，更能彈性調整 Merge 策略 (如 TTL-based Merge、optimize_final 週期性合併)。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;為什麼 &lt;code&gt;initializeAggregation&lt;/code&gt; 必須搭配 &lt;code&gt;optimize_on_insert = 0&lt;/code&gt;？&lt;/h4&gt;
&lt;p&gt;當使用 &lt;code&gt;initializeAggregation()&lt;/code&gt; 時，ClickHouse 會為每筆資料產生一個「尚未合併的 Aggregate State」，這讓每筆資料都能保留獨立的聚合狀態，直到 MergeTree 在背景進行合併 (Merge) 時，才會把相同 Primary Key 的資料進行實際聚合。&lt;/p&gt;
&lt;p&gt;然而，這種「不經 Group By 預聚合」的行為，預設情況下 (&lt;code&gt;optimize_on_insert = 1&lt;/code&gt;) 是無法成立的，因為 ClickHouse 會自動優化將相同 Key 的資料提前聚合後才寫入 (insert-time pre-aggregation)。&lt;/p&gt;
&lt;p&gt;只有當你將 &lt;code&gt;optimize_on_insert&lt;/code&gt; 設為 0 時，ClickHouse 才會跳過這個 insert-time pre-aggregation 優化邏輯，將資料原封不動地寫入 AggregatingMergeTree，並將聚合計算延遲到 Merge 階段進行。&lt;/p&gt;
&lt;h4&gt;效果&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;條件&lt;/th&gt;
&lt;th&gt;行為&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;initializeAggregation() + optimize_on_insert = 0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;每筆來源資料都寫入為單獨的聚合狀態，查詢時需依賴背景 Merge 來獲得最終聚合結果&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;initializeAggregation() + optimize_on_insert = 1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ClickHouse 會自動進行 Group By 聚合後才寫入&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;傳統 &lt;code&gt;sumState() + Group By&lt;/code&gt; 寫法&lt;/td&gt;
&lt;td&gt;INSERT 時資料已經被預先聚合&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;Best Practice&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Best Practice&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;聚合狀態資料需搭配 State/ Merge 函數處理&lt;/td&gt;
&lt;td&gt;INSERT 時寫入 State，SELECT 時用 Merge 函數還原&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Primary Key 設計決定 Merge 粒度&lt;/td&gt;
&lt;td&gt;ORDER BY 應設計為能唯一標識聚合維度 (如 date, page)，避免去重無效或合併過大範圍&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;結合 Partition Key 進行資料區塊管理&lt;/td&gt;
&lt;td&gt;減少 Merge 時的資源消耗，提高查詢裁剪效率&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Materialized View 進行即時聚合計算&lt;/td&gt;
&lt;td&gt;提升原始資料寫入到 AggregatingMergeTree 的實時性與性能&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;定期 OPTIMIZE FINAL 合併資料&lt;/td&gt;
&lt;td&gt;確保合併後查詢結果的一致性與效能，避免過多小 Data Part 影響性能&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;AggregatingMergeTree v.s. SummingMergeTree 選用時機&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;適用場景&lt;/th&gt;
&lt;th&gt;建議引擎選擇&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;單純計數器類型 (如 PV/點擊數)&lt;/td&gt;
&lt;td&gt;SummingMergeTree&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;需計算不重複數量 (如 UV、去重計數)&lt;/td&gt;
&lt;td&gt;AggregatingMergeTree + uniqExact&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;需計算平均值、分位數等進階聚合指標&lt;/td&gt;
&lt;td&gt;AggregatingMergeTree&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;資料補寫與修正較頻繁的場景&lt;/td&gt;
&lt;td&gt;AggregatingMergeTree&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;結語&lt;/h3&gt;
&lt;p&gt;個人覺得 AggregatingMergeTree 是處理「高頻寫入 + 複雜聚合統計」場景的強大工具。但內部真的很複雜 XD，知識點極多，當你在選用 MergeTree 引擎時，應當具備較深的相關知識，才能發揮很好的優化系統。&lt;/p&gt;
&lt;p&gt;而透過 AggregateFunction 型別預儲存聚合狀態，搭配 MergeTree 的合併機制與 Materialized View 實時計算能力，能將查詢效能提升到毫秒級，極適合指標報表、流量監控、IoT 資料彙總等應用場景。&lt;/p&gt;
&lt;p&gt;但也需留意 &lt;strong&gt;Primary Key 與 Partition 設計&lt;/strong&gt;，才能讓聚合合併與查詢裁剪效能發揮到極致。&lt;/p&gt;
&lt;h4&gt;ClickHouse 系列持續更新中:&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/what-is-clickhouse/&quot;&gt;ClickHouse 系列：ClickHouse 是什麼？與傳統 OLAP/OLTP 資料庫的差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-column-row-based-storage/&quot;&gt;ClickHouse 系列：ClickHouse 為什麼選擇 Column-based 儲存？講解 Row-based 與 Column-based 的核心差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-engine&quot;&gt;ClickHouse 系列：ClickHouse 儲存引擎 - MergeTree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-compression-skipping-index/&quot;&gt;ClickHouse 系列：壓縮技術與 Data Skipping Indexes 如何大幅加速查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replacingmergetree-deduplication/&quot;&gt;ClickHouse 系列：ReplacingMergeTree 與資料去重機制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-summingmergetree-aggregation/&quot;&gt;ClickHouse 系列：SummingMergeTree 進行資料彙總的應用場景&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-materialized-view/&quot;&gt;ClickHouse 系列：Materialized Views 即時聚合查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-partition-pruning/&quot;&gt;ClickHouse 系列：分區策略與 Partition Pruning 原理解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-primary-sorting-key/&quot;&gt;ClickHouse 系列：Primary Key、Sorting Key 與 Granule 索引運作原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-collapsingmergetree/&quot;&gt;ClickHouse 系列：CollapsingMergeTree 與邏輯刪除的最佳實踐&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-versioned-collapsingmergetree/&quot;&gt;ClickHouse 系列：VersionedCollapsingMergeTree 版本控制與資料衝突解決&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-aggregatingmergetree/&quot;&gt;ClickHouse 系列：AggregatingMergeTree 實時指標統計的進階應用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-distributed-table-architecture/&quot;&gt;ClickHouse 系列：Distributed Table 與分布式查詢架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replication-failover/&quot;&gt;ClickHouse 系列：Replicated Tables 高可用性與零停機升級實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-kafka-data-streaming-pipeline/&quot;&gt;ClickHouse 系列：與 Kafka 整合打造即時 Data Streaming Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-batch-import/&quot;&gt;ClickHouse 系列：批次匯入最佳實踐 (CSV、Parquet、Native Format)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-external-data-integration/&quot;&gt;ClickHouse 系列：ClickHouse 與外部資料源整合（PostgreSQL）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-query-log-explain/&quot;&gt;ClickHouse 系列：如何提升查詢優化？system.query_log 與 EXPLAIN 用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-projections-optimization/&quot;&gt;ClickHouse 系列：Projections 進階查詢加速技術&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-sampling-statistics/&quot;&gt;ClickHouse 系列：Sampling 抽樣查詢與統計技術原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-ttl-storage-management/&quot;&gt;ClickHouse 系列：TTL 資料清理與儲存成本優化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-storage-policies/&quot;&gt;ClickHouse 系列：儲存政策（Storage Policies）與磁碟資源分層策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-schemas-storage-improvement/&quot;&gt;ClickHouse 系列：表格設計與儲存優化細節&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-grafana-dashboard/&quot;&gt;ClickHouse 系列：ClickHouse 系列：整合 Grafana 打造可視化監控&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-select-optimization/&quot;&gt;ClickHouse 系列：查詢優化案例&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-bi-integration/&quot;&gt;ClickHouse 系列：與 BI 工具整合（Power BI）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-cloud-vs-self-host/&quot;&gt;ClickHouse 系列：ClickHouse Cloud 與自建部署的優劣比較&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-security-rbac/&quot;&gt;ClickHouse 系列：資料庫安全性與權限管理（RBAC）實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-operator-kubernates/&quot;&gt;ClickHouse 系列：Kubernetes 部署分散式架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-sourcecode-introduction/&quot;&gt;ClickHouse 系列：從原始碼看 MergeTree 的六大核心機制&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>ClickHouse 系列：VersionedCollapsingMergeTree 與資料版本控制</title><link>https://vicwen.app/posts/clickhouse-versionedcollapsingmergetree-version-control/</link><guid isPermaLink="true">https://vicwen.app/posts/clickhouse-versionedcollapsingmergetree-version-control/</guid><pubDate>Thu, 14 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在處理即時 Event Streaming 或高頻變動的資料場景時，僅靠 CollapsingMergeTree 的「新增/刪除」邏輯標記，往往無法應付複雜的資料狀態與版本管理需求。ClickHouse 為此提供了更進階的 &lt;strong&gt;VersionedCollapsingMergeTree&lt;/strong&gt; 儲存引擎，透過 &lt;code&gt;sign&lt;/code&gt; 與 &lt;code&gt;version&lt;/code&gt; 雙欄位設計，實現更強大的資料去重與版本控制機制。&lt;/p&gt;
&lt;h2&gt;什麼是 VersionedCollapsingMergeTree？&lt;/h2&gt;
&lt;p&gt;VersionedCollapsingMergeTree 是在 CollapsingMergeTree 基礎上，加入版本欄位 (version) 的變種儲存引擎。
它能根據 Primary Key 判斷資料唯一性，並根據 &lt;code&gt;sign&lt;/code&gt; 進行邏輯刪除，透過 &lt;code&gt;version&lt;/code&gt; 來選擇保留最新版本的紀錄。&lt;/p&gt;
&lt;h3&gt;關鍵特性：&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sign&lt;/code&gt; 欄位：標記新增 (1) 或刪除 (-1)。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;version&lt;/code&gt; 欄位：標記每筆資料的版本號，系統會保留最大版本。&lt;/li&gt;
&lt;li&gt;同一 Primary Key 下，當 &lt;code&gt;sign&lt;/code&gt; 為相反數，且 &lt;code&gt;version&lt;/code&gt; 值相同時，資料會被 collapse (抵消刪除)。&lt;/li&gt;
&lt;li&gt;當 &lt;code&gt;version&lt;/code&gt; 值不同時，會保留最大版本的資料記錄。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;語法與範例&lt;/h2&gt;
&lt;h3&gt;建立 VersionedCollapsingMergeTree 表格：&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE user_profiles
(
    user_id UInt64,
    name String,
    version UInt64,
    sign Int8
) ENGINE = VersionedCollapsingMergeTree(sign, version)
ORDER BY user_id;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;插入資料：&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 初始新增紀錄
INSERT INTO user_profiles VALUES (1, &apos;Alice&apos;, 1, 1);
-- 更新版本紀錄
INSERT INTO user_profiles VALUES (1, &apos;Alice_updated&apos;, 2, 1);
-- 刪除紀錄 (針對 version = 2)
INSERT INTO user_profiles VALUES (1, &apos;Alice_updated&apos;, 2, -1);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;查詢結果 (未做 Merge 前)：&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;user_id&lt;/th&gt;
&lt;th&gt;name&lt;/th&gt;
&lt;th&gt;version&lt;/th&gt;
&lt;th&gt;sign&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Alice&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Alice_updated&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Alice_updated&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;執行背景 Merge (或 OPTIMIZE FINAL) 後：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;version = 2 的資料會被抵消。&lt;/li&gt;
&lt;li&gt;version = 1 的資料因為沒有對應的 -1 sign 紀錄，會被保留下來。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;與 ReplacingMergeTree / CollapsingMergeTree 差異比較&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;CollapsingMergeTree&lt;/th&gt;
&lt;th&gt;ReplacingMergeTree&lt;/th&gt;
&lt;th&gt;VersionedCollapsingMergeTree&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;邏輯刪除 (soft delete) 支援&lt;/td&gt;
&lt;td&gt;sign 抵消&lt;/td&gt;
&lt;td&gt;無邏輯刪除 (只能用 version 覆蓋)&lt;/td&gt;
&lt;td&gt;sign 抵消&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;版本控制&lt;/td&gt;
&lt;td&gt;無內建版本概念&lt;/td&gt;
&lt;td&gt;version 欄位標記最新版本&lt;/td&gt;
&lt;td&gt;version 決定保留最大版本，結合 sign 進行去重&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;去重時機&lt;/td&gt;
&lt;td&gt;Merge 時進行，需配合 FINAL 查詢&lt;/td&gt;
&lt;td&gt;Merge 時進行，需配合 FINAL 查詢&lt;/td&gt;
&lt;td&gt;Merge 時進行，需配合 FINAL 查詢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;使用場景&lt;/td&gt;
&lt;td&gt;簡單新增/刪除標記場景&lt;/td&gt;
&lt;td&gt;資料補寫/覆寫，保留最新紀錄&lt;/td&gt;
&lt;td&gt;複雜補寫、刪除、版本控制需求（如事件版本回滾、補資料等）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;應用場景&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;應用場景&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;資料版本控制 (Data Versioning)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;保留同一 Primary Key 的最新版本資料，並可透過補寫與刪除控制資料狀態。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;即時資料流去重與狀態管理&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;處理 Kafka、Message Queue 中的事件重放與修正，確保資料流中同一主鍵下只有一筆有效紀錄。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;補資料與刪除修正 (Data Correction)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;當資料補寫/刪除需要依賴複雜的版本邏輯時，比單純的 CollapsingMergeTree 更合適。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;IoT 或實時指標狀態更新場景&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;同一設備 (如 sensor_id) 狀態不斷更新，需保證資料只保留最新狀態值，並能正確處理刪除與修正。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;查詢注意事項&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;資料去重與版本選擇會在背景 Merge 階段發生。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;查詢最終結果需使用 &lt;code&gt;FINAL&lt;/code&gt; 關鍵字保證一致性：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT * FROM user_profiles FINAL;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;若資料量大，不建議頻繁全表 FINAL 查詢，可透過：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Partition 粒度縮小查詢範圍&lt;/li&gt;
&lt;li&gt;定期執行 &lt;code&gt;OPTIMIZE TABLE ... FINAL&lt;/code&gt; 保持資料狀態穩定&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;範例&lt;/h2&gt;
&lt;p&gt;假設你正在開發一個使用者行為追蹤平台，需要補寫錯誤事件並能正確刪除修正資料：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;使用者行為事件流表格設計&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE user_events
(
    user_id UInt64,
    event_type String,
    event_time DateTime,
    version UInt64,
    sign Int8
) ENGINE = VersionedCollapsingMergeTree(sign, version)
ORDER BY (user_id, event_time);
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;寫入行為事件與補寫修正資料&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;INSERT INTO user_events VALUES (1001, &apos;click&apos;, &apos;2025-08-01 10:00:00&apos;, 1, 1);
INSERT INTO user_events VALUES (1001, &apos;click&apos;, &apos;2025-08-01 10:00:00&apos;, 2, 1); -- 修正補寫
INSERT INTO user_events VALUES (1001, &apos;click&apos;, &apos;2025-08-01 10:00:00&apos;, 2, -1); -- 取消錯誤修正
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;查詢最終狀態&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT * FROM user_events FINAL;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Best Practice&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Best Practice&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Primary Key 設計應能唯一識別一筆邏輯紀錄&lt;/td&gt;
&lt;td&gt;避免過細碎的主鍵組合，確保去重與版本裁剪能正確發生。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;version 應為遞增且能反映資料變動邏輯&lt;/td&gt;
&lt;td&gt;建議以 timestamp 或 version number 遞增欄位設計，確保去重時保留最新紀錄。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sign 與 version 的邏輯需清晰一致&lt;/td&gt;
&lt;td&gt;若資料補寫、刪除狀態標記混亂，將導致 Merge 時無法正確裁剪資料。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;結合 Partition Key 設計，減少 Merge 範圍裁剪成本&lt;/td&gt;
&lt;td&gt;大資料場景中，Partition 可有效縮小去重與版本選擇的影響範圍，提升查詢與 Merge 效率。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FINAL 查詢請盡量避免全表查詢，可透過局部分區或維運合併處理&lt;/td&gt;
&lt;td&gt;資料量大的情況下，全表 FINAL 會消耗大量資源，應搭配維運腳本定期執行 Optimize FINAL 作業。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;結語&lt;/h2&gt;
&lt;p&gt;VersionedCollapsingMergeTree 是解決 ClickHouse 中「資料狀態管理」與「複雜去重補資料場景」的強大引擎。它兼具 CollapsingMergeTree 的去重邏輯與 ReplacingMergeTree 的版本控制機制，適用於那些資料狀態變動頻繁且版本一致性要求高的業務場景。然而，設計上需特別留意 Primary Key、sign 與 version 的邏輯一致性，才能發揮這個引擎的最大效能優勢。&lt;/p&gt;
&lt;h3&gt;ClickHouse 系列持續更新中:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/what-is-clickhouse/&quot;&gt;ClickHouse 系列：ClickHouse 是什麼？與傳統 OLAP/OLTP 資料庫的差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-column-row-based-storage/&quot;&gt;ClickHouse 系列：ClickHouse 為什麼選擇 Column-based 儲存？講解 Row-based 與 Column-based 的核心差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-engine&quot;&gt;ClickHouse 系列：ClickHouse 儲存引擎 - MergeTree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-compression-skipping-index/&quot;&gt;ClickHouse 系列：壓縮技術與 Data Skipping Indexes 如何大幅加速查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replacingmergetree-deduplication/&quot;&gt;ClickHouse 系列：ReplacingMergeTree 與資料去重機制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-summingmergetree-aggregation/&quot;&gt;ClickHouse 系列：SummingMergeTree 進行資料彙總的應用場景&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-materialized-view/&quot;&gt;ClickHouse 系列：Materialized Views 即時聚合查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-partition-pruning/&quot;&gt;ClickHouse 系列：分區策略與 Partition Pruning 原理解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-primary-sorting-key/&quot;&gt;ClickHouse 系列：Primary Key、Sorting Key 與 Granule 索引運作原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-collapsingmergetree/&quot;&gt;ClickHouse 系列：CollapsingMergeTree 與邏輯刪除的最佳實踐&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-versioned-collapsingmergetree/&quot;&gt;ClickHouse 系列：VersionedCollapsingMergeTree 版本控制與資料衝突解決&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-aggregatingmergetree/&quot;&gt;ClickHouse 系列：AggregatingMergeTree 實時指標統計的進階應用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-distributed-table-architecture/&quot;&gt;ClickHouse 系列：Distributed Table 與分布式查詢架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replication-failover/&quot;&gt;ClickHouse 系列：Replicated Tables 高可用性與零停機升級實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-kafka-data-streaming-pipeline/&quot;&gt;ClickHouse 系列：與 Kafka 整合打造即時 Data Streaming Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-batch-import/&quot;&gt;ClickHouse 系列：批次匯入最佳實踐 (CSV、Parquet、Native Format)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-external-data-integration/&quot;&gt;ClickHouse 系列：ClickHouse 與外部資料源整合（PostgreSQL）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-query-log-explain/&quot;&gt;ClickHouse 系列：如何提升查詢優化？system.query_log 與 EXPLAIN 用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-projections-optimization/&quot;&gt;ClickHouse 系列：Projections 進階查詢加速技術&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-sampling-statistics/&quot;&gt;ClickHouse 系列：Sampling 抽樣查詢與統計技術原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-ttl-storage-management/&quot;&gt;ClickHouse 系列：TTL 資料清理與儲存成本優化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-storage-policies/&quot;&gt;ClickHouse 系列：儲存政策（Storage Policies）與磁碟資源分層策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-schemas-storage-improvement/&quot;&gt;ClickHouse 系列：表格設計與儲存優化細節&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-grafana-dashboard/&quot;&gt;ClickHouse 系列：ClickHouse 系列：整合 Grafana 打造可視化監控&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-select-optimization/&quot;&gt;ClickHouse 系列：查詢優化案例&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-bi-integration/&quot;&gt;ClickHouse 系列：與 BI 工具整合（Power BI）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-cloud-vs-self-host/&quot;&gt;ClickHouse 系列：ClickHouse Cloud 與自建部署的優劣比較&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-security-rbac/&quot;&gt;ClickHouse 系列：資料庫安全性與權限管理（RBAC）實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-operator-kubernates/&quot;&gt;ClickHouse 系列：Kubernetes 部署分散式架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-sourcecode-introduction/&quot;&gt;ClickHouse 系列：從原始碼看 MergeTree 的六大核心機制&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>ClickHouse 系列：CollapsingMergeTree 與邏輯刪除</title><link>https://vicwen.app/posts/clickhouse-collapsingmergetree-soft-delete/</link><guid isPermaLink="true">https://vicwen.app/posts/clickhouse-collapsingmergetree-soft-delete/</guid><pubDate>Wed, 13 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在傳統 OLTP 資料庫中，刪除與更新資料是家常便飯，但在 ClickHouse 這類專為 OLAP 場景設計的資料庫中，「邏輯刪除」與「資料版本控制」則需要透過特別設計的儲存引擎來實現。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CollapsingMergeTree&lt;/strong&gt; 就是 ClickHouse 提供的一種能夠自動處理資料新增與刪除標記（邏輯刪除）的特殊 MergeTree 引擎。&lt;/p&gt;
&lt;h2&gt;什麼是 CollapsingMergeTree？&lt;/h2&gt;
&lt;p&gt;CollapsingMergeTree 是 ClickHouse MergeTree 家族的一員，設計用來解決「資料新增與刪除標記」的場景。
其運作邏輯是透過一個稱為 &lt;code&gt;sign&lt;/code&gt; 的欄位標記資料的新增或刪除狀態，在背景 Merge 操作時，自動將 &lt;code&gt;sign&lt;/code&gt; 相反的紀錄抵消 (collapse) 以實現邏輯刪除效果。&lt;/p&gt;
&lt;h3&gt;核心特性：&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;透過 &lt;code&gt;sign&lt;/code&gt; 欄位標記資料狀態：&lt;code&gt;1&lt;/code&gt; 代表新增，&lt;code&gt;-1&lt;/code&gt; 代表刪除。&lt;/li&gt;
&lt;li&gt;Merge 階段會自動將相同 Primary Key 下的 &lt;code&gt;1&lt;/code&gt; / &lt;code&gt;-1&lt;/code&gt; 抵消掉。&lt;/li&gt;
&lt;li&gt;資料刪除為「最終一致性」動作（&lt;strong&gt;非即時刪除&lt;/strong&gt;）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;語法&lt;/h2&gt;
&lt;h3&gt;建立&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE user_actions
(
    user_id UInt64,
    action String,
    sign Int8
) ENGINE = CollapsingMergeTree(sign)
ORDER BY user_id;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;插入&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-- 新增行為紀錄
INSERT INTO user_actions VALUES (1, &apos;login&apos;, 1);
INSERT INTO user_actions VALUES (2, &apos;purchase&apos;, 1);

-- 邏輯刪除 user_id = 2 的紀錄
INSERT INTO user_actions VALUES (2, &apos;purchase&apos;, -1);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;查詢&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;SELECT * FROM user_actions;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;會看到資料仍然存在，但進行 Merge 後（&lt;code&gt;FINAL&lt;/code&gt;）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;OPTIMIZE TABLE user_actions FINAL;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;user_id = 2&lt;/code&gt; 的紀錄將被抵消 (collapse) 掉，只剩下 &lt;code&gt;user_id = 1&lt;/code&gt; 的資料。&lt;/p&gt;
&lt;h2&gt;運作原理&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;資料寫入時不進行即時去重&lt;/strong&gt;，所有資料（含 &lt;code&gt;sign = -1&lt;/code&gt;）都會被寫入磁碟。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;在背景合併 (Merge) 操作時&lt;/strong&gt;，ClickHouse 會根據 Primary Key 對應的資料進行配對：
&lt;ul&gt;
&lt;li&gt;1 筆 &lt;code&gt;sign = 1&lt;/code&gt; 與 1 筆 &lt;code&gt;sign = -1&lt;/code&gt; 的資料將被抵消 (collapse)。&lt;/li&gt;
&lt;li&gt;若 sign 數量不平衡 (如 &lt;code&gt;sign = 1&lt;/code&gt; 多餘 &lt;code&gt;sign = -1&lt;/code&gt;)，則會保留差值的紀錄。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;查詢時不會自動隱藏尚未抵消的資料&lt;/strong&gt;，若要查詢已 collapse 的最終結果，可透過 &lt;code&gt;FINAL&lt;/code&gt; 查詢保證一致性。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;資料刪除是最終一致性動作&lt;/strong&gt;，在 Merge 完成前仍會顯示原始資料。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;應用場景&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;場景&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;邏輯刪除 (Soft Delete)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;需要保留刪除紀錄但不想讓刪除資料出現在查詢結果中&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;補資料與修正紀錄 (Data Correction)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;錯誤資料寫入後可透過新增 &lt;code&gt;sign = -1&lt;/code&gt; 的紀錄來抵銷錯誤紀錄&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Event Streaming 去重 (Event Deduplication)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;用來消除重複事件紀錄，例如 Kafka Stream 資料去重&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;資料對帳與版本控制&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;結合版本欄位實現更精細的資料補正與對帳邏輯（推薦使用 &lt;code&gt;VersionedCollapsingMergeTree&lt;/code&gt;）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;與 ReplacingMergeTree 的差異比較&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;CollapsingMergeTree&lt;/th&gt;
&lt;th&gt;ReplacingMergeTree&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;資料去重方式&lt;/td&gt;
&lt;td&gt;透過 sign 欄位成對抵消&lt;/td&gt;
&lt;td&gt;根據 version 欄位選擇最新資料 (或隨機選擇其中一筆)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;刪除資料處理&lt;/td&gt;
&lt;td&gt;支援邏輯刪除 (&lt;code&gt;sign = -1&lt;/code&gt; 抵消 &lt;code&gt;sign = 1&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;不支援刪除標記，僅能透過覆蓋新版本資料處理&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;使用時機&lt;/td&gt;
&lt;td&gt;邏輯刪除場景、Event Streaming去重、需要補寫刪除資料的場景&lt;/td&gt;
&lt;td&gt;資料需去重但無需刪除紀錄 (如會員資料、訂單資料的版本替換)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;查詢時是否需使用 FINAL 查詢&lt;/td&gt;
&lt;td&gt;是 (否則會查到尚未 collapse 的紀錄)&lt;/td&gt;
&lt;td&gt;是 (否則會查到未去重的紀錄)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;查詢時的 &lt;code&gt;FINAL&lt;/code&gt; 使用與效能注意&lt;/h2&gt;
&lt;p&gt;因為 CollapsingMergeTree 的去重與刪除動作發生於 Merge 階段，在查詢時若需要&lt;strong&gt;立即看到&lt;/strong&gt;最終一致的結果，必須加上 &lt;code&gt;FINAL&lt;/code&gt; 關鍵字：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT * FROM user_actions FINAL;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;code&gt;FINAL&lt;/code&gt; 的效能注意：&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;FINAL&lt;/code&gt; 查詢會強制進行去重計算，若資料量大會顯著增加查詢負擔。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不建議頻繁全表 FINAL 查詢&lt;/strong&gt;，可透過定期執行 &lt;code&gt;OPTIMIZE TABLE ... FINAL&lt;/code&gt; 來維護資料一致性。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;設計範例&lt;/h2&gt;
&lt;h3&gt;Event Streaming 去重場景：&lt;/h3&gt;
&lt;p&gt;假設從 Kafka 接收使用者行為資料，可能會出現重複寫入的情況，設計方式如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE user_events
(
    event_time DateTime,
    user_id UInt64,
    event_type String,
    sign Int8
) ENGINE = CollapsingMergeTree(sign)
PARTITION BY toYYYYMM(event_time)
ORDER BY (user_id, event_time);
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;資料寫入時，重複事件會有一筆 &lt;code&gt;sign = -1&lt;/code&gt; 紀錄用於去重。&lt;/li&gt;
&lt;li&gt;定期執行 OPTIMIZE 確保資料正確性。&lt;/li&gt;
&lt;li&gt;查詢時如無法等到合併完成，可針對小範圍使用 &lt;code&gt;FINAL&lt;/code&gt; 查詢。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Best Practice&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;最佳實踐&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;只對需要邏輯刪除的場景使用 CollapsingMergeTree&lt;/td&gt;
&lt;td&gt;若無此需求，ReplacingMergeTree 更簡單有效&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;設計 Primary Key 以「能唯一識別紀錄」為原則&lt;/td&gt;
&lt;td&gt;避免設計過度細碎導致去重無效&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;結合 Partition Key 做分區裁剪&lt;/td&gt;
&lt;td&gt;分區裁剪能有效減少 Merge 範圍，降低去重時的資源消耗&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;查詢大表時應避免全表 FINAL&lt;/td&gt;
&lt;td&gt;可將資料區塊切小後僅對特定 Partition FINAL 查詢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;若需版本控制應考慮 VersionedCollapsingMergeTree&lt;/td&gt;
&lt;td&gt;可處理更複雜的資料去重與版本變更場景&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;結語&lt;/h2&gt;
&lt;p&gt;CollapsingMergeTree 是 ClickHouse 在處理事件去重、邏輯刪除場景下的利器。透過 &lt;code&gt;sign&lt;/code&gt; 欄位標記資料狀態，搭配合適的 Primary Key 設計與背景合併策略，能夠高效實現軟刪除與資料去重邏輯。然而，對於查詢一致性需求高的場景，必須謹慎設計 FINAL 查詢策略與定期優化維護作業，以避免效能瓶頸。&lt;/p&gt;
&lt;h3&gt;ClickHouse 系列持續更新中:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/what-is-clickhouse/&quot;&gt;ClickHouse 系列：ClickHouse 是什麼？與傳統 OLAP/OLTP 資料庫的差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-column-row-based-storage/&quot;&gt;ClickHouse 系列：ClickHouse 為什麼選擇 Column-based 儲存？講解 Row-based 與 Column-based 的核心差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-engine&quot;&gt;ClickHouse 系列：ClickHouse 儲存引擎 - MergeTree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-compression-skipping-index/&quot;&gt;ClickHouse 系列：壓縮技術與 Data Skipping Indexes 如何大幅加速查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replacingmergetree-deduplication/&quot;&gt;ClickHouse 系列：ReplacingMergeTree 與資料去重機制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-summingmergetree-aggregation/&quot;&gt;ClickHouse 系列：SummingMergeTree 進行資料彙總的應用場景&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-materialized-view/&quot;&gt;ClickHouse 系列：Materialized Views 即時聚合查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-partition-pruning/&quot;&gt;ClickHouse 系列：分區策略與 Partition Pruning 原理解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-primary-sorting-key/&quot;&gt;ClickHouse 系列：Primary Key、Sorting Key 與 Granule 索引運作原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-collapsingmergetree/&quot;&gt;ClickHouse 系列：CollapsingMergeTree 與邏輯刪除的最佳實踐&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-versioned-collapsingmergetree/&quot;&gt;ClickHouse 系列：VersionedCollapsingMergeTree 版本控制與資料衝突解決&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-aggregatingmergetree/&quot;&gt;ClickHouse 系列：AggregatingMergeTree 實時指標統計的進階應用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-distributed-table-architecture/&quot;&gt;ClickHouse 系列：Distributed Table 與分布式查詢架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replication-failover/&quot;&gt;ClickHouse 系列：Replicated Tables 高可用性與零停機升級實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-kafka-data-streaming-pipeline/&quot;&gt;ClickHouse 系列：與 Kafka 整合打造即時 Data Streaming Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-batch-import/&quot;&gt;ClickHouse 系列：批次匯入最佳實踐 (CSV、Parquet、Native Format)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-external-data-integration/&quot;&gt;ClickHouse 系列：ClickHouse 與外部資料源整合（PostgreSQL）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-query-log-explain/&quot;&gt;ClickHouse 系列：如何提升查詢優化？system.query_log 與 EXPLAIN 用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-projections-optimization/&quot;&gt;ClickHouse 系列：Projections 進階查詢加速技術&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-sampling-statistics/&quot;&gt;ClickHouse 系列：Sampling 抽樣查詢與統計技術原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-ttl-storage-management/&quot;&gt;ClickHouse 系列：TTL 資料清理與儲存成本優化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-storage-policies/&quot;&gt;ClickHouse 系列：儲存政策（Storage Policies）與磁碟資源分層策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-schemas-storage-improvement/&quot;&gt;ClickHouse 系列：表格設計與儲存優化細節&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-grafana-dashboard/&quot;&gt;ClickHouse 系列：ClickHouse 系列：整合 Grafana 打造可視化監控&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-select-optimization/&quot;&gt;ClickHouse 系列：查詢優化案例&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-bi-integration/&quot;&gt;ClickHouse 系列：與 BI 工具整合（Power BI）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-cloud-vs-self-host/&quot;&gt;ClickHouse 系列：ClickHouse Cloud 與自建部署的優劣比較&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-security-rbac/&quot;&gt;ClickHouse 系列：資料庫安全性與權限管理（RBAC）實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-operator-kubernates/&quot;&gt;ClickHouse 系列：Kubernetes 部署分散式架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-sourcecode-introduction/&quot;&gt;ClickHouse 系列：從原始碼看 MergeTree 的六大核心機制&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>ClickHouse 系列：Primary Key、Sorting Key 與 Granule 索引運作原理解析</title><link>https://vicwen.app/posts/clickhouse-primary-sorting-key/</link><guid isPermaLink="true">https://vicwen.app/posts/clickhouse-primary-sorting-key/</guid><pubDate>Tue, 12 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在 ClickHouse 的查詢加速機制中，除了 Partition Pruning 進行粗篩外，另一個細緻化資料範圍掃描的關鍵機制就是 &lt;strong&gt;Primary Key (主鍵索引)、Sorting Key (排序鍵)&lt;/strong&gt; 與 &lt;strong&gt;Granule 索引 (粒度索引)&lt;/strong&gt;。&lt;/p&gt;
&lt;h2&gt;Primary Key 是什麼？&lt;/h2&gt;
&lt;p&gt;在 ClickHouse 中，Primary Key 與傳統 OLTP 資料庫中的「唯一鍵 (Unique Constraint)」不同，它 &lt;strong&gt;不保證資料唯一性，也不會自動加索引樹 (如 B-Tree)&lt;/strong&gt;。
ClickHouse 的 Primary Key 是用來 &lt;strong&gt;決定資料在磁碟中的物理排序方式 (Clustered Index)&lt;/strong&gt;，它是 MergeTree 引擎搜尋資料的首要索引依據。&lt;/p&gt;
&lt;h3&gt;特點：&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;決定資料的排序邏輯，並在查詢時作為區塊篩選的依據。&lt;/li&gt;
&lt;li&gt;與 Partition Key 互補，Partition 負責粗篩區塊，Primary Key 決定區塊內的排序與定位。&lt;/li&gt;
&lt;li&gt;可由一或多個欄位組成（ORDER BY 子句指定）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;範例：&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE orders
(
    order_date Date,
    user_id UInt64,
    order_id UInt64,
    amount Float64
) ENGINE = MergeTree
PARTITION BY toYYYYMM(order_date)
ORDER BY (user_id, order_date);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;這裡的 Primary Key 是 &lt;code&gt;(user_id, order_date)&lt;/code&gt;，資料會依此排序寫入磁碟。&lt;/p&gt;
&lt;h2&gt;Sorting Key 是什麼？&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Sorting Key = ORDER BY 子句指定的欄位組合。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;在 ClickHouse，Sorting Key 就是 Primary Key，只是名稱層面上的不同（某些文件會混用這兩個詞）。&lt;/li&gt;
&lt;li&gt;Sorting Key 決定了 Data Part 中資料的物理排序方式，並影響查詢範圍裁剪的效率。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;小結：&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;名稱&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Primary Key&lt;/td&gt;
&lt;td&gt;排序索引 (Clustered Index)，實際儲存時資料排序依據&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sorting Key&lt;/td&gt;
&lt;td&gt;與 Primary Key 同義，但更偏向強調排序邏輯層級的用詞&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Granule (粒度索引) 是什麼？&lt;/h2&gt;
&lt;p&gt;Granule 是 ClickHouse 將資料拆分為查詢時可裁剪最小單位的「資料區塊」。
一個 Granule 會包含數千筆資料 (預設為 8192 rows)，系統會為每個 Granule 儲存該範圍內的 Sorting Key 最小值與最大值 (min-max 索引)。&lt;/p&gt;
&lt;h3&gt;Granule 的查詢流程：&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;查詢時會根據 WHERE 條件比對 Granule 的 min-max 範圍。&lt;/li&gt;
&lt;li&gt;若條件不在該 Granule 範圍內，則直接跳過讀取該 Granule。&lt;/li&gt;
&lt;li&gt;這種跳過稱為 &lt;strong&gt;Primary Key 範圍裁剪 (Primary Key Indexing)&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Granule 的儲存結構：&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Granule ≈ 8192 rows (預設，可調整)&lt;/li&gt;
&lt;li&gt;一個 Data Part 會包含多個 Granule。&lt;/li&gt;
&lt;li&gt;Primary Key 索引是針對 Granule 粒度儲存的 Sparse Index。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Primary Key 範圍裁剪範例&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;SELECT * FROM orders WHERE user_id = 123456 AND order_date &amp;gt;= &apos;2025-08-01&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;根據 Partition Key 判斷哪些 Partition 需要被讀取 (Partition Pruning)。&lt;/li&gt;
&lt;li&gt;進入符合條件的 Partition，根據 Primary Key 索引比對 Granule 範圍：
&lt;ul&gt;
&lt;li&gt;Granule 1：&lt;code&gt;user_id = 123455 ~ 123455&lt;/code&gt; → 跳過&lt;/li&gt;
&lt;li&gt;Granule 2：&lt;code&gt;user_id = 123456 ~ 123456&lt;/code&gt; → 讀取&lt;/li&gt;
&lt;li&gt;Granule 3：&lt;code&gt;user_id = 123457 ~ 123458&lt;/code&gt; → 跳過&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;這種裁剪動作是查詢能夠在 TB 級資料中僅掃描少量資料的關鍵。&lt;/p&gt;
&lt;h2&gt;Primary Key 與 Secondary Index 有何不同？&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;比較項目&lt;/th&gt;
&lt;th&gt;Primary Key (範圍索引)&lt;/th&gt;
&lt;th&gt;Secondary Index (Data Skipping Index)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;運作方式&lt;/td&gt;
&lt;td&gt;資料寫入時排序，查詢時透過 Granule 索引範圍裁剪&lt;/td&gt;
&lt;td&gt;查詢時依欄位值範圍 (min-max / bloom filter) 決定是否讀取&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;查詢效率&lt;/td&gt;
&lt;td&gt;查詢條件若符合排序欄位 → 裁剪效率極佳&lt;/td&gt;
&lt;td&gt;可支援非排序欄位的查詢過濾，但效率不如 Primary Key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;建立方式&lt;/td&gt;
&lt;td&gt;透過 ORDER BY 設定，與 MergeTree 強耦合&lt;/td&gt;
&lt;td&gt;需額外建立 (ALTER TABLE ADD INDEX...)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;適用查詢&lt;/td&gt;
&lt;td&gt;範圍查詢、序列查詢、依排序邏輯為主的查詢&lt;/td&gt;
&lt;td&gt;高基數欄位查詢（如特定 tag、keyword）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Primary Key 設計策略&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;設計策略&lt;/th&gt;
&lt;th&gt;適用場景&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;常查詢範圍條件放最前面&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;例如 user_id、device_id 若常作為 WHERE 條件，應放排序鍵首位&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;從高選擇性到低選擇性排序&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;user_id → event_date，讓 Granule 範圍更集中，裁剪更精準&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;避免將高變異但不查詢的欄位設為排序鍵&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;如 UUID、隨機 hash，排序無助於裁剪，只會造成合併成本上升&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;結合 Partition 與 Sorting Key 設計&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Partition 粗裁剪、Primary Key 精裁剪，讓查詢僅需掃描極小範圍資料&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Sparse Primary Index 運作原理&lt;/h2&gt;
&lt;p&gt;ClickHouse 的 Primary Key 並不是傳統資料庫的全索引（如 B-Tree），而是設計成「&lt;strong&gt;Sparse (稀疏) 索引&lt;/strong&gt;」，它透過 Granule (粒度區塊) 來達到大規模資料快速篩選的效果。&lt;/p&gt;
&lt;h3&gt;運作方式：&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;每個 Granule 只記錄首筆資料的 Primary Key 值&lt;/strong&gt;：例如預設 Granule 粒度為 8192 筆，索引只會紀錄每個 Granule 第一筆資料的主鍵值。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Sparse 索引非常精簡，能完全載入記憶體中&lt;/strong&gt;，即使資料量達到數百億筆，索引仍僅需占用少量記憶體空間。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;每個 MergeTree 的 Data Part 都有獨立 Primary Index&lt;/strong&gt;，查詢時這些索引會分別比對以達到最佳裁剪效果。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;查詢時，ClickHouse 根據 WHERE 條件與 Sparse Primary Index 比對 Granule 範圍&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;條件範圍外的 Granule 會被直接跳過，不進行掃描。&lt;/li&gt;
&lt;li&gt;條件範圍內的 Granule 才會被讀取進行後續篩選。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;查詢加速效果：&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;這種 Sparse 索引結構，能讓查詢只需掃描必要的 Granule，大幅減少 I/O 與記憶體資源消耗，特別是在 TB 級資料量時能明顯感受到查詢延遲的降低。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;如何檢查 Primary Index 是否生效？&lt;/h3&gt;
&lt;p&gt;ClickHouse 提供了幾個實用的指令來協助你檢視索引運作狀況：&lt;/p&gt;
&lt;h4&gt;1. 檢視索引內容：&lt;code&gt;mergeTreeIndex&lt;/code&gt; table function&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;SELECT * FROM mergeTreeIndex(&apos;your_database.your_table&apos;, &apos;primary_key&apos;) LIMIT 10;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;這可以幫助你看到每個 Granule 第一筆資料的 Primary Key 值，了解索引結構。&lt;/p&gt;
&lt;h4&gt;2. 使用 &lt;code&gt;EXPLAIN&lt;/code&gt; 確認索引是否被裁剪：&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;EXPLAIN PLAN SELECT * FROM orders WHERE user_id = 123456;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;若 WHERE 條件與 Primary Key 匹配良好，查詢計劃會顯示「Granule 範圍裁剪」步驟。&lt;/li&gt;
&lt;li&gt;若條件不符 (如查詢非排序欄位)，則無法利用索引進行裁剪。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;3. &lt;code&gt;system.parts&lt;/code&gt; 觀察查詢裁剪統計：&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;SELECT partition, active, rows, bytes_on_disk
FROM system.parts
WHERE table = &apos;orders&apos; AND active;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Granule 粒度調整與效能平衡&lt;/h2&gt;
&lt;h3&gt;調整 Granule 粒度：&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;透過 &lt;code&gt;index_granularity&lt;/code&gt; 設定：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE t (...) ENGINE = MergeTree() ORDER BY ... SETTINGS index_granularity = 4096;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;粒度越小，裁剪效率越高，但會增加索引大小與查詢時的 CPU 負擔。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;粒度越大，索引資料少，CPU 開銷低，但裁剪不精確，I/O 負擔較大。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;建議：&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;大部分場景用預設 8192 即可。&lt;/li&gt;
&lt;li&gt;若查詢條件能精準對應到排序鍵且資料量大時，可考慮調小到 4096 或 2048。&lt;/li&gt;
&lt;li&gt;若查詢為全表掃描或高聚合查詢為主，可將粒度放大提升查詢吞吐量。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Best Practice&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Best Practice&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Primary Key 欄位數量建議 1~3 欄位&lt;/td&gt;
&lt;td&gt;過多欄位會增加排序成本與合併負擔，減少裁剪效果&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;裁剪效率依賴查詢條件與排序鍵的吻合度&lt;/td&gt;
&lt;td&gt;WHERE 條件若能對應到排序鍵首欄位，裁剪效果最佳&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;index_granularity 避免過度微調&lt;/td&gt;
&lt;td&gt;除非有特殊需求，否則不建議大幅修改，預設 8192 通常是性能與資源平衡的最佳值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;結合 Partition 設計分層裁剪查詢&lt;/td&gt;
&lt;td&gt;Partition 負責粗裁剪，Primary Key 精裁剪，能讓 TB 級資料也只需秒級查詢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;可配合 Secondary Index 提升非排序欄位查詢效率&lt;/td&gt;
&lt;td&gt;如需查詢非 Primary Key 欄位 (如 tags)，可搭配 Bloom Filter Index 加速裁剪查詢&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;結語&lt;/h2&gt;
&lt;p&gt;Primary Key 與 Granule 索引是 ClickHouse 能在海量資料中做到毫秒級查詢的核心技術。透過合理設計 Sorting Key、調整粒度、結合 Partition Pruning，能讓資料掃描量降到最小，大幅提升查詢性能。&lt;/p&gt;
&lt;h3&gt;ClickHouse 系列持續更新中:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/what-is-clickhouse/&quot;&gt;ClickHouse 系列：ClickHouse 是什麼？與傳統 OLAP/OLTP 資料庫的差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-column-row-based-storage/&quot;&gt;ClickHouse 系列：ClickHouse 為什麼選擇 Column-based 儲存？講解 Row-based 與 Column-based 的核心差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-engine&quot;&gt;ClickHouse 系列：ClickHouse 儲存引擎 - MergeTree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-compression-skipping-index/&quot;&gt;ClickHouse 系列：壓縮技術與 Data Skipping Indexes 如何大幅加速查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replacingmergetree-deduplication/&quot;&gt;ClickHouse 系列：ReplacingMergeTree 與資料去重機制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-summingmergetree-aggregation/&quot;&gt;ClickHouse 系列：SummingMergeTree 進行資料彙總的應用場景&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-materialized-view/&quot;&gt;ClickHouse 系列：Materialized Views 即時聚合查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-partition-pruning/&quot;&gt;ClickHouse 系列：分區策略與 Partition Pruning 原理解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-primary-sorting-key/&quot;&gt;ClickHouse 系列：Primary Key、Sorting Key 與 Granule 索引運作原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-collapsingmergetree/&quot;&gt;ClickHouse 系列：CollapsingMergeTree 與邏輯刪除的最佳實踐&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-versioned-collapsingmergetree/&quot;&gt;ClickHouse 系列：VersionedCollapsingMergeTree 版本控制與資料衝突解決&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-aggregatingmergetree/&quot;&gt;ClickHouse 系列：AggregatingMergeTree 實時指標統計的進階應用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-distributed-table-architecture/&quot;&gt;ClickHouse 系列：Distributed Table 與分布式查詢架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replication-failover/&quot;&gt;ClickHouse 系列：Replicated Tables 高可用性與零停機升級實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-kafka-data-streaming-pipeline/&quot;&gt;ClickHouse 系列：與 Kafka 整合打造即時 Data Streaming Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-batch-import/&quot;&gt;ClickHouse 系列：批次匯入最佳實踐 (CSV、Parquet、Native Format)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-external-data-integration/&quot;&gt;ClickHouse 系列：ClickHouse 與外部資料源整合（PostgreSQL）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-query-log-explain/&quot;&gt;ClickHouse 系列：如何提升查詢優化？system.query_log 與 EXPLAIN 用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-projections-optimization/&quot;&gt;ClickHouse 系列：Projections 進階查詢加速技術&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-sampling-statistics/&quot;&gt;ClickHouse 系列：Sampling 抽樣查詢與統計技術原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-ttl-storage-management/&quot;&gt;ClickHouse 系列：TTL 資料清理與儲存成本優化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-storage-policies/&quot;&gt;ClickHouse 系列：儲存政策（Storage Policies）與磁碟資源分層策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-schemas-storage-improvement/&quot;&gt;ClickHouse 系列：表格設計與儲存優化細節&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-grafana-dashboard/&quot;&gt;ClickHouse 系列：ClickHouse 系列：整合 Grafana 打造可視化監控&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-select-optimization/&quot;&gt;ClickHouse 系列：查詢優化案例&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-bi-integration/&quot;&gt;ClickHouse 系列：與 BI 工具整合（Power BI）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-cloud-vs-self-host/&quot;&gt;ClickHouse 系列：ClickHouse Cloud 與自建部署的優劣比較&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-security-rbac/&quot;&gt;ClickHouse 系列：資料庫安全性與權限管理（RBAC）實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-operator-kubernates/&quot;&gt;ClickHouse 系列：Kubernetes 部署分散式架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-sourcecode-introduction/&quot;&gt;ClickHouse 系列：從原始碼看 MergeTree 的六大核心機制&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>檔案到底在哪裡？前端專案架構介紹（FSD 和 FBA）</title><link>https://vicwen.app/posts/fsd-fba-structure/</link><guid isPermaLink="true">https://vicwen.app/posts/fsd-fba-structure/</guid><pubDate>Tue, 12 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在前端專案日漸龐大與模組化需求提升的今天，如何設計一個&lt;strong&gt;可擴展、易維護且高內聚的專案架構&lt;/strong&gt;成為開發者的重要課題。本文將以 React 為示範語言，深入解析「FSD (Feature-Sliced Design)」與「Feature-based Architecture」的核心理念與實作方式，並透過實戰範例帶你一步步建立一個具備現代前端架構思維的專案。&lt;/p&gt;
&lt;h2&gt;Feature-based Architecture 與 FSD 的核心理念&lt;/h2&gt;
&lt;h3&gt;1. Feature-based Architecture 是什麼？&lt;/h3&gt;
&lt;p&gt;Feature-based Architecture (以功能為導向的架構) 強調將專案依據 &lt;strong&gt;功能領域 (feature domain)&lt;/strong&gt; 進行劃分，而非依據技術層面 (如 components, hooks, utils) 進行水平切割。其優點包含：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;High Cohesion&lt;/strong&gt;：功能相關的程式碼集中於同一區域，易於理解與修改。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Low Coupling&lt;/strong&gt;：功能模組之間界線清晰，便於團隊協作與重構。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scalable for Large Teams&lt;/strong&gt;：不同開發者可專注於不同 feature，不互相干擾。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. FSD (Feature-Sliced Design)&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://feature-sliced.design/&quot;&gt;FSD&lt;/a&gt; 是 Feature-based Architecture 的進階實踐框架，其提出一套明確的 &lt;strong&gt;層級設計模型 (Layered Model)&lt;/strong&gt; 與 &lt;strong&gt;Slice-by-Feature&lt;/strong&gt; 概念，有興趣可以看看他們的範例 repo。&lt;/p&gt;
&lt;p&gt;::github{repo=&quot;feature-sliced/examples&quot;}&lt;/p&gt;
&lt;p&gt;FSD 將專案劃分為五大層級：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;層級&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;App&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;應用層，專案的入口與全域設定&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Processes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;業務流程層，負責跨 Feature 的複合邏輯&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pages&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;頁面層，將多個 Feature 組合為完整頁面&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Features&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;功能層，封裝具備獨立業務邏輯的單元&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Entities&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;實體層，對應 Domain Model，如 User, Product 等&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Shared&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;共用層，包含 UI 元件、工具函式、設計系統等&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;專案結構&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;src/
├── app/
│   └── App.tsx
├── processes/
│   └── checkout-flow/
├── pages/
│   ├── HomePage/
│   └── ProductPage/
├── features/
│   ├── AddToCart/
│   └── ProductFilter/
├── entities/
│   ├── Cart/
│   └── Product/
├── shared/
│   ├── ui/
│   │   ├── Button/
│   │   └── Modal/
│   ├── lib/
│   └── config/
└── index.tsx
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;各層實作說明：&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;app/&lt;/strong&gt;：全域路由、Provider、Error Boundary 設定等。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;processes/&lt;/strong&gt;：如 checkout 流程整合 AddToCart、OrderForm 等 Feature。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;pages/&lt;/strong&gt;：純粹負責「組裝與版面配置」，不包含邏輯。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;features/&lt;/strong&gt;：如 AddToCart, ProductFilter，封裝功能邏輯與 UI。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;entities/&lt;/strong&gt;：如 Cart, Product，僅負責 Domain Entity 的資料與操作邏輯。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;shared/&lt;/strong&gt;：全域共用資源 (UI library, 工具函式, API handler)。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;實戰步驟：用 FSD 架構開發「商品加入購物車」功能&lt;/h2&gt;
&lt;h3&gt;Step 1: 定義 Entities (Product, Cart)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;// entities/Product/model/types.ts
export interface Product {
  id: string;
  name: string;
  price: number;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;// entities/Cart/model/cartStore.ts
import { create } from &apos;zustand&apos;;

interface CartItem {
  productId: string;
  quantity: number;
}

interface CartState {
  items: CartItem[];
  addItem: (productId: string) =&amp;gt; void;
}

export const useCartStore = create&amp;lt;CartState&amp;gt;((set) =&amp;gt; ({
  items: [],
  addItem: (productId) =&amp;gt; set((state) =&amp;gt; ({
    items: [...state.items, { productId, quantity: 1 }]
  }))
}));
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Step 2: 建立 Feature - AddToCart&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;// features/AddToCart/ui/AddToCartButton.tsx
import { useCartStore } from &apos;@/entities/Cart&apos;;
import { Product } from &apos;@/entities/Product&apos;;

export const AddToCartButton = ({ product }: { product: Product }) =&amp;gt; {
  const addItem = useCartStore((state) =&amp;gt; state.addItem);
  return &amp;lt;button onClick={() =&amp;gt; addItem(product.id)}&amp;gt;加入購物車&amp;lt;/button&amp;gt;;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Step 3: 在 Page 組裝&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;// pages/ProductPage/ui/ProductPage.tsx
import { Product } from &apos;@/entities/Product&apos;;
import { AddToCartButton } from &apos;@/features/AddToCart&apos;;

const dummyProduct: Product = {
  id: &apos;1&apos;,
  name: &apos;測試商品&apos;,
  price: 1000,
};

export const ProductPage = () =&amp;gt; (
  &amp;lt;div&amp;gt;
    &amp;lt;h1&amp;gt;{dummyProduct.name}&amp;lt;/h1&amp;gt;
    &amp;lt;p&amp;gt;{dummyProduct.price} 元&amp;lt;/p&amp;gt;
    &amp;lt;AddToCartButton product={dummyProduct} /&amp;gt;
  &amp;lt;/div&amp;gt;
);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Step 4: Processes - CheckoutFlow&lt;/h3&gt;
&lt;p&gt;Processes 層會將多個 Feature 組合成跨域業務流程 (設計進階的結帳流程整合 AddToCart, OrderForm, PaymentStep)。&lt;/p&gt;
&lt;h2&gt;FSD 與 Feature-based Architecture 的實務效益&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;優點&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;模組邊界清晰&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Feature 層封裝業務邏輯，Entities 層聚焦 Domain Model，易於解耦與測試&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;適合大型專案與多人協作&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;不同開發者可專注於各自負責的 Slice，降低衝突&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;靈活擴展與重構&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;功能層級的組裝結構，讓你能快速重組頁面流程或替換 Feature 邏輯&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Testable &amp;amp; Maintainable&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Feature 與 Entities 的高內聚設計，自然提升測試與維護效率&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;最佳實踐與常見陷阱&lt;/h2&gt;
&lt;h3&gt;最佳實踐&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;從 Slice-by-Feature 思維出發&lt;/strong&gt;，而非技術導向拆分 (ex: 把 components, hooks 平鋪在 shared/ 是反模式)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Entities 層只做資料與純邏輯，UI 與 SideEffect 交給 Feature 層&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Process 層適度使用&lt;/strong&gt;，避免過度抽象，但複雜業務流程務必導入。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;常見陷阱&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;陷阱&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;目錄層級過深&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;FSD 並不強制目錄層層嵌套，保持簡潔與實用為主&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;把 Feature 當作 UI Library 用&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Feature 應封裝業務邏輯，不只是 UI Component 組合&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Process 層過度設計&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;當流程不夠複雜時不需要勉強使用 Processes 層&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;結語&lt;/h2&gt;
&lt;p&gt;FSD 與 Feature-based Architecture 是應對大型 React 專案複雜度提升的強大工具。其「&lt;strong&gt;功能導向 + 層級分明&lt;/strong&gt;」的設計哲學，能讓專案更具可讀性、可維護性與可擴展性。&lt;/p&gt;
</content:encoded></item><item><title>ClickHouse 系列：分區策略與 Partition Pruning 技術，如何加速大數據查詢</title><link>https://vicwen.app/posts/clickhouse-partition-pruning/</link><guid isPermaLink="true">https://vicwen.app/posts/clickhouse-partition-pruning/</guid><pubDate>Mon, 11 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;當面對數億、數十億筆資料時，若每次查詢都必須掃描全表，效率勢必崩潰。ClickHouse 提供了靈活的 &lt;strong&gt;分區 (Partitioning)&lt;/strong&gt; 與 &lt;strong&gt;Partition Pruning (分區裁剪)&lt;/strong&gt; 技術，讓你在查詢時僅需掃描「真正相關的資料區塊」，大幅減少 I/O 與查詢延遲。&lt;/p&gt;
&lt;h2&gt;什麼是 Partition（分區）？&lt;/h2&gt;
&lt;p&gt;在 ClickHouse 中，&lt;strong&gt;Partition 是一種邏輯資料切分單位&lt;/strong&gt;，資料會根據指定的 Partition Key (表達式) 被切分成獨立的資料區塊（目錄），這些區塊在查詢時可以根據條件篩選，避免全表掃描。&lt;/p&gt;
&lt;h3&gt;重要特性：&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Partition 是 MergeTree 引擎的核心結構之一。&lt;/li&gt;
&lt;li&gt;Partition Key 可以是任意表達式（如 toYYYYMM(date)、&lt;code&gt;device_id&lt;/code&gt;、&lt;code&gt;region_id&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;Partition 是「物理檔案目錄」級別的切分，對查詢優化效果非常明顯。&lt;/li&gt;
&lt;li&gt;Partition 切分範圍越小，查詢時能跳過的資料越多，但會增加小檔案數量與合併負擔。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Partition 與 Primary Key 的差異&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;比較項目&lt;/th&gt;
&lt;th&gt;Partition&lt;/th&gt;
&lt;th&gt;Primary Key&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;作用層級&lt;/td&gt;
&lt;td&gt;資料目錄切分 (磁碟層級)&lt;/td&gt;
&lt;td&gt;區塊內排序索引 (內部儲存層級)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;查詢裁剪&lt;/td&gt;
&lt;td&gt;查詢條件符合 Partition Key 時可直接跳過該區塊&lt;/td&gt;
&lt;td&gt;查詢條件符合 Primary Key 時可精確定位資料區塊&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;設計依據&lt;/td&gt;
&lt;td&gt;常用範圍查詢條件，如日期、業務區域&lt;/td&gt;
&lt;td&gt;常用細粒度查詢條件，如 &lt;code&gt;user_id&lt;/code&gt;、&lt;code&gt;order_id&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;兩者是互補關係，Partition 用於粗略範圍裁剪、Primary Key 用於精細定位。&lt;/p&gt;
&lt;h2&gt;Partition Pruning（分區裁剪）原理&lt;/h2&gt;
&lt;p&gt;Partition Pruning 是 ClickHouse 在查詢時根據 WHERE 條件，自動判斷哪些 Partition 是不可能有資料的，並直接跳過讀取這些資料區塊。&lt;/p&gt;
&lt;h3&gt;範例：&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE page_views
(
    event_date Date,
    user_id UInt64,
    page String
) ENGINE = MergeTree
PARTITION BY toYYYYMM(event_date)
ORDER BY (event_date, user_id);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;查詢：&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;SELECT count() FROM page_views
WHERE event_date &amp;gt;= &apos;2025-08-01&apos; AND event_date &amp;lt; &apos;2025-08-02&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;ClickHouse 會根據 &lt;code&gt;toYYYYMM(event_date)&lt;/code&gt; 的 Partition Key 判斷，只有 202508 分區有可能符合條件，其他分區會被直接跳過，不會進行掃描。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Partition Key 設計策略&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;設計策略&lt;/th&gt;
&lt;th&gt;適用場景&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;以時間維度切分 (toYYYYMM, toYYYYMMDD)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;時間序列資料、日誌系統、流量監控 (依查詢頻率決定切分粒度)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;業務維度切分 (&lt;code&gt;region_id&lt;/code&gt;, &lt;code&gt;device_type&lt;/code&gt;)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;不同地區、設備類型獨立查詢的場景 (適合分片架構與查詢負載均衡)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;複合分區 (如 toYYYYMM(date), &lt;code&gt;region_id&lt;/code&gt;)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;時間 + 區域等複合查詢場景，但需小心 Partition 數量過多問題&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;高變異欄位避免作為分區&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;如 user_id、UUID 這類高基數欄位，不適合作為 Partition Key，否則會造成大量小檔案問題&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Partition 粒度與效率平衡&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;粒度設計&lt;/th&gt;
&lt;th&gt;優點&lt;/th&gt;
&lt;th&gt;缺點&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;toYYYYMM&lt;/td&gt;
&lt;td&gt;分區數量適中，適合查詢數月資料&lt;/td&gt;
&lt;td&gt;跨月查詢效率普通，資料寫入後合併頻率較高&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;toYYYYMMDD&lt;/td&gt;
&lt;td&gt;可將查詢裁剪精度提高至日，適合日誌系統或即時查詢&lt;/td&gt;
&lt;td&gt;會產生大量小 Part，增加磁碟 IOPS 與背景合併負擔&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;複合分區 (時間 + 維度)&lt;/td&gt;
&lt;td&gt;分區粒度最細，查詢裁剪效果極佳&lt;/td&gt;
&lt;td&gt;小心分區數量爆炸（建議 Partition Key 不超過 10,000 個）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;查詢如何確認 Partition Pruning 是否生效？&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;使用 &lt;code&gt;EXPLAIN&lt;/code&gt;&lt;/strong&gt;：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;EXPLAIN PLAN SELECT * FROM page_views WHERE event_date = &apos;2025-08-01&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;查詢分區讀取統計 (&lt;code&gt;system.parts&lt;/code&gt;)&lt;/strong&gt;：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;SELECT
    partition,
    active,
    rows,
    bytes_on_disk
FROM system.parts
WHERE table = &apos;page_views&apos; AND active;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;觀察查詢 profile events&lt;/strong&gt;：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;SET send_logs_level = &apos;trace&apos;;
SELECT count() FROM page_views WHERE event_date = &apos;2025-08-01&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在查詢 Profile Events 中可以看到 &lt;code&gt;read_rows&lt;/code&gt; 與 &lt;code&gt;read_bytes&lt;/code&gt; 是否明顯降低。&lt;/p&gt;
&lt;h2&gt;實際案例&lt;/h2&gt;
&lt;p&gt;假設一個月 30 億筆網站點擊資料，未使用分區時查詢單日流量需掃描全表，I/O 延遲數十秒。若將 Partition Key 設為 toYYYYMMDD (&lt;code&gt;event_date&lt;/code&gt;)：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;單日查詢只需掃描 1/30 的資料量。&lt;/li&gt;
&lt;li&gt;查詢延遲可從 30 秒降至 1 秒以內。&lt;/li&gt;
&lt;li&gt;結合 Primary Key (如 &lt;code&gt;page&lt;/code&gt;, &lt;code&gt;user_id&lt;/code&gt;) 可進一步精確篩選。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Partition 與分片 (Sharding) 的關係&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Partition (分區)&lt;/th&gt;
&lt;th&gt;Sharding (分片)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;將單一表內的資料切分為多個資料區塊，便於裁剪查詢範圍&lt;/td&gt;
&lt;td&gt;將整張表橫向切分到不同節點（叢集）上，提升分散式計算能力&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;分區僅影響單表查詢時的掃描範圍&lt;/td&gt;
&lt;td&gt;分片影響資料的存放位置與分散式查詢執行路徑&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;分區裁剪靠 WHERE 條件判斷，查詢時減少掃描資料量&lt;/td&gt;
&lt;td&gt;分片裁剪靠分片鍵與分布式查詢路由規則，決定哪些節點需參與查詢&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;兩者可結合設計，例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;分片鍵 = &lt;code&gt;user_id&lt;/code&gt;（負載均衡分散寫入）&lt;/li&gt;
&lt;li&gt;分區鍵 = toYYYYMM(date)（加速範圍查詢）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;最佳實踐與注意事項&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;最佳實踐&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;分區 Key 依據查詢模式設計&lt;/td&gt;
&lt;td&gt;針對最常用的 WHERE 範圍條件（如日期、地區）設計分區裁剪維度&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;小心 Partition 數量爆炸&lt;/td&gt;
&lt;td&gt;避免選用高基數欄位作為分區鍵，Partition 數量建議控制在數千 ~ 一萬以內&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;使用 system.parts 監控分區健康度&lt;/td&gt;
&lt;td&gt;定期檢查 Active Parts 數量與大小，避免過多小檔案影響合併性能&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;結合 Primary Key 設計精細資料定位&lt;/td&gt;
&lt;td&gt;分區負責粗篩，Primary Key 負責精細定位，兩者結合達到最佳查詢效率&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;不建議頻繁更改 Partition Key&lt;/td&gt;
&lt;td&gt;Partition Key 變更等同於重建表，需謹慎設計與評估&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;結語&lt;/h2&gt;
&lt;p&gt;Partition Pruning 是 ClickHouse 面對大量資料時的查詢加速神器。透過合理設計分區策略，不僅能減少 I/O 負擔，更能讓你的 OLAP 報表與即時查詢在 TB 級資料量下依然保持 ms 級回應。&lt;/p&gt;
&lt;h3&gt;ClickHouse 系列持續更新中:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/what-is-clickhouse/&quot;&gt;ClickHouse 系列：ClickHouse 是什麼？與傳統 OLAP/OLTP 資料庫的差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-column-row-based-storage/&quot;&gt;ClickHouse 系列：ClickHouse 為什麼選擇 Column-based 儲存？講解 Row-based 與 Column-based 的核心差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-engine&quot;&gt;ClickHouse 系列：ClickHouse 儲存引擎 - MergeTree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-compression-skipping-index/&quot;&gt;ClickHouse 系列：壓縮技術與 Data Skipping Indexes 如何大幅加速查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replacingmergetree-deduplication/&quot;&gt;ClickHouse 系列：ReplacingMergeTree 與資料去重機制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-summingmergetree-aggregation/&quot;&gt;ClickHouse 系列：SummingMergeTree 進行資料彙總的應用場景&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-materialized-view/&quot;&gt;ClickHouse 系列：Materialized Views 即時聚合查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-partition-pruning/&quot;&gt;ClickHouse 系列：分區策略與 Partition Pruning 原理解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-primary-sorting-key/&quot;&gt;ClickHouse 系列：Primary Key、Sorting Key 與 Granule 索引運作原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-collapsingmergetree/&quot;&gt;ClickHouse 系列：CollapsingMergeTree 與邏輯刪除的最佳實踐&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-versioned-collapsingmergetree/&quot;&gt;ClickHouse 系列：VersionedCollapsingMergeTree 版本控制與資料衝突解決&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-aggregatingmergetree/&quot;&gt;ClickHouse 系列：AggregatingMergeTree 實時指標統計的進階應用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-distributed-table-architecture/&quot;&gt;ClickHouse 系列：Distributed Table 與分布式查詢架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replication-failover/&quot;&gt;ClickHouse 系列：Replicated Tables 高可用性與零停機升級實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-kafka-data-streaming-pipeline/&quot;&gt;ClickHouse 系列：與 Kafka 整合打造即時 Data Streaming Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-batch-import/&quot;&gt;ClickHouse 系列：批次匯入最佳實踐 (CSV、Parquet、Native Format)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-external-data-integration/&quot;&gt;ClickHouse 系列：ClickHouse 與外部資料源整合（PostgreSQL）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-query-log-explain/&quot;&gt;ClickHouse 系列：如何提升查詢優化？system.query_log 與 EXPLAIN 用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-projections-optimization/&quot;&gt;ClickHouse 系列：Projections 進階查詢加速技術&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-sampling-statistics/&quot;&gt;ClickHouse 系列：Sampling 抽樣查詢與統計技術原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-ttl-storage-management/&quot;&gt;ClickHouse 系列：TTL 資料清理與儲存成本優化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-storage-policies/&quot;&gt;ClickHouse 系列：儲存政策（Storage Policies）與磁碟資源分層策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-schemas-storage-improvement/&quot;&gt;ClickHouse 系列：表格設計與儲存優化細節&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-grafana-dashboard/&quot;&gt;ClickHouse 系列：ClickHouse 系列：整合 Grafana 打造可視化監控&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-select-optimization/&quot;&gt;ClickHouse 系列：查詢優化案例&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-bi-integration/&quot;&gt;ClickHouse 系列：與 BI 工具整合（Power BI）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-cloud-vs-self-host/&quot;&gt;ClickHouse 系列：ClickHouse Cloud 與自建部署的優劣比較&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-security-rbac/&quot;&gt;ClickHouse 系列：資料庫安全性與權限管理（RBAC）實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-operator-kubernates/&quot;&gt;ClickHouse 系列：Kubernetes 部署分散式架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-sourcecode-introduction/&quot;&gt;ClickHouse 系列：從原始碼看 MergeTree 的六大核心機制&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>ClickHouse 系列：Materialized Views 即時聚合查詢</title><link>https://vicwen.app/posts/clickhouse-materialized-view/</link><guid isPermaLink="true">https://vicwen.app/posts/clickhouse-materialized-view/</guid><pubDate>Sun, 10 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在 OLAP 系統中，「即時聚合」與「預先計算」是加速查詢、降低資源消耗的核心策略。ClickHouse 提供了強大的 &lt;strong&gt;Materialized Views (物化視圖)&lt;/strong&gt;，能將複雜查詢結果實時寫入表中，並大幅減輕查詢時的運算壓力。&lt;/p&gt;
&lt;h2&gt;什麼是 Materialized View？&lt;/h2&gt;
&lt;p&gt;Materialized View 是一種 &lt;strong&gt;帶有持久化儲存結果的查詢視圖&lt;/strong&gt;，當有資料寫入源表 (source table) 時，ClickHouse 會自動根據定義的 SELECT 查詢語句計算並將結果寫入目標表 (target table)。&lt;/p&gt;
&lt;p&gt;簡單來說，它就是一種「&lt;strong&gt;自動觸發的 Insert + 聚合查詢 + 寫入彙總表&lt;/strong&gt;」的機制。&lt;/p&gt;
&lt;h3&gt;特點：&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;INSERT 寫入源表時，自動將計算結果寫入目標表。&lt;/li&gt;
&lt;li&gt;目標表通常是 &lt;code&gt;SummingMergeTree&lt;/code&gt; / &lt;code&gt;AggregatingMergeTree&lt;/code&gt; 等。&lt;/li&gt;
&lt;li&gt;只會計算「新寫入資料」的查詢結果，不會對舊資料重新掃描。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;基本語法與範例&lt;/h2&gt;
&lt;h3&gt;1. 建立 Source Table (Raw Data Table)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE events
(
    event_time DateTime,
    page String,
    user_id UInt64,
    views UInt32
) ENGINE = MergeTree
ORDER BY (event_time, page);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2. 建立 Target Table (Aggregate Table)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE daily_page_views
(
    date Date,
    page String,
    total_views UInt32
) ENGINE = SummingMergeTree
PARTITION BY toYYYYMM(date)
ORDER BY (date, page);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3. 建立 Materialized View&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;CREATE MATERIALIZED VIEW mv_daily_page_views
TO daily_page_views
AS SELECT
    toDate(event_time) AS date,
    page,
    sum(views) AS total_views
FROM events
GROUP BY date, page;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;每當資料寫入 &lt;code&gt;events&lt;/code&gt; 表時，ClickHouse 會自動計算每天每頁面的瀏覽量，並寫入 &lt;code&gt;daily_page_views&lt;/code&gt; 表中。&lt;/p&gt;
&lt;h2&gt;運作流程&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;INSERT → &lt;code&gt;events&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Materialized View 觸發 SELECT + 聚合運算&lt;/li&gt;
&lt;li&gt;將結果 INSERT 進 &lt;code&gt;daily_page_views&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;查詢 &lt;code&gt;daily_page_views&lt;/code&gt; 時直接取用彙總結果&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;應用場景&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;應用場景&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;高頻查詢彙總結果表 (Dashboard/BI 報表)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;先將重計算的聚合結果寫入目標表，查詢時僅需掃小型表格&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;即時Event Streaming彙總 (如 PV/UV 統計)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;結合 Kafka + MV，即時統計點擊數、瀏覽量等&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;指標統計與彙總 (Metrics Storage)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;以 Materialized View 實時計算指標資料，適合 IoT、監控平台&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ReplacingMergeTree 或 SummingMergeTree 結合&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;目標表可以使用去重、聚合引擎進一步優化結果儲存&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Materialized View 設計重點與限制&lt;/h2&gt;
&lt;h3&gt;1. &lt;strong&gt;目標表 (TO Table) 必須先存在&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Materialized View 是寫入目標表的「Trigger」，Target Table 必須先建立好。&lt;/p&gt;
&lt;h3&gt;2. &lt;strong&gt;只計算新增資料&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;MV 只會針對「新寫入」的資料進行聚合，不會針對舊資料補算，若源表有歷史資料變動需手動重算。&lt;/p&gt;
&lt;h3&gt;3. &lt;strong&gt;INSERT 觸發查詢，查詢性能依賴目標表設計&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;目標表應搭配適當的 MergeTree 引擎（&lt;code&gt;Summing&lt;/code&gt;, &lt;code&gt;Aggregating&lt;/code&gt;, &lt;code&gt;Replacing&lt;/code&gt;）來對應資料處理需求。&lt;/p&gt;
&lt;h3&gt;4. &lt;strong&gt;無法直接支援 UPDATE/DELETE&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;MV 只會針對 INSERT 事件觸發，若需進行補數據、資料刪除，需配合 &lt;code&gt;ReplacingMergeTree&lt;/code&gt; 或 Mutation 處理。&lt;/p&gt;
&lt;h2&gt;進階：POPULATE 一次性計算歷史資料&lt;/h2&gt;
&lt;p&gt;若 Materialized View 建立時希望自動將源表的歷史資料也計算入目標表，可以使用 &lt;code&gt;POPULATE&lt;/code&gt; 關鍵字。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE MATERIALIZED VIEW mv_daily_page_views POPULATE
TO daily_page_views
AS SELECT
    toDate(event_time) AS date,
    page,
    sum(views) AS total_views
FROM events
GROUP BY date, page;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;注意：&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;POPULATE 只在建立時執行一次，之後不再作用。&lt;/li&gt;
&lt;li&gt;若源表有新歷史資料寫入，需要自行做補數據操作。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Materialized View vs View vs LIVE View 差異&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;類型&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;th&gt;主要用途&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Materialized View&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;INSERT 時將查詢結果實體化寫入表&lt;/td&gt;
&lt;td&gt;即時聚合、數據彙總&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;View (普通 View)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;查詢時執行 SELECT，無數據儲存&lt;/td&gt;
&lt;td&gt;簡化複雜查詢語法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LIVE View&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;基於資料更新自動推送查詢結果 (類似 Streaming Query)&lt;/td&gt;
&lt;td&gt;即時查詢動態數據（但性能較重，不建議大數據場景使用）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;最佳實踐與效能建議&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;實踐策略&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;搭配 SummingMergeTree 做高效加總表&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;透過 MV 將大量原始數據彙總到小表，提高查詢性能&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Materialized View 支援多層級彙總&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;可以鏈接多個 MV 形成逐層聚合 (日 → 週 → 月)，減少查詢即時計算量&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;針對大表建 MV 時善用 Partition Key&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;讓彙總結果根據日期或業務維度分區，減少寫入與查詢時的 I/O 開銷&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;小心 Insert 負擔&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;每次寫入都會觸發一次子查詢，若 MV 設計過於複雜會影響寫入效能&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;與 Kafka Engine 整合，實現流式彙總&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;MV 可直接從 Kafka Source 自動消費並寫入聚合結果表&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;結語&lt;/h2&gt;
&lt;p&gt;Materialized View 提供了一種「自動計算、實時寫入」的聚合機制，讓 ClickHouse 能在高寫入吞吐量下依然保持查詢效能。透過合理設計源表、目標表、聚合邏輯與表引擎選擇，Materialized View 可成為數據分析場景中的效能加速器。&lt;/p&gt;
&lt;h3&gt;ClickHouse 系列持續更新中:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/what-is-clickhouse/&quot;&gt;ClickHouse 系列：ClickHouse 是什麼？與傳統 OLAP/OLTP 資料庫的差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-column-row-based-storage/&quot;&gt;ClickHouse 系列：ClickHouse 為什麼選擇 Column-based 儲存？講解 Row-based 與 Column-based 的核心差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-engine&quot;&gt;ClickHouse 系列：ClickHouse 儲存引擎 - MergeTree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-compression-skipping-index/&quot;&gt;ClickHouse 系列：壓縮技術與 Data Skipping Indexes 如何大幅加速查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replacingmergetree-deduplication/&quot;&gt;ClickHouse 系列：ReplacingMergeTree 與資料去重機制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-summingmergetree-aggregation/&quot;&gt;ClickHouse 系列：SummingMergeTree 進行資料彙總的應用場景&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-materialized-view/&quot;&gt;ClickHouse 系列：Materialized Views 即時聚合查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-partition-pruning/&quot;&gt;ClickHouse 系列：分區策略與 Partition Pruning 原理解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-primary-sorting-key/&quot;&gt;ClickHouse 系列：Primary Key、Sorting Key 與 Granule 索引運作原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-collapsingmergetree/&quot;&gt;ClickHouse 系列：CollapsingMergeTree 與邏輯刪除的最佳實踐&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-versioned-collapsingmergetree/&quot;&gt;ClickHouse 系列：VersionedCollapsingMergeTree 版本控制與資料衝突解決&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-aggregatingmergetree/&quot;&gt;ClickHouse 系列：AggregatingMergeTree 實時指標統計的進階應用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-distributed-table-architecture/&quot;&gt;ClickHouse 系列：Distributed Table 與分布式查詢架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replication-failover/&quot;&gt;ClickHouse 系列：Replicated Tables 高可用性與零停機升級實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-kafka-data-streaming-pipeline/&quot;&gt;ClickHouse 系列：與 Kafka 整合打造即時 Data Streaming Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-batch-import/&quot;&gt;ClickHouse 系列：批次匯入最佳實踐 (CSV、Parquet、Native Format)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-external-data-integration/&quot;&gt;ClickHouse 系列：ClickHouse 與外部資料源整合（PostgreSQL）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-query-log-explain/&quot;&gt;ClickHouse 系列：如何提升查詢優化？system.query_log 與 EXPLAIN 用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-projections-optimization/&quot;&gt;ClickHouse 系列：Projections 進階查詢加速技術&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-sampling-statistics/&quot;&gt;ClickHouse 系列：Sampling 抽樣查詢與統計技術原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-ttl-storage-management/&quot;&gt;ClickHouse 系列：TTL 資料清理與儲存成本優化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-storage-policies/&quot;&gt;ClickHouse 系列：儲存政策（Storage Policies）與磁碟資源分層策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-schemas-storage-improvement/&quot;&gt;ClickHouse 系列：表格設計與儲存優化細節&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-grafana-dashboard/&quot;&gt;ClickHouse 系列：ClickHouse 系列：整合 Grafana 打造可視化監控&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-select-optimization/&quot;&gt;ClickHouse 系列：查詢優化案例&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-bi-integration/&quot;&gt;ClickHouse 系列：與 BI 工具整合（Power BI）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-cloud-vs-self-host/&quot;&gt;ClickHouse 系列：ClickHouse Cloud 與自建部署的優劣比較&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-security-rbac/&quot;&gt;ClickHouse 系列：資料庫安全性與權限管理（RBAC）實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-operator-kubernates/&quot;&gt;ClickHouse 系列：Kubernetes 部署分散式架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-sourcecode-introduction/&quot;&gt;ClickHouse 系列：從原始碼看 MergeTree 的六大核心機制&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>ClickHouse 系列：SummingMergeTree 進行資料彙總的應用場景</title><link>https://vicwen.app/posts/clickhouse-summingmergetree-aggregation/</link><guid isPermaLink="true">https://vicwen.app/posts/clickhouse-summingmergetree-aggregation/</guid><pubDate>Sat, 09 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;有一天你工作工作，在當社畜的時候，發現你現在需要大量的「數值加總」、「分組彙總統計」，例如每日活躍使用者數量、每小時流量統計、即時計數器 (Counter) 等，ClickHouse 提供了一個極致高效的資料彙總利器 —— &lt;strong&gt;SummingMergeTree&lt;/strong&gt;。&lt;/p&gt;
&lt;h2&gt;SummingMergeTree 是什麼？&lt;/h2&gt;
&lt;p&gt;SummingMergeTree 是 ClickHouse MergeTree 系列的引擎變種，具備在資料合併 (Merge) 階段，自動將具有相同 &lt;code&gt;Primary Key&lt;/code&gt; 的數值欄位進行加總 (SUM)，幫助你快速構建高效能的預彙總表 (Pre-Aggregated Table)。&lt;/p&gt;
&lt;h3&gt;特點：&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;自動數值欄位加總&lt;/strong&gt;：相同 Primary Key 的記錄在 Merge 時會自動執行加總。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;非即時性彙總&lt;/strong&gt;：數值加總發生於背景 Merge 階段。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;無需寫聚合函數邏輯&lt;/strong&gt;，簡單定義即可。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;SummingMergeTree 的語法與範例&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE daily_metrics
(
    date Date,
    page String,
    views UInt32,
    clicks UInt32
) ENGINE = SummingMergeTree
PARTITION BY toYYYYMM(date)
ORDER BY (date, page);
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;所有 &lt;strong&gt;數值型欄位 (Numeric Types)&lt;/strong&gt; 預設皆會參與加總。&lt;/li&gt;
&lt;li&gt;字串欄位僅參與 GROUP BY，不會被彙總。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;INSERT INTO daily_metrics VALUES (&apos;2025-08-01&apos;, &apos;Home&apos;, 100, 10);
INSERT INTO daily_metrics VALUES (&apos;2025-08-01&apos;, &apos;Home&apos;, 200, 25);
INSERT INTO daily_metrics VALUES (&apos;2025-08-01&apos;, &apos;Contact&apos;, 50, 5);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查詢時仍會看到所有資料：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT * FROM daily_metrics WHERE date = &apos;2025-08-01&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;date&lt;/th&gt;
&lt;th&gt;page&lt;/th&gt;
&lt;th&gt;views&lt;/th&gt;
&lt;th&gt;clicks&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2025-08-01&lt;/td&gt;
&lt;td&gt;Home&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2025-08-01&lt;/td&gt;
&lt;td&gt;Home&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2025-08-01&lt;/td&gt;
&lt;td&gt;Contact&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;進行背景 Merge（或強制 Optimize）後：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;OPTIMIZE TABLE daily_metrics FINAL;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;再查詢結果：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;date&lt;/th&gt;
&lt;th&gt;page&lt;/th&gt;
&lt;th&gt;views&lt;/th&gt;
&lt;th&gt;clicks&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2025-08-01&lt;/td&gt;
&lt;td&gt;Home&lt;/td&gt;
&lt;td&gt;300&lt;/td&gt;
&lt;td&gt;35&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2025-08-01&lt;/td&gt;
&lt;td&gt;Contact&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;應用場景&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;場景&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;即時計數器 (Counter)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;用於 API Call 次數、商品瀏覽次數等累加統計場景。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;批次數據預彙總 (Batch Aggregation)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;將大數據表做成 Pre-Aggregated Table，避免即時計算壓力。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;流量/活動數據彙總 (Metrics Aggregation)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;日誌、網站流量統計、使用者行為統計（例如 PV/UV 指標）。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;與 Materialized View 搭配&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;將 Raw Data 資料流寫入 Materialized View，實時聚合彙總結果至 SummingMergeTree 表。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;GROUP BY 規則&lt;/h2&gt;
&lt;p&gt;SummingMergeTree 的去重與彙總邏輯是以 &lt;strong&gt;Primary Key 欄位為基準&lt;/strong&gt;，並對數值欄位進行加總。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ORDER BY 欄位 = GROUP BY Key&lt;/li&gt;
&lt;li&gt;數值型欄位 (Int, Float) 才會自動 SUM&lt;/li&gt;
&lt;li&gt;字串、日期等欄位僅參與分組，不會被聚合&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;若 &lt;code&gt;Primary Key&lt;/code&gt; 欄位設計錯誤（例如太細緻或包含非必要欄位），將導致彙總效果失效，無法正確合併統計數據。&lt;/p&gt;
&lt;h2&gt;進階：Materialized View 實時聚合&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE raw_events
(
    event_time DateTime,
    page String,
    views UInt32,
    clicks UInt32
) ENGINE = MergeTree
ORDER BY (event_time, page);

CREATE MATERIALIZED VIEW mv_daily_metrics
TO daily_metrics
AS SELECT
    toDate(event_time) AS date,
    page,
    sum(views) AS views,
    sum(clicks) AS clicks
FROM raw_events
GROUP BY date, page;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;這樣每次寫入 &lt;code&gt;raw_events&lt;/code&gt; 時，ClickHouse 會即時計算彙總結果並寫入 &lt;code&gt;daily_metrics (SummingMergeTree)&lt;/code&gt;。&lt;/p&gt;
&lt;h2&gt;SummingMergeTree 與 AggregatingMergeTree 差異&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;SummingMergeTree&lt;/th&gt;
&lt;th&gt;AggregatingMergeTree&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;支援的聚合函數&lt;/td&gt;
&lt;td&gt;只支援 SUM（數值欄位）&lt;/td&gt;
&lt;td&gt;支援所有 AggregateFunction (SUM, AVG, COUNT, MIN, MAX)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;聚合邏輯&lt;/td&gt;
&lt;td&gt;背景 Merge 時加總&lt;/td&gt;
&lt;td&gt;需配合 AggregateFunction 資料型別與聚合函數進行計算&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;設計複雜度&lt;/td&gt;
&lt;td&gt;簡單 (適用於計數器、單純加總)&lt;/td&gt;
&lt;td&gt;較複雜，適用於指標統計、分位數、去重統計等&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;寫入性能&lt;/td&gt;
&lt;td&gt;較高（無需特殊型別）&lt;/td&gt;
&lt;td&gt;較低（需轉換為 AggregateFunction 型別資料寫入）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;最佳實踐與注意事項&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Primary Key 設計決定彙總效果&lt;/strong&gt;：&lt;code&gt;ORDER BY&lt;/code&gt; 需謹慎設計，只包含用來 &lt;code&gt;GROUP BY&lt;/code&gt; 的欄位。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;數值欄位命名避免衝突&lt;/strong&gt;：非數值欄位不會被加總，但欄位型別設錯或命名混亂會導致統計錯誤。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;資料寫入順序不影響結果&lt;/strong&gt;：Merge 階段才會進行彙總，因此資料流式寫入無需排序。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;定期執行 OPTIMIZE FINAL&lt;/strong&gt;：若查詢即時性要求高，可定期強制執行合併去重，確保查詢返回彙總後的結果。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Materialized View 為即時彙總最佳搭配&lt;/strong&gt;：用以將原始大表流量，實時寫入 &lt;code&gt;SummingMergeTree&lt;/code&gt; 進行高效查詢。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;結語&lt;/h2&gt;
&lt;p&gt;SummingMergeTree 提供了一種簡單卻強大的數據彙總方式，非常適合用於統計計數、流量指標分析與預彙總表場景。透過合理設計 Primary Key 與 Materialized View 的搭配，能讓你的查詢效能成倍提升。&lt;/p&gt;
&lt;h3&gt;ClickHouse 系列持續更新中:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/what-is-clickhouse/&quot;&gt;ClickHouse 系列：ClickHouse 是什麼？與傳統 OLAP/OLTP 資料庫的差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-column-row-based-storage/&quot;&gt;ClickHouse 系列：ClickHouse 為什麼選擇 Column-based 儲存？講解 Row-based 與 Column-based 的核心差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-engine&quot;&gt;ClickHouse 系列：ClickHouse 儲存引擎 - MergeTree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-compression-skipping-index/&quot;&gt;ClickHouse 系列：壓縮技術與 Data Skipping Indexes 如何大幅加速查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replacingmergetree-deduplication/&quot;&gt;ClickHouse 系列：ReplacingMergeTree 與資料去重機制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-summingmergetree-aggregation/&quot;&gt;ClickHouse 系列：SummingMergeTree 進行資料彙總的應用場景&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-materialized-view/&quot;&gt;ClickHouse 系列：Materialized Views 即時聚合查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-partition-pruning/&quot;&gt;ClickHouse 系列：分區策略與 Partition Pruning 原理解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-primary-sorting-key/&quot;&gt;ClickHouse 系列：Primary Key、Sorting Key 與 Granule 索引運作原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-collapsingmergetree/&quot;&gt;ClickHouse 系列：CollapsingMergeTree 與邏輯刪除的最佳實踐&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-versioned-collapsingmergetree/&quot;&gt;ClickHouse 系列：VersionedCollapsingMergeTree 版本控制與資料衝突解決&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-aggregatingmergetree/&quot;&gt;ClickHouse 系列：AggregatingMergeTree 實時指標統計的進階應用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-distributed-table-architecture/&quot;&gt;ClickHouse 系列：Distributed Table 與分布式查詢架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replication-failover/&quot;&gt;ClickHouse 系列：Replicated Tables 高可用性與零停機升級實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-kafka-data-streaming-pipeline/&quot;&gt;ClickHouse 系列：與 Kafka 整合打造即時 Data Streaming Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-batch-import/&quot;&gt;ClickHouse 系列：批次匯入最佳實踐 (CSV、Parquet、Native Format)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-external-data-integration/&quot;&gt;ClickHouse 系列：ClickHouse 與外部資料源整合（PostgreSQL）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-query-log-explain/&quot;&gt;ClickHouse 系列：如何提升查詢優化？system.query_log 與 EXPLAIN 用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-projections-optimization/&quot;&gt;ClickHouse 系列：Projections 進階查詢加速技術&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-sampling-statistics/&quot;&gt;ClickHouse 系列：Sampling 抽樣查詢與統計技術原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-ttl-storage-management/&quot;&gt;ClickHouse 系列：TTL 資料清理與儲存成本優化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-storage-policies/&quot;&gt;ClickHouse 系列：儲存政策（Storage Policies）與磁碟資源分層策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-schemas-storage-improvement/&quot;&gt;ClickHouse 系列：表格設計與儲存優化細節&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-grafana-dashboard/&quot;&gt;ClickHouse 系列：ClickHouse 系列：整合 Grafana 打造可視化監控&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-select-optimization/&quot;&gt;ClickHouse 系列：查詢優化案例&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-bi-integration/&quot;&gt;ClickHouse 系列：與 BI 工具整合（Power BI）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-cloud-vs-self-host/&quot;&gt;ClickHouse 系列：ClickHouse Cloud 與自建部署的優劣比較&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-security-rbac/&quot;&gt;ClickHouse 系列：資料庫安全性與權限管理（RBAC）實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-operator-kubernates/&quot;&gt;ClickHouse 系列：Kubernetes 部署分散式架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-sourcecode-introduction/&quot;&gt;ClickHouse 系列：從原始碼看 MergeTree 的六大核心機制&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>ClickHouse 系列：ReplacingMergeTree 與資料去重機制</title><link>https://vicwen.app/posts/clickhouse-replacingmergetree-deduplication/</link><guid isPermaLink="true">https://vicwen.app/posts/clickhouse-replacingmergetree-deduplication/</guid><pubDate>Fri, 08 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在大數據環境中，「資料重複」是常見且麻煩的問題，尤其是在 ETL Pipeline 或實時資料流匯入（如 Kafka Stream）時，重複資料會嚴重影響統計結果與查詢性能。ClickHouse 提供了一套簡單卻強大的去重機制：&lt;strong&gt;ReplacingMergeTree 儲存引擎&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;本篇文章將帶你深入了解 ReplacingMergeTree 的運作原理、使用場景與 Best Practice。&lt;/p&gt;
&lt;h2&gt;ReplacingMergeTree 是什麼？&lt;/h2&gt;
&lt;p&gt;ReplacingMergeTree 是 ClickHouse MergeTree 家族的一員（~家族真熱鬧~），它能在背景 Merge 操作時，自動根據指定欄位 (如 version 欄位) 將重複資料去除，保留最新版本或最先寫入的那一筆。&lt;/p&gt;
&lt;h3&gt;運作邏輯：&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;在 INSERT 資料時 &lt;strong&gt;不會即時去重&lt;/strong&gt;，資料會以 Data Part 形式寫入磁碟。&lt;/li&gt;
&lt;li&gt;在 &lt;strong&gt;背景 Merge 操作時&lt;/strong&gt;，根據 Primary Key 進行資料比對，若發現重複記錄（相同 Primary Key），則保留 version 最大值 (或任一筆若無 version)。&lt;/li&gt;
&lt;li&gt;資料去重是 &lt;strong&gt;非即時、最終一致性&lt;/strong&gt; 的，實際去重點發生於 Merge 階段。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;ReplacingMergeTree 的語法與範例&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE user_profiles
(
    user_id UInt64,
    profile_version UInt32,
    name String,
    email String
) ENGINE = ReplacingMergeTree(profile_version)
ORDER BY user_id;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;profile_version&lt;/code&gt; 為版本欄位，決定在去重時保留最新版本。&lt;/li&gt;
&lt;li&gt;若不指定 version，系統會隨機保留其中一筆（不保證順序），走一個俄羅斯輪盤風格。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;INSERT INTO user_profiles VALUES (1, 1, &apos;Alice&apos;, &apos;alice_v1@example.com&apos;);
INSERT INTO user_profiles VALUES (1, 2, &apos;Alice&apos;, &apos;alice_v2@example.com&apos;);
INSERT INTO user_profiles VALUES (2, 1, &apos;Bob&apos;, &apos;bob@example.com&apos;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查詢時可能會看到重複資料，因為去重尚未透過 Merge 發生：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT * FROM user_profiles WHERE user_id = 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;結果：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;user_id&lt;/th&gt;
&lt;th&gt;profile_version&lt;/th&gt;
&lt;th&gt;name&lt;/th&gt;
&lt;th&gt;email&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Alice&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;mailto:alice_v1@example.com&quot;&gt;alice_v1@example.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Alice&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;mailto:alice_v2@example.com&quot;&gt;alice_v2@example.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;進行 &lt;code&gt;FINAL&lt;/code&gt; 查詢語法後（ &lt;code&gt;FINAL&lt;/code&gt; 確保查詢時「讀到最新去重後的結果」）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;OPTIMIZE TABLE user_profiles FINAL;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;SELECT * FROM user_profiles FINAL WHERE user_id = 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;:::important
&lt;code&gt;OPTIMIZE&lt;/code&gt;：強制執行資料片段 (Data Parts) 合併動作，把分散的小 Data Parts 合併成大 Part，並同時觸發資料去重（ReplacingMergeTree）或聚合（SummingMergeTree）等邏輯。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;實質影響的是磁碟上的 Data Parts 資料&lt;/strong&gt;，合併結果是永久性（會寫入磁碟）。
:::&lt;/p&gt;
&lt;p&gt;再查詢結果只會剩下 &lt;code&gt;version = 2&lt;/code&gt; 的那筆資料。&lt;/p&gt;
&lt;h2&gt;ReplacingMergeTree 跟 Primary Key 的關係&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;去重是基於 &lt;code&gt;Primary Key&lt;/code&gt; 判斷的&lt;/strong&gt;，因此建表時的 &lt;code&gt;ORDER BY&lt;/code&gt; 欄位（也就是 &lt;code&gt;Primary Key&lt;/code&gt;）必須正確設計。&lt;/li&gt;
&lt;li&gt;若 &lt;code&gt;ORDER BY&lt;/code&gt; 欄位無法唯一識別一筆記錄，則 &lt;code&gt;ReplacingMergeTree&lt;/code&gt; 可能會保留錯誤的資料版本。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;範例：&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;ORDER BY (user_id, profile_version)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;這種情況下，ReplacingMergeTree 無法自動去重，因為 Primary Key 已包含 version 值，會將每個版本當成不同資料。&lt;/p&gt;
&lt;p&gt;正確設計應該是：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ORDER BY user_id
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;ReplacingMergeTree 使用時機與適用場景&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;場景&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Kafka / Stream 資料流重放去重&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;多次 Consume、資料重放（At-least-once 保證）情境下自動去重。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;批次資料匯入過濾重複&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ETL 導入過程中重複載入相同資料，透過背景去重保證數據唯一性。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;具版本控制的資料歷史維護&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;保留最新版本數據，版本號較小的資料會在合併過程中被去除。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;數據補正修正&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;資料寫入後若有誤，透過補寫版本號較大的修正資料覆蓋錯誤記錄。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;ReplacingMergeTree v.s. AggregatingMergeTree&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;ReplacingMergeTree&lt;/th&gt;
&lt;th&gt;AggregatingMergeTree&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;核心功能&lt;/td&gt;
&lt;td&gt;根據 &lt;code&gt;Primary Key&lt;/code&gt; 去重，保留 &lt;code&gt;version&lt;/code&gt; 最大的資料&lt;/td&gt;
&lt;td&gt;根據 &lt;code&gt;AggregateFunction&lt;/code&gt; 資料型別進行合併彙總&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;版本欄位&lt;/td&gt;
&lt;td&gt;可選，無指定時保留任一筆&lt;/td&gt;
&lt;td&gt;無需版本欄位&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;適用場景&lt;/td&gt;
&lt;td&gt;重複資料去除、最新版本數據保持&lt;/td&gt;
&lt;td&gt;預先彙總的數據庫，如行為統計、即時計數&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Merge 過程&lt;/td&gt;
&lt;td&gt;合併時將相同主鍵的資料合併為一筆&lt;/td&gt;
&lt;td&gt;合併時將 &lt;code&gt;AggregateFunction&lt;/code&gt; 聚合運算結果計算出來&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;ReplacingMergeTree 常見誤區與最佳實踐&lt;/h2&gt;
&lt;h3&gt;常見誤區：&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;以為寫入時就會去重&lt;/strong&gt; → &lt;strong&gt;錯！&lt;/strong&gt; &lt;code&gt;ReplacingMergeTree&lt;/code&gt; 是 &lt;strong&gt;背景合併去重&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ORDER BY 選錯欄位&lt;/strong&gt; → 主鍵沒選好，去重完全無效。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;期待去重後數據即時查詢結果就正確&lt;/strong&gt; → 無強制 Optimize 時可能仍查到重複數據。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;最佳實踐：&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;對於重複資料容忍度低的系統，可在寫入後定期執行 &lt;code&gt;OPTIMIZE TABLE FINAL&lt;/code&gt; 來確保資料唯一。&lt;/li&gt;
&lt;li&gt;若為實時查詢應用，並且資料版本需求複雜，考慮用 &lt;strong&gt;ReplacingMergeTree + Materialized View&lt;/strong&gt; 實作即時計算最新版本視圖。&lt;/li&gt;
&lt;li&gt;設計 &lt;code&gt;Primary Key&lt;/code&gt; 時，應僅包含「能唯一識別一筆邏輯記錄」的欄位，不要把 &lt;code&gt;version&lt;/code&gt; 放進 &lt;code&gt;ORDER BY&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;ReplacingMergeTree 與去重效能影響&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ReplacingMergeTree&lt;/code&gt; 的寫入效能與 &lt;code&gt;MergeTree&lt;/code&gt; 相近，因為去重是延遲處理的，不影響寫入吞吐量。&lt;/li&gt;
&lt;li&gt;背景合併 (Merge) 時會額外進行去重比對，Merge 負載會稍大，但對查詢來說，去重後反而能大幅減少掃描資料量。&lt;/li&gt;
&lt;li&gt;資料去重完成後，儲存空間可獲得顯著節省（取決於重複數據比例）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;結語&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;ReplacingMergeTree&lt;/code&gt; 提供了一種無需複雜索引或額外處理邏輯即可實現數據去重的輕量解決方案，非常適合用於 &lt;strong&gt;「資料流去重」、「批次匯入防重複」、「版本控制」&lt;/strong&gt; 等場景。
然而，了解其 &lt;strong&gt;去重時機&lt;/strong&gt; 與 &lt;strong&gt;&lt;code&gt;Primary Key&lt;/code&gt; 設計要點&lt;/strong&gt; 是使用這個引擎的關鍵。&lt;/p&gt;
&lt;h3&gt;ClickHouse 系列持續更新中:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/what-is-clickhouse/&quot;&gt;ClickHouse 系列：ClickHouse 是什麼？與傳統 OLAP/OLTP 資料庫的差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-column-row-based-storage/&quot;&gt;ClickHouse 系列：ClickHouse 為什麼選擇 Column-based 儲存？講解 Row-based 與 Column-based 的核心差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-engine&quot;&gt;ClickHouse 系列：ClickHouse 儲存引擎 - MergeTree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-compression-skipping-index/&quot;&gt;ClickHouse 系列：壓縮技術與 Data Skipping Indexes 如何大幅加速查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replacingmergetree-deduplication/&quot;&gt;ClickHouse 系列：ReplacingMergeTree 與資料去重機制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-summingmergetree-aggregation/&quot;&gt;ClickHouse 系列：SummingMergeTree 進行資料彙總的應用場景&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-materialized-view/&quot;&gt;ClickHouse 系列：Materialized Views 即時聚合查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-partition-pruning/&quot;&gt;ClickHouse 系列：分區策略與 Partition Pruning 原理解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-primary-sorting-key/&quot;&gt;ClickHouse 系列：Primary Key、Sorting Key 與 Granule 索引運作原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-collapsingmergetree/&quot;&gt;ClickHouse 系列：CollapsingMergeTree 與邏輯刪除的最佳實踐&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-versioned-collapsingmergetree/&quot;&gt;ClickHouse 系列：VersionedCollapsingMergeTree 版本控制與資料衝突解決&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-aggregatingmergetree/&quot;&gt;ClickHouse 系列：AggregatingMergeTree 實時指標統計的進階應用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-distributed-table-architecture/&quot;&gt;ClickHouse 系列：Distributed Table 與分布式查詢架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replication-failover/&quot;&gt;ClickHouse 系列：Replicated Tables 高可用性與零停機升級實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-kafka-data-streaming-pipeline/&quot;&gt;ClickHouse 系列：與 Kafka 整合打造即時 Data Streaming Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-batch-import/&quot;&gt;ClickHouse 系列：批次匯入最佳實踐 (CSV、Parquet、Native Format)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-external-data-integration/&quot;&gt;ClickHouse 系列：ClickHouse 與外部資料源整合（PostgreSQL）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-query-log-explain/&quot;&gt;ClickHouse 系列：如何提升查詢優化？system.query_log 與 EXPLAIN 用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-projections-optimization/&quot;&gt;ClickHouse 系列：Projections 進階查詢加速技術&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-sampling-statistics/&quot;&gt;ClickHouse 系列：Sampling 抽樣查詢與統計技術原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-ttl-storage-management/&quot;&gt;ClickHouse 系列：TTL 資料清理與儲存成本優化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-storage-policies/&quot;&gt;ClickHouse 系列：儲存政策（Storage Policies）與磁碟資源分層策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-schemas-storage-improvement/&quot;&gt;ClickHouse 系列：表格設計與儲存優化細節&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-grafana-dashboard/&quot;&gt;ClickHouse 系列：ClickHouse 系列：整合 Grafana 打造可視化監控&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-select-optimization/&quot;&gt;ClickHouse 系列：查詢優化案例&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-bi-integration/&quot;&gt;ClickHouse 系列：與 BI 工具整合（Power BI）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-cloud-vs-self-host/&quot;&gt;ClickHouse 系列：ClickHouse Cloud 與自建部署的優劣比較&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-security-rbac/&quot;&gt;ClickHouse 系列：資料庫安全性與權限管理（RBAC）實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-operator-kubernates/&quot;&gt;ClickHouse 系列：Kubernetes 部署分散式架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-sourcecode-introduction/&quot;&gt;ClickHouse 系列：從原始碼看 MergeTree 的六大核心機制&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>ClickHouse 系列：壓縮技術與 Data Skipping Indexes 如何大幅加速查詢</title><link>https://vicwen.app/posts/clickhouse-compression-skipping-index/</link><guid isPermaLink="true">https://vicwen.app/posts/clickhouse-compression-skipping-index/</guid><pubDate>Thu, 07 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在 ClickHouse 的高性能查詢背後，除了列式存儲與向量化執行外，「壓縮技術」與「資料跳過索引（Data Skipping Indexes）」也是讓它能夠應對 PB 級數據的核心關鍵。本文將深入解析這兩項技術的原理與應用，並說明如何有效提升查詢效率、降低儲存成本。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://clickhouse.com/docs/data-compression/compression-in-clickhouse&quot;&gt;One of the secrets to ClickHouse query performance is compression.&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;為何壓縮對 OLAP 效能如此關鍵？&lt;/h2&gt;
&lt;p&gt;在 OLAP 場景中，資料量動輒百萬、千萬筆，若未經良好壓縮處理，磁碟 I/O 將成為查詢效率瓶頸。ClickHouse 採用 Columnar Storage，讓每欄資料型態一致、重複性高，使得壓縮成效極佳。&lt;/p&gt;
&lt;h3&gt;ClickHouse 的壓縮優勢：&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;降低磁碟儲存空間需求&lt;/strong&gt;（通常壓縮比達 5~10 倍以上）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;減少磁碟 I/O 傳輸量&lt;/strong&gt;（更快讀取、更低延遲）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CPU 解壓縮效能優化&lt;/strong&gt;（使用輕量快速壓縮算法）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;ClickHouse 支援的壓縮編碼技術&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;壓縮編碼&lt;/th&gt;
&lt;th&gt;特性與應用說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LZ4 (預設)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;高速低延遲，壓縮比中等，預設壓縮器，適用即時查詢與寫入場景&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ZSTD&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;壓縮比高於 LZ4，但壓縮/解壓速度稍慢，適合冷資料儲存或報表分析&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Delta Encoding&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;對遞增數值（如時間戳、ID）進行差值壓縮，大幅減少儲存空間&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Gorilla Encoding&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;對時間序列數據極致優化，適用 IoT 與時序資料壓縮（例如 CPU 使用率、溫度等）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Double Delta&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;適用變化趨勢平穩的數值型資料，加強壓縮效果&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;以下是以 ClickHouse 儲存 StackOverflow posts 資料表的壓縮統計，透過查詢 &lt;code&gt;system.columns&lt;/code&gt; 取得各欄位壓縮前後的體積與壓縮比資料&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT name,
   formatReadableSize(sum(data_compressed_bytes)) AS compressed_size,
   formatReadableSize(sum(data_uncompressed_bytes)) AS uncompressed_size,
   round(sum(data_uncompressed_bytes) / sum(data_compressed_bytes), 2) AS ratio
FROM system.columns
WHERE table = &apos;posts&apos;
GROUP BY name

┌─name──────────────────┬─compressed_size─┬─uncompressed_size─┬───ratio────┐
│ Body                  │ 46.14 GiB       │ 127.31 GiB        │ 2.76       │
│ Title                 │ 1.20 GiB        │ 2.63 GiB          │ 2.19       │
│ Score                 │ 84.77 MiB       │ 736.45 MiB        │ 8.69       │
│ Tags                  │ 475.56 MiB      │ 1.40 GiB          │ 3.02       │
│ ParentId              │ 210.91 MiB      │ 696.20 MiB        │ 3.3        │
│ Id                    │ 111.17 MiB      │ 736.45 MiB        │ 6.62       │
│ AcceptedAnswerId      │ 81.55 MiB       │ 736.45 MiB        │ 9.03       │
│ ClosedDate            │ 13.99 MiB       │ 517.82 MiB        │ 37.02      │
│ LastActivityDate      │ 489.84 MiB      │ 964.64 MiB        │ 1.97       │
│ CommentCount          │ 37.62 MiB       │ 565.30 MiB        │ 15.03      │
│ OwnerUserId           │ 368.98 MiB      │ 736.45 MiB        │ 2          │
│ AnswerCount           │ 21.82 MiB       │ 622.35 MiB        │ 28.53      │
│ FavoriteCount         │ 280.95 KiB      │ 508.40 MiB        │ 1853.02    │
│ ViewCount             │ 95.77 MiB       │ 736.45 MiB        │ 7.69       │
│ LastEditorUserId      │ 179.47 MiB      │ 736.45 MiB        │ 4.1        │
│ ContentLicense        │ 5.45 MiB        │ 847.92 MiB        │ 155.5      │
│ OwnerDisplayName      │ 14.30 MiB       │ 142.58 MiB        │ 9.97       │
│ PostTypeId            │ 20.93 MiB       │ 565.30 MiB        │ 27         │
│ CreationDate          │ 314.17 MiB      │ 964.64 MiB        │ 3.07       │
│ LastEditDate          │ 346.32 MiB      │ 964.64 MiB        │ 2.79       │
│ LastEditorDisplayName │ 5.46 MiB        │ 124.25 MiB        │ 22.75      │
│ CommunityOwnedDate    │ 2.21 MiB        │ 509.60 MiB        │ 230.94     │
└───────────────────────┴─────────────────┴───────────────────┴────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;你會發現：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;高重複值欄位（如 FavoriteCount, ContentLicense）壓縮比可達數百甚至數千倍，這正是列式儲存結合專屬編碼的優勢。&lt;/li&gt;
&lt;li&gt;數值型欄位（Score, AcceptedAnswerId, PostTypeId）壓縮效果也非常顯著，Delta 編碼結合 LZ4/ZSTD 能大幅減少資料體積。&lt;/li&gt;
&lt;li&gt;Body, Title 雖為文字型欄位，但仍有 2–3x 壓縮比，搭配 LowCardinality 設計將有更好空間優化空間。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;如何指定壓縮算法？&lt;/h2&gt;
&lt;p&gt;你可以在建表時，透過 &lt;code&gt;CODEC&lt;/code&gt; 參數指定欄位使用的壓縮編碼：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE user_events (
  event_date Date,
  user_id UInt32,
  event_type String CODEC(ZSTD),
  event_value Float64 CODEC(Delta, LZ4)
) ENGINE = MergeTree
PARTITION BY toYYYYMM(event_date)
ORDER BY (event_date, user_id);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;說明：&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;event_type&lt;/code&gt; 使用 ZSTD，適合重複性高的字串欄位。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;event_value&lt;/code&gt; 使用 Delta 編碼處理數值，再以 LZ4 壓縮，提升查詢與儲存效能。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;ClickHouse 的 Data Skipping Indexes 原理&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Data Skipping Indexes（資料跳過索引）&lt;/strong&gt; 是 ClickHouse 特有的查詢加速技術，核心原理是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;在查詢時，只掃描必要的資料區塊，跳過無關區塊，降低 I/O 成本與查詢延遲。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;這些索引不是傳統意義上的 B-Tree 索引，而是針對 MergeTree 資料片段內的「欄位統計資訊」建立的快速過濾機制。&lt;/p&gt;
&lt;h2&gt;minmax 索引如何運作？&lt;/h2&gt;
&lt;p&gt;每個 MergeTree Part 會為主鍵欄位自動建立 &lt;code&gt;minmax&lt;/code&gt; 索引，例如：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT * FROM orders WHERE order_date &amp;gt;= &apos;2025-01-01&apos; AND order_date &amp;lt; &apos;2025-02-01&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;系統會檢查每個資料區塊的 &lt;code&gt;min(order_date)&lt;/code&gt; ~ &lt;code&gt;max(order_date)&lt;/code&gt; 範圍。&lt;/li&gt;
&lt;li&gt;若該區塊的日期範圍完全不符合查詢條件，則直接跳過不讀取！&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;假設你有 10 億筆日誌資料：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用預設 minmax 索引查詢近一天資料，可跳過 99% 資料塊。&lt;/li&gt;
&lt;li&gt;查詢延遲從 15s 降至 300ms。&lt;/li&gt;
&lt;li&gt;系統僅需讀取與解壓 1% 的資料，極大節省 CPU 與 I/O 資源。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;這就是 &lt;strong&gt;Partition Pruning + Data Skipping&lt;/strong&gt; 的效果。&lt;/p&gt;
&lt;h2&gt;進階：Secondary Index 實作&lt;/h2&gt;
&lt;p&gt;ClickHouse 也允許你針對特定欄位建立進階索引，例如：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE logs (
  timestamp DateTime,
  level String,
  message String
) ENGINE = MergeTree
ORDER BY timestamp
SETTINGS index_granularity = 8192;

ALTER TABLE logs ADD INDEX idx_level (level) TYPE set(1000) GRANULARITY 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;索引類型說明：&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;索引類型&lt;/th&gt;
&lt;th&gt;說明與應用&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;minmax&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;預設，針對數值或日期範圍查詢加速&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;set(N)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;建立集合型索引，適用高重複字串欄位（如 Log Level）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ngrambf_v1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;適用模糊查詢，加上布隆過濾器實現快速匹配&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bloom_filter&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;快速過濾欄位可能值，適用多值欄位（如 tags、labels）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Best Practice &amp;amp; Notice&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;索引粒度設定&lt;/strong&gt;：&lt;code&gt;index_granularity&lt;/code&gt; 預設為 8192 筆，查詢越頻繁、粒度可適當調小以加速跳過效率。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;壓縮與索引兼得&lt;/strong&gt;：壓縮減少資料量，索引減少掃描範圍，兩者配合達成極致查詢性能。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不宜濫用索引&lt;/strong&gt;：太多索引會增加寫入負擔，應針對查詢頻繁欄位建索引。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;結語&lt;/h2&gt;
&lt;p&gt;ClickHouse 透過高效的壓縮與跳過索引技術，讓大數據查詢也能實現「ms級響應」。但記得不要濫用索引噢，反而會適得其反呢。&lt;/p&gt;
&lt;h3&gt;ClickHouse 系列持續更新中:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/what-is-clickhouse/&quot;&gt;ClickHouse 系列：ClickHouse 是什麼？與傳統 OLAP/OLTP 資料庫的差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-column-row-based-storage/&quot;&gt;ClickHouse 系列：ClickHouse 為什麼選擇 Column-based 儲存？講解 Row-based 與 Column-based 的核心差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-engine&quot;&gt;ClickHouse 系列：ClickHouse 儲存引擎 - MergeTree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-compression-skipping-index/&quot;&gt;ClickHouse 系列：壓縮技術與 Data Skipping Indexes 如何大幅加速查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replacingmergetree-deduplication/&quot;&gt;ClickHouse 系列：ReplacingMergeTree 與資料去重機制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-summingmergetree-aggregation/&quot;&gt;ClickHouse 系列：SummingMergeTree 進行資料彙總的應用場景&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-materialized-view/&quot;&gt;ClickHouse 系列：Materialized Views 即時聚合查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-partition-pruning/&quot;&gt;ClickHouse 系列：分區策略與 Partition Pruning 原理解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-primary-sorting-key/&quot;&gt;ClickHouse 系列：Primary Key、Sorting Key 與 Granule 索引運作原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-collapsingmergetree/&quot;&gt;ClickHouse 系列：CollapsingMergeTree 與邏輯刪除的最佳實踐&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-versioned-collapsingmergetree/&quot;&gt;ClickHouse 系列：VersionedCollapsingMergeTree 版本控制與資料衝突解決&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-aggregatingmergetree/&quot;&gt;ClickHouse 系列：AggregatingMergeTree 實時指標統計的進階應用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-distributed-table-architecture/&quot;&gt;ClickHouse 系列：Distributed Table 與分布式查詢架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replication-failover/&quot;&gt;ClickHouse 系列：Replicated Tables 高可用性與零停機升級實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-kafka-data-streaming-pipeline/&quot;&gt;ClickHouse 系列：與 Kafka 整合打造即時 Data Streaming Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-batch-import/&quot;&gt;ClickHouse 系列：批次匯入最佳實踐 (CSV、Parquet、Native Format)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-external-data-integration/&quot;&gt;ClickHouse 系列：ClickHouse 與外部資料源整合（PostgreSQL）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-query-log-explain/&quot;&gt;ClickHouse 系列：如何提升查詢優化？system.query_log 與 EXPLAIN 用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-projections-optimization/&quot;&gt;ClickHouse 系列：Projections 進階查詢加速技術&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-sampling-statistics/&quot;&gt;ClickHouse 系列：Sampling 抽樣查詢與統計技術原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-ttl-storage-management/&quot;&gt;ClickHouse 系列：TTL 資料清理與儲存成本優化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-storage-policies/&quot;&gt;ClickHouse 系列：儲存政策（Storage Policies）與磁碟資源分層策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-schemas-storage-improvement/&quot;&gt;ClickHouse 系列：表格設計與儲存優化細節&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-grafana-dashboard/&quot;&gt;ClickHouse 系列：ClickHouse 系列：整合 Grafana 打造可視化監控&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-select-optimization/&quot;&gt;ClickHouse 系列：查詢優化案例&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-bi-integration/&quot;&gt;ClickHouse 系列：與 BI 工具整合（Power BI）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-cloud-vs-self-host/&quot;&gt;ClickHouse 系列：ClickHouse Cloud 與自建部署的優劣比較&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-security-rbac/&quot;&gt;ClickHouse 系列：資料庫安全性與權限管理（RBAC）實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-operator-kubernates/&quot;&gt;ClickHouse 系列：Kubernetes 部署分散式架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-sourcecode-introduction/&quot;&gt;ClickHouse 系列：從原始碼看 MergeTree 的六大核心機制&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>ClickHouse 系列：ClickHouse 儲存引擎 - MergeTree</title><link>https://vicwen.app/posts/clickhouse-mergetree-engine/</link><guid isPermaLink="true">https://vicwen.app/posts/clickhouse-mergetree-engine/</guid><pubDate>Wed, 06 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;ClickHouse 能夠支撐高性能資料查詢的核心秘密之一，就是其強大的儲存引擎 — &lt;strong&gt;MergeTree&lt;/strong&gt;。這一篇將帶你深入理解 MergeTree 是什麼、它解決了哪些問題，以及如何透過不同的變種引擎 (ReplacingMergeTree, SummingMergeTree 等) 應對不同資料處理場景。&lt;/p&gt;
&lt;h2&gt;MergeTree 是什麼？&lt;/h2&gt;
&lt;p&gt;MergeTree 是 ClickHouse 中最基礎的儲存引擎，負責將大量寫入資料有效儲存與管理，並支援高效的查詢與資料合併（Merge）操作。&lt;/p&gt;
&lt;h3&gt;核心概念：&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;分區 (Partitions)&lt;/strong&gt;：將資料依據指定欄位（如日期）切分成不同區塊，減少查詢時需掃描的資料量。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://clickhouse.com/docs/assets/ideal-img/partitions.4c07acd.2048.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://clickhouse.com/docs/partitions&quot;&gt;取自官網: Table partitions&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Primary Key (主鍵排序索引)&lt;/strong&gt;：定義資料在磁碟中的排序方式，讓查詢條件能快速定位資料範圍。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data Parts (資料片段)&lt;/strong&gt;：每次資料寫入時，都會生成一個 Data Part，經過以下手續：Sorting、Splitting、Compression，最後 Writing to Disk&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://clickhouse.com/docs/assets/ideal-img/part.d9b96ef.2048.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;:::note&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Sorting&lt;/strong&gt;：資料根據 Sorting Key（如 town, street）進行排序，並產生一個稀疏主鍵索引（Sparse Primary Index）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Splitting&lt;/strong&gt;：排序後的資料會被拆分成單獨的欄位。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compression&lt;/strong&gt;：每個欄位分別進行壓縮處理，應用 LZ4、ZSTD 等壓縮算法。
:::&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;後續透過背景合併 (Merge) 將小片段整理成大型優化片段。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://clickhouse.com/docs/assets/ideal-img/merges.285da65.2048.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://clickhouse.com/docs/parts&quot;&gt;取自官網: Table parts&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;MergeTree 的核心特性與 Merge 操作原理&lt;/h2&gt;
&lt;p&gt;MergeTree 家族的引擎具備以下幾個特性：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Primary Key 排序與稀疏索引 (Sparse Primary Index)&lt;/strong&gt;：表格的主鍵決定了每個資料片段 (Data Part) 內的排序方式（Clustered Index）。不過，這個索引並不指向單筆資料，而是以 8192 筆資料為單位的 Granule (粒度)。這種設計讓主鍵索引即便在超大資料量下仍能被保留在記憶體中，並且能有效快速地存取磁碟上的資料區塊。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;靈活的分區機制 (Partitioning)&lt;/strong&gt;：使用任意表達式來劃分分區，並能透過 Partition Pruning 技術在查詢時自動跳過不相關的分區，避免不必要的 I/O。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;高可用性與容錯 (Replication)&lt;/strong&gt;：資料可於多個 Cluster Nodes 間進行複製，支援高可用性、故障切換 (Failover)、以及無停機升級 (Zero Downtime Upgrade)。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;統計與抽樣查詢 (Sampling &amp;amp; Statistics)&lt;/strong&gt;：MergeTree 支援各類型的統計與抽樣機制，可協助查詢優化器進行查詢路徑選擇與加速。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;MergeTree 解決了哪些問題？&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;大規模寫入效能瓶頸&lt;/strong&gt;：透過將寫入資料分為小型 Data Parts 先行儲存，避免頻繁修改大型檔案帶來的 I/O 開銷。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;查詢效率提升&lt;/strong&gt;：依據 Partition 與 Primary Key 排序，能快速定位查詢資料區塊，避免全表掃描。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;資料壓縮與去重整合&lt;/strong&gt;：透過 Merge 操作合併資料時進行壓縮與去重，大幅降低儲存空間與查詢延遲。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;語法和範例&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
    name1 [type1] [[NOT] NULL] [DEFAULT|MATERIALIZED|ALIAS|EPHEMERAL expr1] [COMMENT ...] [CODEC(codec1)] [STATISTICS(stat1)] [TTL expr1] [PRIMARY KEY] [SETTINGS (name = value, ...)],
    name2 [type2] [[NOT] NULL] [DEFAULT|MATERIALIZED|ALIAS|EPHEMERAL expr2] [COMMENT ...] [CODEC(codec2)] [STATISTICS(stat2)] [TTL expr2] [PRIMARY KEY] [SETTINGS (name = value, ...)],
    ...
    INDEX index_name1 expr1 TYPE type1(...) [GRANULARITY value1],
    INDEX index_name2 expr2 TYPE type2(...) [GRANULARITY value2],
    ...
    PROJECTION projection_name_1 (SELECT &amp;lt;COLUMN LIST EXPR&amp;gt; [GROUP BY] [ORDER BY]),
    PROJECTION projection_name_2 (SELECT &amp;lt;COLUMN LIST EXPR&amp;gt; [GROUP BY] [ORDER BY])
) ENGINE = MergeTree()
ORDER BY expr
[PARTITION BY expr]
[PRIMARY KEY expr]
[SAMPLE BY expr]
[TTL expr
    [DELETE|TO DISK &apos;xxx&apos;|TO VOLUME &apos;xxx&apos; [, ...] ]
    [WHERE conditions]
    [GROUP BY key_expr [SET v1 = aggr_func(v1) [, v2 = aggr_func(v2) ...]] ] ]
[SETTINGS name = value, ...]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;建立 TABLE&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE user_events
(
    EventDate Date,
    UserID UInt64,
    EventType String,
    EventValue Float32
) ENGINE = MergeTree()
PARTITION BY toYYYYMM(EventDate)
ORDER BY (EventDate, UserID);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;寫入資料&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;INSERT INTO user_events VALUES (&apos;2025-08-10&apos;, 1001, &apos;click&apos;, 1.0);
INSERT INTO user_events VALUES (&apos;2025-08-10&apos;, 1002, &apos;view&apos;, 1.0);
INSERT INTO user_events VALUES (&apos;2025-08-11&apos;, 1001, &apos;purchase&apos;, 299.99);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;查詢資料&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;SELECT *
FROM user_events
WHERE EventDate = &apos;2025-08-10&apos;
  AND UserID = 1001;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此查詢會根據 Partition 先裁剪至 2025-08 分區，再透過 Primary Key (EventDate, UserID) 直接命中相關 Granule，大幅減少掃描資料量。&lt;/p&gt;
&lt;h3&gt;補充：指定索引 Granularity&lt;/h3&gt;
&lt;p&gt;若想更細緻控制 Primary Index 的粒度大小，可以透過 &lt;code&gt;index_granularity&lt;/code&gt; 參數：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ENGINE = MergeTree()
PARTITION BY toYYYYMM(EventDate)
ORDER BY (EventDate, UserID)
SETTINGS index_granularity = 8192;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;8192 是預設 Granule 大小，若資料查詢行為較分散，也可以調整為更小的粒度 (如 4096) 來提升查詢命中率，但會增加索引體積。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Granularity 會在後面的篇幅中詳細描述，由於這是額外補充，日後了解 Granularity 的定義和用途，可以再回來看補充～&lt;/p&gt;
&lt;h2&gt;MergeTree 家族的特殊變種&lt;/h2&gt;
&lt;p&gt;ClickHouse 根據不同業務需求，衍生出許多 MergeTree 變種引擎：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;儲存引擎&lt;/th&gt;
&lt;th&gt;特性與應用場景&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ReplacingMergeTree&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;自動以指定欄位 (如 version 欄位) 替換重複資料，適合資料需去重的場景。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SummingMergeTree&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;在合併時自動將相同 Primary Key 的數值欄位進行加總，適用於資料匯總場景。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AggregatingMergeTree&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;針對 AggregateFunction 資料型別做更複雜的聚合運算，適合實時指標統計場景。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CollapsingMergeTree&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;透過 sign 欄位標記資料新增/刪除狀態，自動實現邏輯刪除與衝突解決。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;VersionedCollapsingMergeTree&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;在 Collapsing 基礎上支援版本控制的資料去重。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Merge 操作的原理與效能影響&lt;/h2&gt;
&lt;p&gt;MergeTree 會在背景執行 Merge 操作，將多個小型 Data Part 合併成大型 Part，並在此過程中進行排序、壓縮與去重。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Merge 頻率與效能平衡&lt;/strong&gt;：Merge 操作會佔用系統 I/O 資源，設定合理的 Merge 參數（如 &lt;code&gt;max_parts_to_merge_at_once&lt;/code&gt;）可平衡查詢與寫入效能。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mutation (資料變異操作)&lt;/strong&gt;：ClickHouse 也支援在 Merge 階段進行 UPDATE / DELETE 操作，但屬於非即時性處理，適合資料分析場景。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;應用場景&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Log 分析&lt;/strong&gt;：以日期為 Partition，URL 或 IP 為 Primary Key，支援快速查詢特定時段與條件的日誌資料。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;使用者行為追蹤資料&lt;/strong&gt;：透過 &lt;code&gt;ReplacingMergeTree&lt;/code&gt; 去重資料、&lt;code&gt;SummingMergeTree&lt;/code&gt; 快速整理使用者點擊行為。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IoT 感測資料平台&lt;/strong&gt;：大量寫入感測資料並以 &lt;code&gt;AggregatingMergeTree&lt;/code&gt; 實時統計各種資料指標。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;結語&lt;/h2&gt;
&lt;p&gt;MergeTree 是 ClickHouse 高效能儲存與查詢的基礎，透過分區、排序與背景 Merge 機制，使得海量資料寫入與查詢皆能達到極致效能。針對不同業務場景選擇合適的 MergeTree 變種引擎，能夠很大的提升系統性能。&lt;/p&gt;
&lt;h3&gt;ClickHouse 系列持續更新中:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/what-is-clickhouse/&quot;&gt;ClickHouse 系列：ClickHouse 是什麼？與傳統 OLAP/OLTP 資料庫的差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-column-row-based-storage/&quot;&gt;ClickHouse 系列：ClickHouse 為什麼選擇 Column-based 儲存？講解 Row-based 與 Column-based 的核心差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-engine&quot;&gt;ClickHouse 系列：ClickHouse 儲存引擎 - MergeTree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-compression-skipping-index/&quot;&gt;ClickHouse 系列：壓縮技術與 Data Skipping Indexes 如何大幅加速查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replacingmergetree-deduplication/&quot;&gt;ClickHouse 系列：ReplacingMergeTree 與資料去重機制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-summingmergetree-aggregation/&quot;&gt;ClickHouse 系列：SummingMergeTree 進行資料彙總的應用場景&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-materialized-view/&quot;&gt;ClickHouse 系列：Materialized Views 即時聚合查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-partition-pruning/&quot;&gt;ClickHouse 系列：分區策略與 Partition Pruning 原理解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-primary-sorting-key/&quot;&gt;ClickHouse 系列：Primary Key、Sorting Key 與 Granule 索引運作原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-collapsingmergetree/&quot;&gt;ClickHouse 系列：CollapsingMergeTree 與邏輯刪除的最佳實踐&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-versioned-collapsingmergetree/&quot;&gt;ClickHouse 系列：VersionedCollapsingMergeTree 版本控制與資料衝突解決&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-aggregatingmergetree/&quot;&gt;ClickHouse 系列：AggregatingMergeTree 實時指標統計的進階應用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-distributed-table-architecture/&quot;&gt;ClickHouse 系列：Distributed Table 與分布式查詢架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replication-failover/&quot;&gt;ClickHouse 系列：Replicated Tables 高可用性與零停機升級實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-kafka-data-streaming-pipeline/&quot;&gt;ClickHouse 系列：與 Kafka 整合打造即時 Data Streaming Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-batch-import/&quot;&gt;ClickHouse 系列：批次匯入最佳實踐 (CSV、Parquet、Native Format)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-external-data-integration/&quot;&gt;ClickHouse 系列：ClickHouse 與外部資料源整合（PostgreSQL）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-query-log-explain/&quot;&gt;ClickHouse 系列：如何提升查詢優化？system.query_log 與 EXPLAIN 用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-projections-optimization/&quot;&gt;ClickHouse 系列：Projections 進階查詢加速技術&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-sampling-statistics/&quot;&gt;ClickHouse 系列：Sampling 抽樣查詢與統計技術原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-ttl-storage-management/&quot;&gt;ClickHouse 系列：TTL 資料清理與儲存成本優化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-storage-policies/&quot;&gt;ClickHouse 系列：儲存政策（Storage Policies）與磁碟資源分層策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-schemas-storage-improvement/&quot;&gt;ClickHouse 系列：表格設計與儲存優化細節&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-grafana-dashboard/&quot;&gt;ClickHouse 系列：ClickHouse 系列：整合 Grafana 打造可視化監控&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-select-optimization/&quot;&gt;ClickHouse 系列：查詢優化案例&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-bi-integration/&quot;&gt;ClickHouse 系列：與 BI 工具整合（Power BI）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-cloud-vs-self-host/&quot;&gt;ClickHouse 系列：ClickHouse Cloud 與自建部署的優劣比較&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-security-rbac/&quot;&gt;ClickHouse 系列：資料庫安全性與權限管理（RBAC）實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-operator-kubernates/&quot;&gt;ClickHouse 系列：Kubernetes 部署分散式架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-sourcecode-introduction/&quot;&gt;ClickHouse 系列：從原始碼看 MergeTree 的六大核心機制&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>ClickHouse 系列：ClickHouse 為什麼選擇 Column-based 儲存？講解 Row-based 與 Column-based 的核心差異</title><link>https://vicwen.app/posts/clickhouse-column-row-based-storage/</link><guid isPermaLink="true">https://vicwen.app/posts/clickhouse-column-row-based-storage/</guid><pubDate>Tue, 05 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在過去兩篇文章中有提到「Row-based Storage」與「Column-based Storage」是 OLTP 與 OLAP 系統架構選擇的根本差異。本文將從行列存儲的原理出發，說明 ClickHouse 為什麼選擇列式架構，以及它帶來的效能優勢與適用場景。&lt;/p&gt;
&lt;h2&gt;什麼是 Row-based Storage？&lt;/h2&gt;
&lt;p&gt;Row-based 儲存是將一筆記錄的所有欄位資料「以行為單位」連續存放於磁碟上。也就是說，資料庫每次存取時，會一次性讀取該行的所有欄位資料。&lt;/p&gt;
&lt;h3&gt;優點：&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;適合 OLTP 交易型應用&lt;/strong&gt;：例如電商網站的訂單處理、會員登入系統。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;單筆查詢/修改效率高&lt;/strong&gt;：根據主鍵 (Primary Key) 查詢或更新單筆記錄時，能快速取得完整資訊。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;缺點：&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;批次查詢效率低&lt;/strong&gt;：當我們只需查詢「特定欄位」的大量數據時（如報表統計），行式存儲會將整行資料都讀取，造成 I/O 資源浪費。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;資料壓縮效率差&lt;/strong&gt;：由於行內欄位類型與資料分布不一致，壓縮效果有限。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;代表性資料庫：&lt;/h3&gt;
&lt;p&gt;MySQL、PostgreSQL、Oracle DB、SQL Server&lt;/p&gt;
&lt;h2&gt;什麼是 Column-based Storage？&lt;/h2&gt;
&lt;p&gt;Column-based 儲存則是將資料「以欄為單位」儲存在磁碟上。每一個欄位的資料會被獨立且連續地儲存，當查詢時，僅需讀取需要的欄位即可。&lt;/p&gt;
&lt;h3&gt;優點：&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;查詢效能極佳（OLAP 場景）&lt;/strong&gt;：例如只需統計使用者年齡分佈時，系統僅讀取 Age 欄位，不需讀取 Name、Address 等無關欄位。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;壓縮率高&lt;/strong&gt;：同一欄位的資料型態一致，重複值多，能進行更有效率的編碼與壓縮（如 Run-Length Encoding、Delta Encoding）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;向量化運算加速查詢&lt;/strong&gt;：資料欄位緊密排列，使得 CPU 可針對欄位資料進行 SIMD 向量化批次運算，加速查詢執行。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;缺點：&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;單筆查詢效率低&lt;/strong&gt;：若查詢目標是完整一筆記錄，需從多個欄位位置讀取資料並組裝，延遲較高。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;寫入頻繁場景不適用&lt;/strong&gt;：寫入與修改操作成本較高，適合「讀多寫少」的應用情境。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;代表性資料庫：&lt;/h3&gt;
&lt;p&gt;ClickHouse、Apache Druid、Amazon Redshift、Google BigQuery&lt;/p&gt;
&lt;h2&gt;ClickHouse 為什麼選擇 Column-based？&lt;/h2&gt;
&lt;p&gt;ClickHouse 作為一個專為 OLAP 場景優化的數據庫，選擇 Column-based 架構是為了解決「大規模資料查詢」時的效能瓶頸。以下是 ClickHouse 透過列式存儲帶來的幾個關鍵優勢：&lt;/p&gt;
&lt;h3&gt;1. 只讀取需要的欄位&lt;/h3&gt;
&lt;p&gt;在傳統 Row-based 資料庫中，查詢某一欄位的數百萬筆資料時，仍會將整行的其他欄位一起讀取，I/O 浪費嚴重。而 ClickHouse 只需從磁碟讀取查詢所需的欄位，極大幅降低 I/O 操作，查詢延遲也大幅縮短。&lt;/p&gt;
&lt;h3&gt;2. 極致壓縮效能&lt;/h3&gt;
&lt;p&gt;ClickHouse 內建多種壓縮編碼（LZ4、ZSTD、Gorilla Encoding），並利用 Columnar 儲存的資料重複性，將儲存空間需求降低數倍甚至數十倍。不僅節省儲存成本，亦因資料壓縮而減少 I/O 傳輸量，進一步提升查詢效率。&lt;/p&gt;
&lt;h3&gt;3. 向量化查詢執行&lt;/h3&gt;
&lt;p&gt;ClickHouse 以向量化 (Vectorized Execution) 為核心，將欄位資料轉換為連續記憶體區塊進行 SIMD 批次處理，使得像 SUM、AVG、COUNT 這類聚合查詢的 CPU 使用率與執行速度都達到極致。&lt;/p&gt;
&lt;h3&gt;4. 與 Data Skipping Indexes 的完美結合&lt;/h3&gt;
&lt;p&gt;ClickHouse 採用 Data Skipping Indexes（資料跳過索引），當查詢條件不滿足某些資料區塊時，可直接跳過掃描這些無關區塊。這種機制在 Column-based 架構下運作尤為高效，能夠避免全表掃描，讓大規模數據查詢僅需秒級甚至毫秒級回應。&lt;/p&gt;
&lt;h3&gt;5. 更符合 Data Analysis 需求&lt;/h3&gt;
&lt;p&gt;現代數據分析場景中，查詢行為大多是「大量讀取」與「多欄位聚合」，寫入與修改則相對較少。ClickHouse 透過列式存儲，專注於「讀多寫少」的查詢模式，完美符合數據報表、使用者行為分析、即時數據儀表板等應用場景。&lt;/p&gt;
&lt;h2&gt;Row-based 與 Column-based 適用場景總結&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;類型&lt;/th&gt;
&lt;th&gt;Row-based 資料庫&lt;/th&gt;
&lt;th&gt;Column-based 資料庫&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;典型應用&lt;/td&gt;
&lt;td&gt;OLTP 系統（訂單交易、會員登入）&lt;/td&gt;
&lt;td&gt;OLAP 系統（數據分析、報表、指標監控）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;查詢類型&lt;/td&gt;
&lt;td&gt;單筆查詢 (主鍵查詢、多次頻繁寫入)&lt;/td&gt;
&lt;td&gt;批次查詢 (聚合分析、大量讀取特定欄位)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;資料寫入頻率&lt;/td&gt;
&lt;td&gt;高頻寫入、隨時修改&lt;/td&gt;
&lt;td&gt;寫入頻率低，以批次/流式匯入為主&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;查詢效率&lt;/td&gt;
&lt;td&gt;單筆查詢延遲低、批次查詢效率差&lt;/td&gt;
&lt;td&gt;單筆查詢較慢、批次查詢極速&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;代表資料庫&lt;/td&gt;
&lt;td&gt;MySQL、PostgreSQL、Oracle&lt;/td&gt;
&lt;td&gt;ClickHouse、Druid、Redshift&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;結語&lt;/h2&gt;
&lt;p&gt;平時大家小打小鬧的開發大多是使用 OLTP 資料庫，畢竟比較聚焦於即時的 transaction 處理（ACID），我也是因緣際會下才接觸到 TB~PB 等級的資料量，了解到原來還有 OLAP/OLTP/Row-based/Column-based Storage 的存在。&lt;/p&gt;
&lt;h3&gt;ClickHouse 系列持續更新中:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/what-is-clickhouse/&quot;&gt;ClickHouse 系列：ClickHouse 是什麼？與傳統 OLAP/OLTP 資料庫的差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-column-row-based-storage/&quot;&gt;ClickHouse 系列：ClickHouse 為什麼選擇 Column-based 儲存？講解 Row-based 與 Column-based 的核心差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-engine&quot;&gt;ClickHouse 系列：ClickHouse 儲存引擎 - MergeTree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-compression-skipping-index/&quot;&gt;ClickHouse 系列：壓縮技術與 Data Skipping Indexes 如何大幅加速查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replacingmergetree-deduplication/&quot;&gt;ClickHouse 系列：ReplacingMergeTree 與資料去重機制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-summingmergetree-aggregation/&quot;&gt;ClickHouse 系列：SummingMergeTree 進行資料彙總的應用場景&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-materialized-view/&quot;&gt;ClickHouse 系列：Materialized Views 即時聚合查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-partition-pruning/&quot;&gt;ClickHouse 系列：分區策略與 Partition Pruning 原理解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-primary-sorting-key/&quot;&gt;ClickHouse 系列：Primary Key、Sorting Key 與 Granule 索引運作原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-collapsingmergetree/&quot;&gt;ClickHouse 系列：CollapsingMergeTree 與邏輯刪除的最佳實踐&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-versioned-collapsingmergetree/&quot;&gt;ClickHouse 系列：VersionedCollapsingMergeTree 版本控制與資料衝突解決&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-aggregatingmergetree/&quot;&gt;ClickHouse 系列：AggregatingMergeTree 實時指標統計的進階應用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-distributed-table-architecture/&quot;&gt;ClickHouse 系列：Distributed Table 與分布式查詢架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replication-failover/&quot;&gt;ClickHouse 系列：Replicated Tables 高可用性與零停機升級實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-kafka-data-streaming-pipeline/&quot;&gt;ClickHouse 系列：與 Kafka 整合打造即時 Data Streaming Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-batch-import/&quot;&gt;ClickHouse 系列：批次匯入最佳實踐 (CSV、Parquet、Native Format)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-external-data-integration/&quot;&gt;ClickHouse 系列：ClickHouse 與外部資料源整合（PostgreSQL）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-query-log-explain/&quot;&gt;ClickHouse 系列：如何提升查詢優化？system.query_log 與 EXPLAIN 用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-projections-optimization/&quot;&gt;ClickHouse 系列：Projections 進階查詢加速技術&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-sampling-statistics/&quot;&gt;ClickHouse 系列：Sampling 抽樣查詢與統計技術原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-ttl-storage-management/&quot;&gt;ClickHouse 系列：TTL 資料清理與儲存成本優化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-storage-policies/&quot;&gt;ClickHouse 系列：儲存政策（Storage Policies）與磁碟資源分層策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-schemas-storage-improvement/&quot;&gt;ClickHouse 系列：表格設計與儲存優化細節&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-grafana-dashboard/&quot;&gt;ClickHouse 系列：ClickHouse 系列：整合 Grafana 打造可視化監控&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-select-optimization/&quot;&gt;ClickHouse 系列：查詢優化案例&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-bi-integration/&quot;&gt;ClickHouse 系列：與 BI 工具整合（Power BI）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-cloud-vs-self-host/&quot;&gt;ClickHouse 系列：ClickHouse Cloud 與自建部署的優劣比較&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-security-rbac/&quot;&gt;ClickHouse 系列：資料庫安全性與權限管理（RBAC）實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-operator-kubernates/&quot;&gt;ClickHouse 系列：Kubernetes 部署分散式架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-sourcecode-introduction/&quot;&gt;ClickHouse 系列：從原始碼看 MergeTree 的六大核心機制&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>ClickHouse 系列：ClickHouse 是什麼？與傳統 OLAP/OLTP 資料庫的差異</title><link>https://vicwen.app/posts/what-is-clickhouse/</link><guid isPermaLink="true">https://vicwen.app/posts/what-is-clickhouse/</guid><pubDate>Mon, 04 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;ClickHouse 是由 Yandex 開發的 開源分布式列式資料庫管理系統（Column-oriented DBMS）。&lt;/p&gt;
&lt;p&gt;主要針對 &lt;strong&gt;即時數據分析&lt;/strong&gt; (&lt;strong&gt;Real-Time Analytics&lt;/strong&gt;) 場景設計，能夠在秒級內處理 &lt;strong&gt;PB 級&lt;/strong&gt;數據。&lt;/p&gt;
&lt;p&gt;::github{repo=ClickHouse/ClickHouse}&lt;/p&gt;
&lt;h2&gt;架構&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://clickhouse.com/docs/assets/ideal-img/_vldb2024_2_Figure_0.ab9606a.1024.png&quot; alt=&quot;ClickHouse Architecture&quot; /&gt;&lt;/p&gt;
&lt;p&gt;ClickHouse 的整體設計邏輯非常清晰：以高效能讀取為核心，透過分散式架構與儲存最佳化，讓秒級查詢在 PB 級數據中成為可能。
從資料寫入、儲存、索引到查詢回傳，ClickHouse 有著一套完全為 OLAP 場景最佳化的底層架構。&lt;/p&gt;
&lt;p&gt;我們可以將流程稍微簡化成以下步驟：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;資料寫入 → 拆分成 Data Parts → Partition 劃分 → Primary Key 排序 → 壓縮 → Merge → 索引裁剪 → 向量化查詢 → 回傳結果
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;看不懂？沒關係，追完系列文章就全都懂了 😎&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;圖片取自 &lt;a href=&quot;https://clickhouse.com/docs/academic_overview&quot;&gt;Architecture Overview&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;特色 &amp;amp; 特性&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ClickHouse 技術特性&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Columnar Storage&lt;/td&gt;
&lt;td&gt;只讀取需要的欄位，避免不必要的 I/O。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vectorized Execution&lt;/td&gt;
&lt;td&gt;將資料轉成 SIMD 批次處理，加速 CPU 運算效率。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compression&lt;/td&gt;
&lt;td&gt;各種編碼方式 (LZ4, ZSTD, Delta Encoding) 提供高壓縮比，降低儲存成本。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data Skipping Indexes&lt;/td&gt;
&lt;td&gt;不需掃描全部資料，可根據索引直接跳過不相關的數據區塊，查詢更快。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MergeTree 儲存引擎&lt;/td&gt;
&lt;td&gt;強大靈活的底層結構，支援分區、排序鍵、TTL 清理機制，適合大量數據分析。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Materialized Views&lt;/td&gt;
&lt;td&gt;可將複雜查詢結果預先計算並實時更新，大幅加快查詢速度。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;分布式架構&lt;/td&gt;
&lt;td&gt;支援 Sharding 與 Replica ，易於擴展到 PB 級數據處理規模。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Near-Real-Time Ingestion&lt;/td&gt;
&lt;td&gt;支援高吞吐量寫入 (如 Kafka Stream)，數據可秒級查詢分析。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;OLAP v.s. OLTP 基本概念&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;分類&lt;/th&gt;
&lt;th&gt;OLTP (Online Transaction Processing)&lt;/th&gt;
&lt;th&gt;OLAP (Online Analytical Processing)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;主要用途&lt;/td&gt;
&lt;td&gt;交易處理 (CRUD 操作)&lt;/td&gt;
&lt;td&gt;數據分析、統計報表&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;操作特性&lt;/td&gt;
&lt;td&gt;少量資料的頻繁寫入&lt;/td&gt;
&lt;td&gt;大量資料的批次查詢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;查詢型態&lt;/td&gt;
&lt;td&gt;單筆/少量記錄查詢&lt;/td&gt;
&lt;td&gt;大範圍聚合查詢 (Aggregation)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;儲存結構&lt;/td&gt;
&lt;td&gt;行式存儲 (Row-based)&lt;/td&gt;
&lt;td&gt;列式存儲 (Column-based)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;代表產品&lt;/td&gt;
&lt;td&gt;MySQL, PostgreSQL, Oracle&lt;/td&gt;
&lt;td&gt;ClickHouse, Druid, Redshift&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;ClickHouse 與傳統 OLAP 資料庫的差異&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;項目&lt;/th&gt;
&lt;th&gt;ClickHouse&lt;/th&gt;
&lt;th&gt;傳統 Data Warehouse (如 Oracle DW, Teradata)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;架構&lt;/td&gt;
&lt;td&gt;分布式列式存儲&lt;/td&gt;
&lt;td&gt;多數為行式存儲或需額外配置列式引擎&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;查詢速度&lt;/td&gt;
&lt;td&gt;毫秒級到秒級回應&lt;/td&gt;
&lt;td&gt;通常需數秒到數分鐘&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;硬體需求&lt;/td&gt;
&lt;td&gt;可用商用硬體&lt;/td&gt;
&lt;td&gt;多數需昂貴專用伺服器&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;成本&lt;/td&gt;
&lt;td&gt;開源免費/雲端計價模式&lt;/td&gt;
&lt;td&gt;軟硬體成本高昂&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;延展性&lt;/td&gt;
&lt;td&gt;支援線性水平擴展 (Sharding/Replication)&lt;/td&gt;
&lt;td&gt;擴展成本高&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;ClickHouse 與 OLTP 資料庫（如 MySQL, PostgreSQL）的差異&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;OLTP 資料庫在於 ACID 交易完整性、寫入頻繁的即時處理。&lt;/li&gt;
&lt;li&gt;ClickHouse 更適合「&lt;strong&gt;大量讀取查詢&lt;/strong&gt;」且「&lt;strong&gt;不需要頻繁即時修改&lt;/strong&gt;」的場景（如報表查詢、BI 分析）。&lt;/li&gt;
&lt;li&gt;OLTP 常見的 UPDATE/DELETE 操作在 ClickHouse 中屬於非即時（Mutation 機制）。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;ClickHouse 系列持續更新中:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/what-is-clickhouse/&quot;&gt;ClickHouse 系列：ClickHouse 是什麼？與傳統 OLAP/OLTP 資料庫的差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-column-row-based-storage/&quot;&gt;ClickHouse 系列：ClickHouse 為什麼選擇 Column-based 儲存？講解 Row-based 與 Column-based 的核心差異&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-mergetree-engine&quot;&gt;ClickHouse 系列：ClickHouse 儲存引擎 - MergeTree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-compression-skipping-index/&quot;&gt;ClickHouse 系列：壓縮技術與 Data Skipping Indexes 如何大幅加速查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replacingmergetree-deduplication/&quot;&gt;ClickHouse 系列：ReplacingMergeTree 與資料去重機制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-summingmergetree-aggregation/&quot;&gt;ClickHouse 系列：SummingMergeTree 進行資料彙總的應用場景&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-materialized-view/&quot;&gt;ClickHouse 系列：Materialized Views 即時聚合查詢&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-partition-pruning/&quot;&gt;ClickHouse 系列：分區策略與 Partition Pruning 原理解析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-primary-sorting-key/&quot;&gt;ClickHouse 系列：Primary Key、Sorting Key 與 Granule 索引運作原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-collapsingmergetree/&quot;&gt;ClickHouse 系列：CollapsingMergeTree 與邏輯刪除的最佳實踐&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-versioned-collapsingmergetree/&quot;&gt;ClickHouse 系列：VersionedCollapsingMergeTree 版本控制與資料衝突解決&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-aggregatingmergetree/&quot;&gt;ClickHouse 系列：AggregatingMergeTree 實時指標統計的進階應用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-distributed-table-architecture/&quot;&gt;ClickHouse 系列：Distributed Table 與分布式查詢架構&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-replication-failover/&quot;&gt;ClickHouse 系列：Replicated Tables 高可用性與零停機升級實作&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-kafka-data-streaming-pipeline/&quot;&gt;ClickHouse 系列：與 Kafka 整合打造即時 Data Streaming Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-batch-import/&quot;&gt;ClickHouse 系列：批次匯入最佳實踐 (CSV、Parquet、Native Format)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-external-data-integration/&quot;&gt;ClickHouse 系列：ClickHouse 與外部資料源整合（PostgreSQL）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-query-log-explain/&quot;&gt;ClickHouse 系列：如何提升查詢優化？system.query_log 與 EXPLAIN 用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-projections-optimization/&quot;&gt;ClickHouse 系列：Projections 進階查詢加速技術&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-sampling-statistics/&quot;&gt;ClickHouse 系列：Sampling 抽樣查詢與統計技術原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-ttl-storage-management/&quot;&gt;ClickHouse 系列：TTL 資料清理與儲存成本優化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-storage-policies/&quot;&gt;ClickHouse 系列：儲存政策（Storage Policies）與磁碟資源分層策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-schemas-storage-improvement/&quot;&gt;ClickHouse 系列：表格設計與儲存優化細節&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-grafana-dashboard/&quot;&gt;ClickHouse 系列：ClickHouse 系列：整合 Grafana 打造可視化監控&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-select-optimization/&quot;&gt;ClickHouse 系列：查詢優化案例&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-bi-integration/&quot;&gt;ClickHouse 系列：與 BI 工具整合（Power BI）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-cloud-vs-self-host/&quot;&gt;ClickHouse 系列：ClickHouse Cloud 與自建部署的優劣比較&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/clickhouse-security-rbac/&quot;&gt;ClickHouse 系列：資料庫安全性與權限管理（RBAC）實作&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>台科電資學院暨校園傑出青年申請歷程</title><link>https://vicwen.app/posts/outstanding-young-persons-experience/</link><guid isPermaLink="true">https://vicwen.app/posts/outstanding-young-persons-experience/</guid><pubDate>Sun, 03 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;這篇文章主要是想紀錄自己當初申請院傑青和校傑青的歷程，以及留一些當初準備的方向給後面的人～
如果你對於爭取校內傑出青年榮譽有意願，看完文章如果仍然有疑問，歡迎私訊我的 Instagram，能回答的我都會幫忙解答！（要有禮貌喔 ✨）&lt;/p&gt;
&lt;h1&gt;為何想申請傑出青年&lt;/h1&gt;
&lt;p&gt;我當時申請是大一的時候，其實是思遠跟我說：「欸Vic，傑青欸要不要選一下」
我就說：「好啊，選一個。」&lt;/p&gt;
&lt;p&gt;也沒有什麼宏大的夢想。&lt;/p&gt;
&lt;p&gt;再來就是看了看申請資格，內容我都有符合，突然發現跟我的經驗滿吻合的，於是便出來參選了。&lt;/p&gt;
&lt;p&gt;個人認為傑青像是個稱號，也是個幫助你擴展自身眼界，接觸到其他優秀的人的一個渠道！尤其又經過學校的嚴格篩選，校傑可是只選 &lt;strong&gt;0.1%（12/12000）&lt;/strong&gt;，也是競爭非常激烈的呢。&lt;/p&gt;
&lt;h2&gt;好處？&lt;/h2&gt;
&lt;p&gt;大致有以下幾項：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;每屆都固定會有一次傑青聚會 🎈&lt;/li&gt;
&lt;li&gt;集結台科各科系的傑青 LINE 社群，裡面不乏有已經當教授、公司大老闆的存在&lt;/li&gt;
&lt;li&gt;~你可以偷偷炫耀自己是傑青~&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;畢竟這就是一個稱號，讓你透過這個 Program 認識到其他的傑青長姊們，我就有因為和一位在 Google 的傑青學長聯繫，&lt;strong&gt;促成兩次社團內的 Google 企業參訪&lt;/strong&gt;～學長真的在這之中幫助我這個小學弟很多。&lt;/p&gt;
&lt;h1&gt;院傑出準備歷程&lt;/h1&gt;
&lt;p&gt;建議可以參照當年度的評估勾選表作爲參考：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;參與社團活動，表現優異，有具體事蹟者。&lt;/li&gt;
&lt;li&gt;熱心公益，推展社會服務工作，表現優異，有具體事蹟者。&lt;/li&gt;
&lt;li&gt;參與國際或全國性競賽活動，表現優異，有具體事蹟者。&lt;/li&gt;
&lt;li&gt;參與學術研究或在文學、藝術、體育等方面，表現優異，有具體事蹟者。&lt;/li&gt;
&lt;li&gt;刻苦向學，卓然有成，足為青年學生楷模者。&lt;/li&gt;
&lt;li&gt;其他足資為青年學生楷模有具體事蹟者&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;所以我大致拆分為：&lt;strong&gt;公益、學術、品德、社團、競賽&lt;/strong&gt;去做準備，當然我觀察大部分傑青都不會全部具備，而是深耕於特定幾個項目，但如果你&lt;strong&gt;只有符合其中 3 項&lt;/strong&gt;，你一定會被刷下來，請評估自身能力，即使要掰也要至少掰到四項喔。&lt;/p&gt;
&lt;p&gt;我自身申請是著重：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;接任 Google 學生開發者社群社長 &amp;amp; 資工系系學會會長&lt;/li&gt;
&lt;li&gt;2018-2024 新北瑞芳扶少團創團團長 &amp;amp; 基金會輔導志工 &amp;amp; 一些七七八八的志工經驗&lt;/li&gt;
&lt;li&gt;工科賽電修職種冠軍&lt;/li&gt;
&lt;li&gt;拿 GPA 和書卷出來招搖撞騙 XD&lt;/li&gt;
&lt;li&gt;拿家庭背景出來說&lt;/li&gt;
&lt;li&gt;反正能拿出來講的都寫上去就對了，但還是要有料的再寫上去&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;因為一開始是先選院傑出青年，申請表只有單純用條列式去撰寫，所以也沒有刻意去雕用字遣詞，主要還是在面試環節決定是否有第二階段的門票～
到時候面試前也會讓你準備簡報，可以把詳細資訊再面談告知。&lt;/p&gt;
&lt;p&gt;也可以在這時候思考一下自己的優點、缺點，面試時該如何和評審委員介紹自己。&lt;/p&gt;
&lt;p&gt;我當時是先介紹自己的大多數經歷，拆分成上面幾個區塊快速帶過，接著帶到家庭和讀書環境的窘境，最後轉折帶出不服輸、堅毅的性格，以及最後帶出曾經做過的幾個公益項目。&lt;/p&gt;
&lt;p&gt;我當然最後是用座右銘作為結尾，說實在滿中二的，但好像滿有效果的 XD&lt;/p&gt;
&lt;h1&gt;校傑出準備歷程&lt;/h1&gt;
&lt;p&gt;當收到自己 9 取 3 有成功晉級的消息，真的鬆了口氣，當時我最小，大家的經歷都很強，滿怕自己第一階段就被刷下去了。&lt;/p&gt;
&lt;p&gt;接下來就是跟全校的院傑出一較高下了，在面談前有收到參考提問：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;自我介紹、最想感謝的人？&lt;/li&gt;
&lt;li&gt;記憶中最特殊的貢獻？&lt;/li&gt;
&lt;li&gt;你認為傑出青年應具備的條件？&lt;/li&gt;
&lt;li&gt;你認為你的何種事蹟(能力、才華)足以讓人感動或被認為傑出？&lt;/li&gt;
&lt;li&gt;未來有何抱負？&lt;/li&gt;
&lt;li&gt;如果當選了傑出青年，有無什麼想法或作為，能讓此活動更能擴大
其影響力？(承先啟後追求卓越)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;但除了第一項以外，我只有被問到第 5 項，甚至有人完全沒被問到上面的 XD
幾乎都是評審委員基於你給的自傳、自我介紹繼續提問，我當時幾乎都被問到生長環境、如何保持這樣的心態、公益項目為主，
有題我覺得滿耐人尋味的，有想法的可以思考看看：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;您認為「傑出」的定義是什麼？如何體現於日常？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我當時面談的氣氛算是挺和諧的，整體體驗還不錯（還有午餐吃），也有趁空檔多認識一些候選人，大家都是我的學長姐 😭，個個都超級優秀的，讓我很想成為像他們一樣的人。&lt;/p&gt;
&lt;h1&gt;心得 &amp;amp; 建議&lt;/h1&gt;
&lt;p&gt;大家常會問我：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Vic 你拿到雙傑出青年的頭銜有什麼轉變嗎？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;我可以說幾乎沒有&lt;/strong&gt;，日子照樣過，我也還是個普通的學生，並沒有因為多了個頭銜而過上人生巔峰，最重要的還是在個人的平時心態，我觀察傑青的長姊們，發現「傑出」的特質並不是刻意展露的，而是平時的成果累積、一舉一動都是符合所謂的特質。&lt;/p&gt;
&lt;p&gt;我自己是有一些心得，用條列式大概是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;頭銜能製造影響力，那你如何透過影響力影響他人？&lt;/li&gt;
&lt;li&gt;即使你不是全方位的，那你是如何深耕於你擅長的領域？&lt;/li&gt;
&lt;li&gt;「&lt;strong&gt;傑出&lt;/strong&gt;」並不是刻意展現的，而是渾然天成的，保持平常心吧！&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;最後，如果你仔細看簡章你會發現，如果你當選院傑出青年，但在校傑出青年階段不幸落選，你是&lt;strong&gt;沒有資格再參加一次&lt;/strong&gt;囉，所以如果你還年輕，經歷還累積的不夠多，可以不用那麼急著申請喔，我這種二年級就當選的人，十九屆來屈指可數，各位選之前還是要審慎評估一下 ✌️（除非你只想拿院傑出）&lt;/p&gt;
&lt;p&gt;我當時甚至是選到校傑出青年才知道這件事情，真可怕 💀&lt;/p&gt;
&lt;p&gt;最後用當天面試校傑出的照片結束這個回憶吧~&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/posts/outstanding-young-persons-experience/photo.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>SITCON 2025 - 從「經營」到「領導」：社群領導經驗分享</title><link>https://vicwen.app/posts/sitcon-2025-management-leadership/</link><guid isPermaLink="true">https://vicwen.app/posts/sitcon-2025-management-leadership/</guid><pubDate>Sat, 08 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;本文章是摘錄自 &lt;a href=&quot;https://sitcon.org/2025/agenda/62f54e/&quot;&gt;SITCON 2025 投稿&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在學生時代，我們都有機會參與各種社團、學生組織，甚至創立屬於自己的社群。但從「經營」到「領導」，這中間的轉變並非一蹴可及。這篇文章，我將分享自己在社群領導上的經驗，從如何開始、如何帶領，到如何真正發揮影響力。&lt;/p&gt;
&lt;h1&gt;為什麼選擇領導？&lt;/h1&gt;
&lt;p&gt;如果你曾在社團中擔任幹部，或是參加過一些大型活動，你可能會發現：「領導者」與「管理者」其實是不同的角色。管理者確保事情運行順利，而領導者則是能夠啟發團隊、帶動變革的人。&lt;/p&gt;
&lt;p&gt;在我的經驗中，擔任社群領導者不僅讓我累積了大量的實戰經驗，還讓我學會如何與不同的人合作、如何解決問題，甚至如何在逆境中帶領團隊走向成功。&lt;/p&gt;
&lt;h1&gt;如何開始成為社群領導？&lt;/h1&gt;
&lt;h2&gt;1. 校內社團&lt;/h2&gt;
&lt;p&gt;如果你還在學校，社團是一個很好的起點。我自己的經歷就是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;參加社團—找到一個你有興趣的社團，積極參與活動。&lt;/li&gt;
&lt;li&gt;在社團中活躍—不只是「參加」，而是主動發言、貢獻自己的想法。&lt;/li&gt;
&lt;li&gt;表達想成為幹部的意願—讓幹部知道你有興趣，機會自然會來。&lt;/li&gt;
&lt;li&gt;成為幹部—從小事做起，逐漸累積經驗，最後成為社團的核心成員。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;2. 學生社群年會&lt;/h2&gt;
&lt;p&gt;除了學校內的社團，各種學生社群年會也是一個很棒的機會，例如：COSCUP、DevFest、SITCON、HITCON。許多人可能會覺得這類活動只是聽聽講座，但其實：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;20% 是聽議程，80% 是交朋友！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;這是一個建立人脈、認識志同道合夥伴的機會。&lt;/p&gt;
&lt;p&gt;所以，當你參加這類活動時，記得主動和別人交流，而不只是坐在台下聽講。第一次參加可以詢問周遭的朋友會不會去，可以一起去壯壯膽 🏃&lt;/p&gt;
&lt;h1&gt;從「經營」者到「領導」者&lt;/h1&gt;
&lt;p&gt;許多人在擔任社團幹部時，會專注於「經營」層面，例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;確保活動順利舉辦&lt;/li&gt;
&lt;li&gt;處理內部的行政事務&lt;/li&gt;
&lt;li&gt;管理社群成員&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;這些當然是重要的，但要成為真正的領導者，則需要更進一步：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;建立願景—讓團隊知道我們的目標是什麼，並讓大家對此產生共鳴。&lt;/li&gt;
&lt;li&gt;激勵團隊—每個人都有低潮期，如何讓大家保持動力，是領導者的重要任務。&lt;/li&gt;
&lt;li&gt;拓展影響力—不只是管理內部事務，還要對外建立合作關係，讓社群更有發展性。&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;領導者的重要性&lt;/h1&gt;
&lt;p&gt;為什麼「領導者」這個角色這麼重要？我認為關鍵在於：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;內部管理 + 外部開發 = 成功！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;如果只專注於內部管理，團隊可能會變得保守、缺乏成長。&lt;/p&gt;
&lt;p&gt;如果只專注於外部發展，內部可能會鬆散、沒有向心力。因此，真正的領導者要能夠平衡這兩者。&lt;/p&gt;
&lt;h1&gt;拓展人脈，創造更多機會&lt;/h1&gt;
&lt;p&gt;我在擔任社群領導的過程中，發現「&lt;strong&gt;人脈&lt;/strong&gt;」是非常關鍵的資源。透過社群活動、交流聚會，我不僅結識了許多優秀的夥伴，也得到了許多寶貴的機會。&lt;/p&gt;
&lt;h2&gt;如何有效拓展人脈？&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;主動出擊—不要害羞，勇敢去認識新朋友。&lt;/li&gt;
&lt;li&gt;真誠交流—不是單純為了利益，而是發自內心的想認識對方。&lt;/li&gt;
&lt;li&gt;保持聯繫—認識之後，記得保持聯絡，未來才有機會一起合作。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;有哪些資源可以幫助我們成長？&lt;/h2&gt;
&lt;p&gt;在成為社群領導者的過程中，我發現有許多資源可以幫助我們提升領導能力、拓展視野，甚至開創更多可能性。以下是一些我親身體驗過的資源，推薦給想要進一步成長的你：&lt;/p&gt;
&lt;h3&gt;1. 校內社團：最佳的起點&lt;/h3&gt;
&lt;p&gt;社團是最直接、最容易參與的成長平台，不管是擔任幹部還是積極參與活動，都能累積許多寶貴的經驗。例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;學生會、自治會：學習組織管理、行政運作、溝通技巧。&lt;/li&gt;
&lt;li&gt;技術性社團（如 GDG on Campus、AI Club）：提升專業技能，並與業界接軌。&lt;/li&gt;
&lt;li&gt;興趣社團（如舞蹈社、音樂社）：培養團隊合作與創意發想能力。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;小技巧：先從參加活動開始，積極發言、提出建議，之後再爭取成為幹部。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;2. 跨校活動：拓展視野，建立人脈&lt;/h3&gt;
&lt;p&gt;除了學校內部的社團，跨校活動能夠讓你認識更多志同道合的夥伴，甚至與來自不同背景的人交流。例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;黑客松（Hackathon）：與各校學生組隊解決問題，提升技術與合作能力。&lt;/li&gt;
&lt;li&gt;社群年會（如 DevFest、PyCon、COSCUP）：去聽聽看專業講座，與業界人士交流。&lt;/li&gt;
&lt;li&gt;跨校比賽（如創新創業競賽、簡報比賽、模擬聯合國）：訓練表達、決策與團隊協作能力。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;小技巧：參加這類活動時，不要只專注於議程，記得主動認識新朋友，這將會成為你未來重要的人脈資源！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;3. 企業參訪、企業工作坊：與業界接軌&lt;/h3&gt;
&lt;p&gt;如果想要提前了解職場生態，企業參訪與企業工作坊是非常好的機會。例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;企業參訪：直接走進企業，與業界人士交流，了解產業趨勢與職場文化。&lt;/li&gt;
&lt;li&gt;企業工作坊：由企業舉辦的技能培訓活動，如簡報技巧、設計思維、數據分析等。&lt;/li&gt;
&lt;li&gt;Mentorship 計畫：部分企業或社群會推出導師計畫，讓學生能夠與業界前輩配對學習。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;小技巧：企業參訪時，勇敢發問、與講者交流，甚至在 LinkedIn 上與對方保持聯絡，未來可能會帶來職涯機會！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;4. 校園大使：成為品牌與學生的橋樑&lt;/h3&gt;
&lt;p&gt;許多企業會推出「校園大使」計畫，讓學生成為品牌的代表，協助推廣活動、社群經營，並獲得企業內部資源。例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Google Developer Groups On Campus&lt;/li&gt;
&lt;li&gt;Microsoft Learn Student Ambassadors&lt;/li&gt;
&lt;li&gt;AWS Educate 校園大使計畫&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;身為校園大使，你將有機會：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;直接與企業接觸，獲得內部資訊與資源。&lt;/li&gt;
&lt;li&gt;參與企業活動，提升專業技能與人脈。&lt;/li&gt;
&lt;li&gt;主導校內活動，學習組織、行銷與領導能力。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;小技巧：申請校園大使時，強調自己的熱情、相關經驗以及對品牌的理解，能夠提高錄取機會！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;5. 陌生開發：學會主動出擊&lt;/h3&gt;
&lt;p&gt;陌生開發（Cold Outreach）聽起來有點困難，但其實是一個非常實用的技能，無論是社群經營、職涯發展，甚至創業，都需要這種能力。例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;LinkedIn 人脈拓展：主動聯繫業界前輩，詢問他們的職場建議。&lt;/li&gt;
&lt;li&gt;主動爭取講者：如果你在籌辦活動，試著直接聯繫企業或專家，邀請他們擔任講者。&lt;/li&gt;
&lt;li&gt;尋找合作機會：無論是跨社團合作、與企業洽談贊助，都需要主動開口。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;小技巧：陌生開發時，簡潔明確地表達你的來意，例如「您好，我是某某社團的負責人，希望能邀請您來演講，時間地點如下…」，提高對方回應的機率！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;結論：資源多的是，重點是你願不願意行動！&lt;/h2&gt;
&lt;p&gt;這些資源只是冰山一角，關鍵在於你是否願意踏出第一步，勇敢去參與、去學習、去嘗試。記住，沒有人天生是領導者，所有的能力都是在實戰中鍛鍊出來的！所以，從今天開始，試著參加一場活動、加入一個社團、主動聯繫一位業界前輩，你會發現自己正在一步步成長！💪🔥&lt;/p&gt;
&lt;h1&gt;總結&lt;/h1&gt;
&lt;p&gt;從「經營」到「領導」的過程，對我來說是一個充滿挑戰但非常有收穫的旅程。我不僅學會了如何管理團隊，也學到了如何真正帶領一個社群向前邁進。
如果你也有興趣成為社群領導者，我鼓勵你從小地方開始，勇敢嘗試，累積經驗。最重要的是，不要害怕失敗，因為每一次挑戰都是讓自己成長的機會。
最後，推薦幾本對我幫助很大的書籍與影片：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;《領導力21法則》&lt;/li&gt;
&lt;li&gt;Simon Sinek 的 TED 演講：「Why Good Leaders Make You Feel Safe」&lt;/li&gt;
&lt;li&gt;《上位思維：從當上主管到成為領導人》&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;希望這篇文章能幫助到正在思考如何成為領導者的你！如果有任何問題，歡迎私訊我 🎉&lt;/p&gt;
</content:encoded></item><item><title>Google 學生社團在做什麼？我該如何申請？ - 讓 GDG on Campus 現任 Lead 帶你了解吧！</title><link>https://vicwen.app/posts/google-developer-groups-on-campus-apply/</link><guid isPermaLink="true">https://vicwen.app/posts/google-developer-groups-on-campus-apply/</guid><pubDate>Sat, 25 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;當我們談到校園技術型社團，最著名想必是 Google Developer Student Clubs（Google Developer Groups On Campus 的前身），但有名歸有名，許多人可能會疑惑：「他們到底在做什麼？」「參加這樣的社團，對我的學習和未來有什麼幫助？」&lt;/p&gt;
&lt;p&gt;作為一名 GDG on Campus 現任台灣科技大學 Lead，可以藉由這篇文章，帶大家一探究竟&lt;/p&gt;
&lt;h1&gt;什麼是 GDG on Campus？&lt;/h1&gt;
&lt;p&gt;GDG on Campus 的前身是 Google Developer Student Clubs，它的核心理念是透過 社群開放與合作的方式，幫助學生接觸最新的科技工具與趨勢，進而提升自身的技術能力，甚至找到志同道合的夥伴，一同開發創新專案。&lt;/p&gt;
&lt;p&gt;主要活動內容包括：
依照各校社群會有些許不同，但大多會是以下配置：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. 日常社課&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;每週固定一天，會邀請核心幹部或是學長姐來教特定主題之技術，如 ML/DL、Cloud Computing、Frontend、Backend...等主題，讓你直接接觸最新技術，並動手實作。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. 專家技術講座與工作坊&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;每學期，我們會舉辦專家講座，分享業界有名人士職場狀況、AI 應用技術，讓你直接接觸業界人士、工具、技術和想法。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. 專案開發與比賽&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;社團內鼓勵成員組隊參加各種技術挑戰賽，例如 Google Solution Challenge 或本地的黑客松活動，累積實戰經驗。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. 社群交流與成長&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;我們還會舉辦非正式的社交活動，如Coffee Chat、企業參訪（臺科曾去過 LINE, Google 等外商企業），讓成員間建立深厚的連結，學習如何在團隊中成長，以及開拓眼界。&lt;/p&gt;
&lt;h1&gt;加入 GDG on Campus 的好處是什麼？&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;1. 接觸最新技術與資源&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;你將有機會接觸 Google 的官方資源，例如免費的技術認證考試機會或每年一度的 Cloud Study Jam，2024 我們社群便有超過 5 位拿到最高等級獎勵（Google 書包、水壺、貼紙）。&lt;/p&gt;
&lt;p&gt;今年卸任更免費獲得了一年的開發者計畫（價值 299 鎂）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;../../assets/posts/google-developer-groups-on-campus-apply/Google%20Benefit.png&quot; alt=&quot;Benefit&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. 拓展人脈，連結未來&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;無論是學術界、業界、他校成員，你能夠在這認識來自不同背景的夥伴，甚至是 Google 員工！&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. 實踐軟實力&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;除了技術，參加社團還能鍛鍊你的領導能力、溝通技巧與活動策劃能力，為未來求職加分。&lt;/p&gt;
&lt;h2&gt;如何申請加入 GDG on Campus 成員？&lt;/h2&gt;
&lt;p&gt;請關注自己學校的 GDG on Campus 社群，目前多在 Instagram 有粉絲專頁，可以直接私訊詢問！&lt;/p&gt;
&lt;p&gt;臺科：&lt;a href=&quot;https://www.instagram.com/gdg.ntust/&quot;&gt;@gdg.ntust&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;如何申請加入 GDG on Campus 核心幹部？&lt;/h2&gt;
&lt;p&gt;Core-Team 會由各社團 Lead 進行控制，因此請直接私訊社群媒體帳號，通常我們會每學年招募新幹部，申請表單會在寒暑假開放填寫。記得準備好你的基本資料、興趣及技能簡介，也要記得先私訊。&lt;/p&gt;
&lt;h2&gt;如何申請加入 GDG on Campus Lead？&lt;/h2&gt;
&lt;p&gt;這部分應該也是大家最感興趣的！成為 GDG on Campus Lead 不僅能讓你在技術社群中發揮影響力，還能幫助你提升組織能力、擴展人脈，甚至獲得與 Google 官方直接接觸的機會。如果你對擔任 Lead 感興趣，以下是完整的申請與準備流程：&lt;/p&gt;
&lt;h3&gt;Step 1: 確保你符合基本資格&lt;/h3&gt;
&lt;p&gt;GDG on Campus Lead 的選拔對技術背景與領導潛力有一定要求，通常會關注以下幾點：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;技術能力： 具備基本的開發經驗（例如熟悉 Google 技術如 Firebase、TensorFlow、Flutter 等是加分項）。&lt;/li&gt;
&lt;li&gt;團隊合作經驗： 你是否曾參與過技術專案或相關社團活動？&lt;/li&gt;
&lt;li&gt;熱忱與影響力： 你對於帶領技術社群是否充滿熱情？是否具備帶動活動的能力？&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Step 2: 提交申請表單&lt;/h3&gt;
&lt;p&gt;Google 目前採取推薦制，因此記得先行詢問該校 Lead！每年約會在4月~5月開放 GDG on Campus Lead 的申請。&lt;/p&gt;
&lt;p&gt;申請表單通常需要你填寫以下內容：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;個人基本資料（例如姓名、學校、科系、聯絡方式）。&lt;/li&gt;
&lt;li&gt;技術背景與經驗（例如參與過的專案、使用的技術工具）。&lt;/li&gt;
&lt;li&gt;對於 GDG on Campus 的願景與計劃（你期望如何帶領這個社群）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Step 3: 接受面試&lt;/h3&gt;
&lt;p&gt;在提交表單後，Google 的 Relationship Manager 會邀請你進行一對一面試（全中文），評估你的以下能力：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;技術深度與學習能力： 你如何快速學習新技術，並將其應用於社群活動中。&lt;/li&gt;
&lt;li&gt;領導與協調能力： 你是否能有效組織活動，協調成員間的合作。&lt;/li&gt;
&lt;li&gt;創新思維： 你是否能為社群帶來獨特的活動與價值。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;這部分不需要過度緊張，只要真誠地分享你的經歷與熱情即可，也可以趁這時候把想詢問的問題問一問。&lt;/p&gt;
&lt;h3&gt;Step 4: 接受培訓與正式任命&lt;/h3&gt;
&lt;p&gt;一旦被選上，你將參加由 Google 舉辦的 Lead Onboarding Event，時間跨度約是整天，內容包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Google 技術、資源教學&lt;/li&gt;
&lt;li&gt;社群運營實務&lt;/li&gt;
&lt;li&gt;如何建立與維護社群文化&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;完成培訓後，你就正式成為 GDG on Campus Lead，並開始帶領你的校園社群！&lt;/p&gt;
&lt;h1&gt;成為 Lead 的好處是什麼？&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Google 官方資源支援： 你將獲得 Google 的技術資源、Badge補助（直接獲取經費機會很少）。&lt;/li&gt;
&lt;li&gt;職涯發展機會： 若你能在這段時間，帶領社團做出中大型 Project，這會是一份具份量的加分項，能讓你在這方面更具競爭力！&lt;/li&gt;
&lt;li&gt;個人成長與影響力： 作為 Lead，你將學會如何組織活動、管理團隊，並成為校園內的技術領袖，也能從中認識該校、他校的電神 ⚡。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;GDG on Campus 期待你的加入！&lt;/h2&gt;
&lt;p&gt;GDG on Campus 不只是學習技術的地方，更是一個充滿活力、技術和團隊合作的社群。如果你對技術充滿熱情，希望成為未來的核心幹部、Lead，我們隨時歡迎你/妳加入！&lt;/p&gt;
&lt;p&gt;如果你有任何問題，或是想了解更多細節，歡迎在下方留言、私訊 GDG on Campus NTUST Instagram 粉絲專頁，或是直接 follow 我的 &lt;a href=&quot;https://www.instagram.com/viiccwen/&quot;&gt;Instagram&lt;/a&gt;。😎&lt;/p&gt;
</content:encoded></item><item><title>Zeabur 雲端部署平台：懶人最愛的一鍵部署利器</title><link>https://vicwen.app/posts/zeabur-deployment-tutorial/</link><guid isPermaLink="true">https://vicwen.app/posts/zeabur-deployment-tutorial/</guid><pubDate>Tue, 21 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;是否覺得雲端部署過於複雜？&lt;/h1&gt;
&lt;p&gt;作為軟體工程師，你是否經常為了部署服務而苦惱於繁瑣的流程，甚至需要應對高昂的雲端服務費用？現在，Zeabur 的出現為開發者帶來全新的解決方案，讓你可以輕鬆實現一鍵部署，同時兼顧高效與成本效益。&lt;/p&gt;
&lt;h1&gt;什麼是 Zeabur？&lt;/h1&gt;
&lt;p&gt;Zeabur 是一款專為開發者設計的平台即服務（Platform as a Service, PaaS）。它提供簡潔的介面，支持一鍵部署應用程式至雲端，無需處理複雜的基礎設施配置。&lt;/p&gt;
&lt;p&gt;核心功能：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;支援多種程式語言和框架：例如 Node.js、Python、Java 等。&lt;/li&gt;
&lt;li&gt;彈性部署：根據需求自動調整資源分配。&lt;/li&gt;
&lt;li&gt;一鍵操作：無需手動配置伺服器或網路設置，節省開發者時間。&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;優缺點分析&lt;/h1&gt;
&lt;h2&gt;優點&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;簡化部署流程：一鍵即可完成應用程式部署，適合初學者及時間有限的專業工程師。&lt;/li&gt;
&lt;li&gt;高性價比：針對中小型專案，提供價格友善的服務選項。&lt;/li&gt;
&lt;li&gt;即時監控：提供應用程式的健康狀態、日誌及使用資源報告。&lt;/li&gt;
&lt;li&gt;友好的開發者社群支持：親測出現 Bug，在 Discord 詢問都能極快獲得解決方案！&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;缺點&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;功能深度有限：對於需要高度自定義的複雜專案，可能需要結合其他服務。&lt;/li&gt;
&lt;li&gt;新興平台穩定性：部分功能可能仍在完善中，也常常會出現 Bug，需要極度依靠 Discord 官方社群。&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;價格&lt;/h1&gt;
&lt;h2&gt;雲端部署方案&lt;/h2&gt;
&lt;p&gt;我自己是最推開發者專案，但這邊要注意的是，5美金的開發者方案是指最低使用量為5美金，若超過5美金的使用量，會再額外收費喔！
但實測數月，跟其他雲端部署平台相比真的佛心很多...況且 CEO 為台灣人，在 Discord 提 issue，在場的 Intern 也會很迅速幫助排錯。&lt;/p&gt;
&lt;h1&gt;VPS 方案&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;極度推薦，4GB RAM + 60GB SSD 一個月 3 USD...
這是無意間發現的，只能說極度推薦。若與其他 VPS 服務價格相比的話：&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;GoDaddy：4GB + 100GB 一個月約 18 USD&lt;/li&gt;
&lt;li&gt;DigitalOcean官方：25GB SSD + 1000 GB transfer 一個月 6 USD&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;只能說 3 USD/m 真香...&lt;/p&gt;
&lt;h1&gt;註冊與使用流程&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://zeabur.com/referral?referralCode=viiccwen&amp;amp;utm_source=viiccwen&quot;&gt;&lt;img src=&quot;https://zeabur.com/deployed-on-zeabur-dark.svg&quot; alt=&quot;Deployed on Zeabur&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;點擊上方進入 Zeabur 官網，點擊 Getting Start 註冊。
進入管理介面，選擇新建專案。&lt;/p&gt;
&lt;h2&gt;部署應用程式&lt;/h2&gt;
&lt;p&gt;連接你的 GitHub、Docker Container、官方範本（如本部落格便是範本之一）。
選擇應用框架並配置基本設定（如埠號、環境變數）。
點擊 Deploy 按鈕，等待數秒即可完成部署。&lt;/p&gt;
&lt;h1&gt;服務涵蓋範圍&lt;/h1&gt;
&lt;p&gt;Zeabur 提供的服務範圍廣泛，能滿足多種專案需求：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;應用部署：支持從 Web App 到 API 的各類應用程式。&lt;/li&gt;
&lt;li&gt;資料庫托管：整合主流資料庫如 PostgreSQL、MySQL。&lt;/li&gt;
&lt;li&gt;靜態文件托管：適用於前端專案或靜態網站。&lt;/li&gt;
&lt;li&gt;自動擴展：流量高峰時自動調整資源，確保穩定運行。&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;結論&lt;/h1&gt;
&lt;p&gt;Zeabur 是一款高效且易於使用的雲端部署平台，特別適合中小型專案開發者和希望簡化部署流程的工程師。如果你正在尋找一個價格友善、功能強大的雲端部署解決方案，Zeabur 絕對值得一試。
若你也想加入 Zeabur 的懷抱，也歡迎加入官方 Discord 社群！&lt;/p&gt;
</content:encoded></item><item><title>2024年17校GDSC聯合技術交流會</title><link>https://vicwen.app/posts/gdsc-2024-17-interaction/</link><guid isPermaLink="true">https://vicwen.app/posts/gdsc-2024-17-interaction/</guid><pubDate>Sun, 28 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;前言&lt;/h1&gt;
&lt;p&gt;很高興在 2024/04/21（日）能夠來參加17校GDSC聯合技術交流會，跟上次的心情類似，不只是抱著想交流技術、專案的心情，更是想去認識更多在不同領域中發光發熱的存在，讓自己在人生中的道路不孤單，有動力繼續向前！&lt;/p&gt;
&lt;p&gt;但這次的身分變得不同了，從General Member搖身一變，變成了&lt;strong&gt;Core Team Member&lt;/strong&gt;了。&lt;/p&gt;
&lt;h1&gt;活動流程&lt;/h1&gt;
&lt;p&gt;13:00-13:15 報到&lt;/p&gt;
&lt;p&gt;13:15-13:50 破冰小遊戲&lt;/p&gt;
&lt;p&gt;13:50-14:00 休息時間&lt;/p&gt;
&lt;p&gt;14:00-14:50 各校專案分享&lt;/p&gt;
&lt;p&gt;14:50-15:00 休息時間&lt;/p&gt;
&lt;p&gt;15:00-16:00 分組活動&lt;/p&gt;
&lt;p&gt;16:00-16:30 如何申請成為GDSC Lead ?&lt;/p&gt;
&lt;p&gt;16:30-17:00 Google知識王暨頒獎&lt;/p&gt;
&lt;p&gt;17:00-18:00 自由交流&lt;/p&gt;
&lt;h1&gt;破冰&lt;/h1&gt;
&lt;p&gt;大家需要站起來自我介紹，但是要融入你抽到的角色中XD，像我們這組就是老師。&lt;/p&gt;
&lt;p&gt;然後我就跑到別組去跟不認識的聊天了，認識一位清大、交大的學長，我還一直瘋狂的問一些未來的方向，雖然他們一直叫我不用急哈哈哈，但對未來沒方向，真的會很焦慮，我現在也仍在找尋未來要走的方向，不要讓自己一直停滯不前。&lt;/p&gt;
&lt;h1&gt;各校專案分享&lt;/h1&gt;
&lt;h2&gt;師大酒比特&lt;/h2&gt;
&lt;p&gt;其中最印象深刻的就是師大同學們的分享了！&lt;/p&gt;
&lt;p&gt;他們有分享到一款名為「師大酒比特」的專案，透過填寫Google表單，由系統推薦給妳/你最適合你的對象。&lt;/p&gt;
&lt;p&gt;但他們的小編其實滿慘的，居然用人工寄信的方式...寄了四百多封，連我聽著都感覺崩潰了，為何不寫個自動化呢XD。&lt;/p&gt;
&lt;p&gt;最好笑的是，有人送了一堆紀錄過去，讓主辦方寄信給本人，要他不要再送了XD。&lt;/p&gt;
&lt;p&gt;我們後來還跟他們聊了下，或許也能銜接至臺科這邊，做個臺科酒比特。&lt;/p&gt;
&lt;h1&gt;遊戲&lt;/h1&gt;
&lt;p&gt;Kahoot環節！&lt;/p&gt;
&lt;p&gt;可惜，我的GDSC價值還不夠高，飲恨拿第四，哭了（但我有凹了個小禮物嘿嘿）。&lt;/p&gt;
&lt;h1&gt;自由交流&lt;/h1&gt;
&lt;p&gt;這次相較於上次，熟悉了許多，或許上次是孤軍奮戰吧，獨自一人前往交流會認識其他人，顯得有點膽怯，但這次就很大方的走過去跟大家自然的聊起天來，還認識了個北醫醫的學長，超強...GitHub的Campus Ambassador，Orz 超電。&lt;/p&gt;
&lt;p&gt;也跟學長聊了些未來的規劃，其中有位學長要去國外讀研，我其實也有類似的想法，但真的是現實面狠狠的打擊在我身上QQ，一部分也是金援的問題。&lt;/p&gt;
&lt;p&gt;但這次的活動時長比較短，感覺大家都還沒聊夠，於是我們就一群人揪一揪去吃晚餐了。&lt;/p&gt;
&lt;h1&gt;11位晚餐聚會&lt;/h1&gt;
&lt;p&gt;後來大家揪一揪跑去吃飯，由臺科GDSC下任Core Team Member * 4 帶領了剩下7位新認識的朋友們一起去吃燒烤！&lt;/p&gt;
&lt;p&gt;我們跑去吃&lt;a href=&quot;https://www.facebook.com/profile.php?id=100092295227119&quot;&gt;99%燒烤&lt;/a&gt;，在大安區樂業街那！&lt;/p&gt;
&lt;p&gt;在聚會中，有的人聊動漫，有的人聊大學，有的人聊社團、興趣、專案...等等，很高興能夠在短短的半天內，就能收穫新的友情，甚至能毫無障礙的聊起天來，我們就這樣一路吃到了九點多。&lt;/p&gt;
&lt;p&gt;然後我們吃超飽XD，一個人只吃了快兩百而已，超讚耶。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/PPNaQKq.jpg&quot; alt=&quot;聚餐合照&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/SguDVTI.jpg&quot; alt=&quot;聚餐合照&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;結語&lt;/h1&gt;
&lt;p&gt;好像每次交流會都卡在期中考左右，所以來的人都沒有說到很多XD，而且跟上次的人都不一樣，全是生面孔。&lt;/p&gt;
&lt;p&gt;這是我第二次參加GDSC的交流會，很感謝北醫的Lead都會辦交流會，讓大家一同交流技術、交流自己學校的事物！但很可惜的是，臺科又不是協辦單位，希望自己onboard後能成為協辦單位，嘻嘻。也希望自己上任後能好好lead NTUST GDSC一起鑽研技術，好好經營社團，大家就像一個大家庭一樣！&lt;/p&gt;
&lt;p&gt;最後一樣，希望未來我也能成為在舞台上閃閃發光，在各領域中嶄露頭角的人，以此文共勉之。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/hrASZdh.jpg&quot; alt=&quot;大合照&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>工科技藝競賽電腦修護職種：選手歷程</title><link>https://vicwen.app/posts/computer-repairment-competitor-experience/</link><guid isPermaLink="true">https://vicwen.app/posts/computer-repairment-competitor-experience/</guid><pubDate>Wed, 17 Jan 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;前言&lt;/h1&gt;
&lt;p&gt;自 2022 年 11 月比完賽至今，已經過了一年，卻還沒好好還選手債...，連 bubu 跟我們的溜冰姐都在催我寫了 XD，看來是時候該還債了...。&lt;/p&gt;
&lt;p&gt;本篇文章應該不會太長（更新：沒有，這篇超長），但你會看到一位完全沒有任何資源的偏鄉學生，靠著自己的不服輸和毅力，在全國賽中殺出一條路，最後奪得冠軍的驚險歷程，相信各位觀眾會滿意這篇文章的！&lt;/p&gt;
&lt;h1&gt;背景&lt;/h1&gt;
&lt;p&gt;:::note
先來介紹一下我自己，我是來自新北瑞芳的一位資訊科學生，升上高職後，僅僅只有設下一個目標，透過繁星計畫登上職科殿堂—臺科，因此我高一高二就是顧好成績，加上是 Breaking 舞社的社長，僅此而已。
:::&lt;/p&gt;
&lt;p&gt;在高二下學期時，主任告訴我：「希望你能代表學校去參加工科賽」，有這麼特別的機會當然不會錯過啦 XD，我有三個職種可以挑：電腦軟體設計、機器人、電腦修護，雖然我最後是比電腦修護，但其實我最一開始的志願序是軟設&amp;gt;機器人&amp;gt;電修，是主任和文憲（我的指導老師）的大力推薦 XD，我才跑去電腦修護的。&lt;/p&gt;
&lt;p&gt;但我想要先澄清，電腦修護職種不是在修電腦！我們的職責是伺服器、網路管理和 Arduino 溝通程式設計，專攻 Windows, Linux server 角色和架設，類似於學校或企業的網管職位。&lt;/p&gt;
&lt;p&gt;總之，我就進入選手室啦～當時是二月決定的，但我到六月才開始慢慢接手選手室，到七月才開始密集訓練。&lt;/p&gt;
&lt;h1&gt;訓練&lt;/h1&gt;
&lt;h2&gt;誤入虎穴？&lt;/h2&gt;
&lt;p&gt;當時抱持著興奮的心情步入選手室，但沒幾天心態就炸了...。&lt;/p&gt;
&lt;p&gt;首先是&lt;strong&gt;完全沒留任何有用的資料&lt;/strong&gt;，過去傳承下來的資料都泛黃了，也就是說時代極度悠久，作業系統一些語法和架構都已經大改版，甚至根本就被遺棄（deprecated）了，我完全用不上！甚至會拖慢練習進度。&lt;/p&gt;
&lt;p&gt;再來是學長超雷...，英勇事蹟有：帶入比賽會場的程式是文憲寫的，進去根本看不懂；比賽拿倒數第二，倒數第一是確診沒比賽的；最過分的是，他居然偷拿我工具包裡的工具，沒有經過我的同意，甚至被我當場抓包==，甚至在練習前期完全沒帶過我（廢話，有夠雷），我只能獨自面對一切。&lt;/p&gt;
&lt;p&gt;最後是指導老師...，文憲由於自己有課要帶，還有晚自習的導師班，根本就無暇顧及選手這區塊，尤其前幾年選手成績都普普，因此對於比賽內容沒有很熟悉（對，我也覺得有點扯），只能帶我練一些很基礎的知識，例如：丙級和乙級證照的題目，剩下就只能放飛了...，甚至還是我回答他問題。&lt;/p&gt;
&lt;p&gt;OK，總結一下，學長超雷加上沒有 Data 加上指導老師不了解競賽內容，這也難怪前幾屆學長的成績不夠亮眼，一般人面臨到這種「地獄開局」，肯定直接放飛自我、自暴自棄，甚至半路中離的。&lt;/p&gt;
&lt;p&gt;我當時將情況跟科上主任說，當時主任只告訴我：「沒關係，你就顧好課業，課業第一，至少你還會有學校。」這一切就是我當時 6 月接手面臨到的狀況，連主任都覺得不太會有希望了，選手該如何去面對這場挑戰呢？&lt;/p&gt;
&lt;p&gt;但我想澄清一下，主任其實是位很盡責的教師，他雖然這樣跟我說，但還有告訴我若有缺什麼資源可以告訴他，要買練習材料都可以跟他講，他會負責採買。&lt;/p&gt;
&lt;p&gt;聽到這裡，嗯，我處在一個資源極度匱乏、同儕競爭壓力底下的環境，沒辦法享受其他傳統強校的學校資源，或許這就是學校漸漸沒落的原因，強的學校越強，弱的學校越弱。&lt;/p&gt;
&lt;p&gt;但既然我會寫這篇文章，就代表一定會有轉折，就讓我們繼續看下去。&lt;/p&gt;
&lt;h2&gt;不服輸&lt;/h2&gt;
&lt;p&gt;雖然面臨到這種狀況真的很痛苦，甚至到絕望的地步，但我還是不願面對放棄、懦弱的自己，不服輸的心也在這時被激發起來，「你覺得我不行，我偏要證明給你看，我做得到，我不只是埋沒在茫茫人海的一粒沙。」&lt;/p&gt;
&lt;p&gt;於是我開始制定練習時程，從七月開始練習，到十一月中比賽，我總共有四個月的時間練習，中間還有四次新北市辦的培訓營和模擬賽，因此我至少在八月中要練到一定程度，後面慢慢補細節。&lt;/p&gt;
&lt;p&gt;於是，初步計劃出來了，先練大概，再練細節，補全所有技能分支，就像玩遊戲練技能一樣。&lt;/p&gt;
&lt;h2&gt;請教的心&lt;/h2&gt;
&lt;p&gt;由於我沒什麼資源，只能一步一腳印，靠 Google 來拯救我的選手生涯，但我還是想感謝一個人，那就是上屆冠軍的學長，我們是因為跳舞而認識的，就這麼巧，他也曾經是我們這個職種的選手，因為我有時候真的卡住，解不出來的時候，便會厚著臉皮向他請教，他也會提供一些他的經驗還有技術的關鍵字，好似一盞明燈，在指點著我，雖然只有寥寥數語，但對我幫助極大。&lt;/p&gt;
&lt;p&gt;雖然直到現在都沒有一起跳舞了，但如果未來有機會又見面，我會請你吃飯的學長！&lt;/p&gt;
&lt;h2&gt;選手，是矛盾的代名詞&lt;/h2&gt;
&lt;p&gt;選手，是一個矛盾的詞，平常的我追求「效率」，希望不要花太多時間在一件瑣碎的事情上，但選手的練習過程中卻打破了這個鐵律。&lt;/p&gt;
&lt;p&gt;我們常常需要專注於任何小事上，盡可能的透過訓練提升自己的速度，例如：撥網路線，一件極為簡單的撥網路線，我為了提升速度，拉了不下上百條，甚至撥到手長繭，從一開始的細皮嫩肉，拉幾條就紅通通的手，到了後面，面不改色，就可以行雲流水的撥完四、五條。&lt;/p&gt;
&lt;p&gt;我曾經計算過，若我拉一條網路線需要 12 分鐘，拉六條就是 72 分鐘，我絕對不能花超過一個小時在「撥網路線」這件屁事上，因此就是練，練到有肌肉記憶，最終拉在 5 分鐘左右，六條那就是 30 分鐘，足足少了一倍不止，在追求完美和速度的賽場上，一分一秒都是致命的。&lt;/p&gt;
&lt;p&gt;在工具方面，我們也有講究，電腦放中間、延長線放後面、焊槍放右邊、工具放左邊，目的就是為了做到萬無一失，儘量將練習環境及狀況控制到和賽場一致，降低出錯的可能性，也會更加熟悉流程。&lt;/p&gt;
&lt;p&gt;在飲食方面，我自賽前兩週便都只吃 7-11 的沙拉，並且確認肚子一切正常，都不會出問題，為的就是要排除比賽當天因為飲食導致失利，只要一有狀況，可能就跟獎盃離很遠了，因此這種細節也是我所重視的。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/L4dRPvy.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;身心俱疲&lt;/h2&gt;
&lt;p&gt;我在比賽倒數一個月半便被科上抽離（選手皆是如此），抽離至選手室，全心投入於競賽中。&lt;/p&gt;
&lt;p&gt;每天行程：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;時間&lt;/th&gt;
&lt;th&gt;事項&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;8:00 ~ 12:00&lt;/td&gt;
&lt;td&gt;練習&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12:00~12:40&lt;/td&gt;
&lt;td&gt;吃飯&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13:00-17:00&lt;/td&gt;
&lt;td&gt;練習&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17:00-17:40&lt;/td&gt;
&lt;td&gt;吃飯&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17:40-21:20&lt;/td&gt;
&lt;td&gt;練習&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;21:30&lt;/td&gt;
&lt;td&gt;收工回家&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;大概持續了一個月半，練習都是單獨練習，雖說選手室也有另一位選手，但我們是不同職種，也頂多累的時候聊一兩句，僅此而已，因此枯燥乏味的選手練習時光幾乎只有電腦和焊接版陪伴著自己，喔對，還有 Google。&lt;/p&gt;
&lt;p&gt;但我也很喜歡在吃飯時間，找電子科的兩位同學一同去吃飯，在去吃飯的途中，我們常常會聊到今天練了什麼，雖然各自都是不同職種，各自都有自己的故事，但在此時，我們卻能體會到彼此的心情，向對方宣洩著內心孤獨、壓抑已久的心情，那大概是僅次於領獎外，最開心的一段時光吧。&lt;/p&gt;
&lt;p&gt;在一天的練習結束之後，我便會搭上瑞芳往基隆 788 客運的倒數第二班車，夜色朦朧，我的心很累，我的身也很累，常常在坐車時就覺得好累，此時我就會幻想，自己站在頒獎台上，鋪上校旗、手捧獎杯，萬眾矚目的模樣，想著想著頓時就覺得現在的累也沒什麼了，或許繼續撐下去也不是不行，若是現在的努力能夠換取之後的成就，這算是筆很不錯的交易！&lt;/p&gt;
&lt;p&gt;但我想抱怨的是，788 常常會跳班，導致我坐不到公車，有時候會從 9:20 等到 10:30 的末班車，每次心裡都在幹幹叫，這什麼破偏鄉，而且當時我又還沒滿 18，也不能騎機車回家，所以常常會在路上等呀等呀，這時為了打發時間，我就給自己下了個決定，不然，把英檢中高級考了？&lt;/p&gt;
&lt;p&gt;於是乎，我就開始抱著高中五六級的單字開始背了，他也是陪伴了我好一段時間的枯燥等車利器，因此我在那段時間，不只準備比賽，還順道準備英檢（最後有過）。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/mGSgGbo.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;警衛&lt;/h2&gt;
&lt;p&gt;提到我的選手生涯，我一定會想到學校的警衛，因為我常常是學校最後一個走的，因此常常會跟警衛聊天，他們也是非常辛苦，三更半夜要去鎖保全，有辦公室沒關窗戶或門，他們就要重新跑回辦公室一趟，再次檢查門窗。&lt;/p&gt;
&lt;p&gt;有次，因為公車跳班，因此完全沒車可以搭，此時！警衛剛好要回家，便騎著他的小摩托出場了，騎到公車亭前面跟我說：「走，我帶你回家！」&lt;/p&gt;
&lt;p&gt;於是乎，難忘的經歷便在此刻寫下，他給我他另一頂小瓜皮帽，我們就這樣騎到了我家旁邊的公車站，還記得那時候大概 10:40 吧，真的非常感謝他載我回家，不然我就要叫計程車了... 窮學生還真不捨得花呢。&lt;/p&gt;
&lt;p&gt;可惜的是，沒多久他便因病退休了，就再也沒看過他了，但他依舊在我心裡留下了足跡。&lt;/p&gt;
&lt;p&gt;我的選手生涯中，總共結識了三位警衛，有時候公車不可靠，~我就只好投向警衛的懷抱~，我會陪他們一起鎖保全，鎖到十一點，然後載我回家，在路上，我們便會一起聊天，也感覺得出他們跟我一樣挺孤單的，因此他們走在路上話匣子都停不下來，常常說哪個處室都不關門啊，哪個老師在摸魚啊，超好笑的，也有說那個老師人最好之類的話，為無趣的「鎖保全之路」增添了些許色彩。&lt;/p&gt;
&lt;p&gt;寒假我還有再回去找他們聊天，他們也很開心的跟我分享，這段時間發生了什麼，彷彿又回到了當時，一年前的我們，也讓我很珍惜這段回憶，雖然僅僅是支線劇情而已。&lt;/p&gt;
&lt;h2&gt;模擬賽&lt;/h2&gt;
&lt;p&gt;其實，我認為我的模擬賽是一路順遂的，幾乎是每次霸榜，頂多有一次因為確診，拿了第二，其餘在新北市模擬賽中都是拿第一的，其實我覺得比到後面就是看誰反應力快、臨機應變能力靈活罷了，畢竟隨著時間推移，大家都漸漸把相關知識備齊，只差熟悉度和應變能力而已，我也沒有因為自己在模擬賽中皆拿首席而感到驕傲或自滿，我仍然認為外面的人更強，若是輕敵了，那比賽時我就有可能會收到嚴重的打擊。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/kmyLafg.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;或許是因為來自偏鄉，又有接觸過外面世界的關係，我了解不能只將目光聚焦於眼前，往往目光所及之外的才是挑戰，這也能應用在選手心態上。&lt;/p&gt;
&lt;p&gt;因此我直到比賽前，我都仍然埋頭在補齊自己的缺點，我也會羅列自己的弱勢，並去修正，希望讓自己成為一名「無懈可擊」的選手。&lt;/p&gt;
&lt;p&gt;當時，我會為了一項技能而蹲下去詢問別的選手，雖然他的能力不如我，但那又怎樣？我只知道一件事，他會我不會的東西，因此我向他請教，只要能讓我進步，厚著臉皮過去問何來不可？&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/q49X69f.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;這便是我的態度。&lt;/p&gt;
&lt;p&gt;至於我當上下一屆的講師，那就是&lt;a href=&quot;https://blog.vicwen.app/posts/newtaipeicity-computer-repairment-camp-lecturer-records/&quot;&gt;另一個故事&lt;/a&gt;了...。&lt;/p&gt;
&lt;h1&gt;工科賽&lt;/h1&gt;
&lt;h2&gt;比賽前一天&lt;/h2&gt;
&lt;p&gt;該來的最終還是來了，為期兩天的工科賽在南港高工來開序幕，我們競賽前天便扛著比賽工具和電腦衝往會場佈置場地，也去熟悉了一下比賽現場狀況，人數還是一如既往的多，工科賽前幾名多人的職種，61 位選手齊聚於此，準備在明後天一同角逐名次。&lt;/p&gt;
&lt;p&gt;我在車上跟文憲隨意聊天，他就問我說：「想拿第幾名？」我立刻回答道：「肯定是冠軍啊，今天我們抱座金手獎第一名回來給你看看，你應該沒看過吧。」&lt;/p&gt;
&lt;p&gt;我們笑得很開心，只為了沖淡比賽的緊張氣氛。&lt;/p&gt;
&lt;h2&gt;第一天&lt;/h2&gt;
&lt;p&gt;第一天比的是早上第一站：伺服器架設、網路管理，以及下午學科。&lt;/p&gt;
&lt;p&gt;一進去會場，就感受到大家的肅殺之氣，大家都屏氣凝神，我也趁這時候趕緊確認資料和工具情形，等待著比賽的到來，文憲也在一旁給我加油打氣。告訴我一些細節，提醒我一定要小心謹慎，不要疏忽，有任何問題也要趁講解問題時趕緊問。&lt;/p&gt;
&lt;p&gt;時間一到，指導老師全部撤去外面，淨空會場，我們被帶到會議室中宣讀比賽規章及競賽內容，看到試卷的當下，我估自己會是錯一個項目或是能全對，因為除了必考題外，有兩個看起來是有鑑別度的項目，一個是 NTP Server 和 windows Sandbox。&lt;/p&gt;
&lt;p&gt;前者我碰過，在民國 105 年的試題上曾經出過，巧合的是，我還真的只練到那年而已，再往前便是 Linux 為 Server，不符合當前時光背景的考題，因此，賺大了...。&lt;/p&gt;
&lt;p&gt;第二個是 Sandbox，完全沒碰過，但我大概猜得出他在哪裡安裝，就靠當下的臨機應變吧。&lt;/p&gt;
&lt;p&gt;試題講解完後，選手各自回位，開始倒數計時，三個半小時，大大的紅色數位鐘就在我的右上方，一瞥眼就看得到時間一分一秒的在流逝，我認為前期最痛苦的莫過於要撥六條網路線...，撥到受不了，也太多條，以往都只有四條而已。&lt;/p&gt;
&lt;p&gt;做著做著，該做到都做了，最後便是來研究 Sandbox 了，經過我的努力，完成了！並且還有剩餘一個半小時的時間，經過數次的檢查後，我便舉手等待評分。&lt;/p&gt;
&lt;p&gt;所有項目評下來，我自認會全對，但卻漏了一個地方，使用者名稱...，我來講解一下，總共要建三種類型的使用者，其中兩種的編號是 12345，中間那種居然要 0102030405，超級靠北，就因為這樣我被扣了十分+時間分數十分，總計二十分，沒想到最難的我沒錯，反而錯這種搞怪題，當時心情真的很差，就因為自己的小疏忽搞丟了 20 分。&lt;/p&gt;
&lt;p&gt;吃完飯後，就接著下午的學科，我覺得沒很難，當時估大概 90 左右。&lt;/p&gt;
&lt;p&gt;回去的時候跟文憲講了這些，他跟我說，已經很好了！回去好好準備明天的考試項目，才能力挽狂瀾！此時，心情就有好一些了。&lt;/p&gt;
&lt;h2&gt;第二天&lt;/h2&gt;
&lt;p&gt;第二天比的是第二站：Arduino, VB.NET 程式設計、焊接電路板，下午結語、公布部分成績。&lt;/p&gt;
&lt;p&gt;第二天我決定不管昨天發生了甚麼，我就是專注於當下，對於第二站我有十足的把握，因為我本身對於程式就花了多功夫了，因此我也預估自己是拿滿分。&lt;/p&gt;
&lt;p&gt;比賽開始後，我其實覺得題目出得很模糊，但我們是只能盡量去猜評審再想什麼 XD，時間總共三個半小時，我花了四十到五十分鐘焊接，四十分鐘打 code。&lt;/p&gt;
&lt;p&gt;滿可惜的是，我沒有猜到 code 部分，所以幾乎是打掉重寫，所以寫了 40 分鐘，若是有猜到我想可以再壓縮時間。最後加上檢查，我是用一個小時四十分鐘結束第二站，距離比賽結束還有兩個小時，於是我就鼓起勇氣舉起手要求評分了 XD。&lt;/p&gt;
&lt;p&gt;在評分的過程中，其實有些地方裁判所想表達的跟我所思考的是有出入的，但在評分時，我們是有機會去闡述自己的想法，「說服」評審裁判的，因此我有兩個環節就在跟他爭辯 XD，因為我是照著文字敘述去實作的，真的要說的話，不能說我是錯的。也幸好我有跟他解釋，才保住我的分數。&lt;/p&gt;
&lt;p&gt;驚險的評分完後，便抱著愉悅的心情前往會議廳，準備等待比賽的結束~&lt;/p&gt;
&lt;p&gt;滿有趣的地方是，我是第一位進去會議廳的（想當然），第二位大概隔了半小時才進來，第三位又隔了半小時，後面才陸陸續續有其他人進來，所以我在裡面滿無聊的，也沒手機可以玩 QQ。&lt;/p&gt;
&lt;p&gt;當時粗估自己大概一樣是前五，但具體第幾就真的不確定了，當時是覺得第一站失誤，可能會沒有第一，但又覺得沒第一真的對不起自己，所以在等待的過程中，心是滿煎熬的。&lt;/p&gt;
&lt;h2&gt;戰歸&lt;/h2&gt;
&lt;p&gt;等到大家皆就緒後，指導老師也陸續進場，一位選手一位指導老師坐在會議廳中，等待評審團的公布，前面就是說大家辛苦了~選手只是一個經歷，各位要繼續加油…等等。&lt;/p&gt;
&lt;p&gt;接下來就開始公布成績，他說了一句話，讓我如釋重負：「本次第一站最高分為八十分，因此第二站加權會全體加二十分，最高補至一百。」我聽到這句，快哭了，這樣不就代表我第一站最高嗎！而且我第二站全對，因此幾乎確信我就是第一名了。&lt;/p&gt;
&lt;p&gt;等到走出會議廳外，收拾東西準備返回瑞芳，赫然發現外面貼著學科的答案，我估算了一下，我拿到 92 分，跟我預估的差不多，我就抱著愉悅的心跟文憲回去學校了~（晚上還有慶功宴）。&lt;/p&gt;
&lt;p&gt;但我還暫時沒有伸張 XD，等到隔天成績公布再說吧~。&lt;/p&gt;
&lt;h2&gt;努力，獲得了回報&lt;/h2&gt;
&lt;p&gt;今早，我們全體前往了南港展覽館，準備領獎囉～&lt;/p&gt;
&lt;p&gt;紅衣服的選手隊伍們，好不威風（我到畢業前都還會穿去學校，其實滿好看的）。&lt;/p&gt;
&lt;p&gt;在等的過程中，拿到了這張：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/qdkEkLd.jpg&quot; alt=&quot;獲獎名單&quot; /&gt;&lt;/p&gt;
&lt;p&gt;只要有取得優勝以上的人都會被列在上面，然後就只有三個人被明顯的畫上粗體字 XD，聰明的人應該很快就知道了，這是前三名的意思，也讓我期待等等頒獎的畫面。&lt;/p&gt;
&lt;p&gt;頒獎前，我們還留了一張照片！我其實只認識第二名和第四名而已 XD，我覺得這張照片很讚，幸好我有好好留存呢。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/FUDCI6D.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;上台前，因為我們都已經知道各自的名次了，所以都已經排好，我就直接站在最後一個 XD。然後，那個主持人叫我演一下！不然沒有拿冠軍的氛圍，於是才有了以下的片段：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/4w8qZDo.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我雖然很高興，但畢竟是提前知道分數了 XD，所以我只是配合演出而已，實際上本人沒那麼誇張。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/7G26IUI.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/5zL2Qqi.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;站上去的時候，我還朝著文憲說：「&lt;strong&gt;我們做到了&lt;/strong&gt;」。&lt;/p&gt;
&lt;p&gt;同時也為瑞工和資訊科留下了足跡，我是創科 21 年來，首位拿金手獎第一名的！
聽到其實滿訝異的，但也很高興自己有努力到最後，為自己的選手生涯畫下了完美的句點。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/Pr7tImW.jpg&quot; alt=&quot;下台的時候還抱著獎盃拍了一張&quot; /&gt;&lt;/p&gt;
&lt;p&gt;很高興的是，我們的許大姊後來也拿冠軍，於是我們兩個還合拍了一張。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/qgijdQo.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;後來也偷偷的拿到自己的成績，原來我學科也是最高的）？&lt;/p&gt;
&lt;p&gt;加權直接拿好拿滿&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/TM1rrbS.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;感恩餐會&lt;/h1&gt;
&lt;p&gt;新北市可以說是最重視高職選手的縣市也不為過，光是各種福利和資源，市長都是非常支持的！很高興能夠代表新北市，為市爭光！&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/fLBFs3S.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;甚至也讓得獎的同學能夠前往各國（我是去澳洲）進行海外見學，但只有前兩名是免費的，其他名次要擔一些費用，因此是新北爸爸帶我出了第一次國 XD。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/Y5zLf8Q.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我們學校的選手也幾乎都有取得名次，甚至今年出了兩個冠軍，偷偷說，校長那天喝很ㄎㄧㄤ...。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/kHAGLF9.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;結語&lt;/h1&gt;
&lt;p&gt;很高興自己努力了數月，迎來了最美好的結局。&lt;/p&gt;
&lt;p&gt;我認為的競賽=90%努力+10%運氣，因此我認為我是位很幸運的人，我只能夠靠努力將自己的排名衝向前面，但卻是那 10%的運氣帶領我到首席。&lt;/p&gt;
&lt;p&gt;選手，是個沉重的包袱，背負著自己、朋友、親人、師長，甚至是校方，將力量集結於一人，為的就是在競賽的殿堂中嶄露頭角。&lt;/p&gt;
&lt;p&gt;當然，除了拿到冠軍，有了頭銜外，也有獎金、出國機會、人際圈增廣，等等諸多的好處，不僅僅只是「埋頭苦練只為了拚一個飄渺的名頭」，這段時間也帶給我非常多的改變，不只是心態上，變得更加沉著、穩定，抑或是面對大軍來襲仍面不改色的那份堅毅信念；還有想法上，也讓我結識了許多技藝競賽、技能競賽的選手，位位都是技能方面的頂尖好手，讓我這位原先只是單純的「考試生」，領悟到：高職的頂尖好手原來是這麼競爭激烈且殘酷，完完全全的顛覆了我的世界觀。&lt;/p&gt;
&lt;p&gt;雖然選手的經歷已經是一年前的事了，但在撰寫文章的當下，翻看著過往的照片，回憶皆一一浮現於腦中，彷彿是昨天我才剛練完一次試題，還在準備迎接比賽的心情。&lt;/p&gt;
&lt;p&gt;殊不知，早已過去了許久，甚至澳洲也去過了，德國也去過了，也早已升上了大學。&lt;/p&gt;
&lt;p&gt;最後，很感謝一路陪伴我的女友、朋友、師長、父母、並肩作戰的選手們，若沒有你們的鼓勵，我恐怕無法撐到現在，是你們成就了我，深深一鞠躬。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/iS0BysX.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;若你想知道比完選手後我做了什麼，可以透過傳送門前往~&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.vicwen.app/posts/newtaipeicity-australia-records/&quot;&gt;澳洲之旅&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;德國之旅（還在還債中...）&lt;/p&gt;
</content:encoded></item><item><title>RECAP：回顧2023</title><link>https://vicwen.app/posts/2023-recap/</link><guid isPermaLink="true">https://vicwen.app/posts/2023-recap/</guid><pubDate>Sat, 06 Jan 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;前言&lt;/h1&gt;
&lt;p&gt;今年可以說是目前為止，改變我最多、發生最多事的一年了，也希望從 2024 開始記錄前一年的所見所聞，將改變自己、幫助過我的人事物、難忘的經驗，都記錄下來，留給未來的自己回味。&lt;/p&gt;
&lt;h1&gt;紀錄&lt;/h1&gt;
&lt;p&gt;因為看到身邊的同學有寫 Recap 的習慣，所以我也來學他（學學怪）。
接下來我會以月份來做整理！&lt;/p&gt;
&lt;h2&gt;一月&lt;/h2&gt;
&lt;h3&gt;模擬考&lt;/h3&gt;
&lt;p&gt;新的一年來了，也是我準備登陸大學的年份，這時正屬高三下，統測最後衝刺的時段，我也正在準備模擬考，雖然自從當時準備工科賽學科，順便準備模擬考，考過全國 18 名後，我的成績就成直線下滑了...但我覺得有一部份原因是，我心中認為應該要將時間擺在提升自己專業能力上，而不是準備枯燥乏味的升學考試，因此我將時間分配成七三，&lt;strong&gt;七成在於提升自我，三成在於準備統測，升上大學後，事實證明我是對的。&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;想接家教的心&lt;/h3&gt;
&lt;p&gt;那麼我為什麼還會想去準備統測？甚至科上的老師都說我不用準備了，有一大原因也是因為想在暑假的時候開始接家教，為了家教，我還是需要拿出一點自己的大考成績。&lt;/p&gt;
&lt;p&gt;其實在我國小國中的時候，我的夢想是成為一名老師，我很喜歡教書育人的感覺，但這一路上看過太多屁孩了，搞出來的事太多了，整個勸退我想從事老師的心 XD，那我乾脆去當家教就好了，賺賺外快，教教書，生活豈不美哉？&lt;/p&gt;
&lt;h3&gt;繁星%數公布！&lt;/h3&gt;
&lt;p&gt;苦撐了兩年半，努力換來了成果。&lt;/p&gt;
&lt;p&gt;曾經的我，邊背題庫，邊背體育，邊背國防，邊靠邀教育體制的荒唐，忍受著競爭力低下、教育資源貧脊的環境，咬著牙苦撐過來，終於到了繁星採計校內成績的期限。&lt;/p&gt;
&lt;p&gt;結果公布後，校內取六項採計成績，總成績、實習、專業科目、國英數....等等的成績，我皆為&lt;strong&gt;校內 1%&lt;/strong&gt;，雖然說這是毫不意外的，但看到後真的鬆一口氣，因為只要%數一出來，若都是 1%，臺科是穩穩地上的，因此我已經是整隻腳跨入臺科的門口了，可以用躺著進去了。&lt;/p&gt;
&lt;p&gt;但我其實不滿足於此，我希望不只是上去臺科這麼簡單，我想拿到的是全國不分群類總榜首，因為當時我的第七比序（競賽、證照、檢定）積分是 72 分，我有工科賽金手一、GEPT 中高聽讀、四張丙級、兩張乙級證照，可以說是拿好拿滿，我當時也相信沒有人能夠高過於我的積分，畢竟光是要顧好課業就是一大壓力，若要在取得競賽冠軍之類的成績，幾乎是鳳毛麟角，所以我就抱持著等待五月公告放榜，公布我是榜首的心情在等待 XD，但...看到後面就知道了，我們先將這件事放到一邊。&lt;/p&gt;
&lt;h2&gt;二月 三月 四月&lt;/h2&gt;
&lt;p&gt;這段時間一直在探索我的興趣愛好，主要在做兩件事，一：學習資料結構和演算法，二：決定我要選臺科資工還是電子 XD，甚至還跑去電子科拿 FPGA，寫 Verilog 來玩，也問過很多人的意見，最後還是選擇了朝資工的領域發展。&lt;/p&gt;
&lt;p&gt;還有跟著班上一起寫題本，但我也沒甚麼心在上面，模擬考成績頂多在國排五十到一百之間徘徊，最後心願就是總分拿個 610，然後在國排一百內就好。&lt;/p&gt;
&lt;p&gt;在四月的時候也收到了新北市教育局的技職海外參訪通知，我們即將在考完統測的隔天，飛往澳洲兩周！&lt;/p&gt;
&lt;p&gt;人生中第一次出國居然是因為自己得獎 XD，被教育局爸爸帶出國，真...真好。&lt;/p&gt;
&lt;h2&gt;五月&lt;/h2&gt;
&lt;h3&gt;澳洲之旅&lt;/h3&gt;
&lt;p&gt;考完統測了！出國囉，詳細內容請看這篇：&lt;/p&gt;
&lt;p&gt;-&amp;gt; &lt;a href=&quot;https://viiccwen.github.io/NewTaipeiCity-Australia-Records/&quot;&gt;新北市薦送海外技職研習：澳大利亞之旅&lt;/a&gt; &amp;lt;-&lt;/p&gt;
&lt;p&gt;在這兩周，我們十二位小夥伴每天一起上課，一起吃，一起睡（這好像沒有），一起聊天，很高興能認識到它們，在澳洲也學到了許多，留下了很多美好回憶！&lt;/p&gt;
&lt;h3&gt;繁星放榜！&lt;/h3&gt;
&lt;p&gt;這算是我這一年少數的遺憾了，但我覺得也不是我不夠努力，因為這一切都太荒唐了...。&lt;/p&gt;
&lt;p&gt;我在當時公布榜單後，我在電子與電機群類中是 1/505，也就是電機與電子群的榜首，但是我在不分群中卻只排第四！這已經夠讓我驚訝了，更誇張的在後面，&lt;strong&gt;居然前面三位都來自同一間學校？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/zkPgpIs.jpg&quot; alt=&quot;繁星排名&quot; /&gt;&lt;/p&gt;
&lt;p&gt;後來才知道，原來這三位為了繁星的稱號，考了一堆其他類群的證照，用證照把我的排名擠下去，甚至第一名考到二十幾張證照？＿？&lt;/p&gt;
&lt;p&gt;我只能說，這什麼迷之操作，但後來想想就釋懷了，因為一般人不會這樣亂搞，也就只有私立學校為了衝榜單會搞這種沒意義的事，畢竟，你讓一個學資處的人去考印前製程和西點製作的證照不怪嗎？&lt;/p&gt;
&lt;p&gt;不過，有電機與電子群的榜首就知足了，也給了我一些底氣，走進臺科和其他大佬一較高下的底氣。&lt;/p&gt;
&lt;h2&gt;六月&lt;/h2&gt;
&lt;h3&gt;德國之旅&lt;/h3&gt;
&lt;p&gt;在前幾個月前，也收到了教育部的通知，準備帶領工科賽冠亞軍的同學一同前往德國進行二周的技職參訪。&lt;/p&gt;
&lt;p&gt;沒錯！又可以出國囉！（興奮轉圈圈）&lt;/p&gt;
&lt;p&gt;但我因為時間太忙，德國之旅的心得債還沒還完哈哈哈，等我把債還完了，會在更新網址的，各位德國團的同學在等等我！&lt;/p&gt;
&lt;p&gt;在這趟旅途中，又認識到更多大佬，因為大家都是職種的冠亞軍，個個都是技職的前端學生，甚至在進臺科後，也常常會遇到熟面孔 XD。&lt;/p&gt;
&lt;h2&gt;七月 八月&lt;/h2&gt;
&lt;h3&gt;家教&lt;/h3&gt;
&lt;p&gt;由於統測有達到自己預期，在國排前百內，成績也算相對漂亮（除了數學炸掉 QQ），因此也開始上家教社團找學生~。&lt;/p&gt;
&lt;p&gt;俗話說：「萬事起頭難。」我真的在此時感受到了，&lt;strong&gt;家教社團上每個都建中、北一女、附中等等明星學校&lt;/strong&gt;，就我一個臺科額外矚目，也數次懷疑人生，&lt;strong&gt;思考我這樣真的找的到學生嗎？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;但我還是不放棄，隔一周發一次文，經過十五天的煎熬等待，最後很幸運找到了兩位學生！也很高興是在自己的家鄉找到的，就不用跑到台北去教書，費心神又費錢。&lt;/p&gt;
&lt;p&gt;也在這時候體會到說話的藝術和優勢，由於我本身國中就在學生會裡打轉，常常會和大人們打交道，因此在說話、表達方面很有優勢，在家教開始沒多久便順利上手了～和學生、家長都處的還不錯，也幫自己存了一些錢。&lt;/p&gt;
&lt;h3&gt;特聘講師&lt;/h3&gt;
&lt;p&gt;這部分也有做成專門的文章，詳細內容請看這篇：&lt;/p&gt;
&lt;p&gt;-&amp;gt; &lt;a href=&quot;https://viiccwen.github.io/NewTaipeiCity-ComputerRepairment-Camp-Lecturer-Records/&quot;&gt;112 學年度新北市電腦修護金手培訓營&lt;/a&gt; &amp;lt;-&lt;/p&gt;
&lt;p&gt;很高興今年能夠以講師的身分回到熟悉的地方，傳授自己的親身經歷及小撇步給台下的學生~。&lt;/p&gt;
&lt;h2&gt;九月 十月 十一月 十二月&lt;/h2&gt;
&lt;p&gt;接下來四個月可以說是過得非常充實（爆肝）呢...。&lt;/p&gt;
&lt;h3&gt;同儕&lt;/h3&gt;
&lt;p&gt;高職小孩準備踏入夢寐以求的學校就讀，一切都是那麼的美好，尤其是學餐 XD。剛開始新生茶會就認識到很多很有料的同學，也讓我對未來充滿著憧憬，未來彼此一起成長，一起競爭。&lt;/p&gt;
&lt;p&gt;然後我甚至選上了副班代）x。&lt;/p&gt;
&lt;h3&gt;宿舍&lt;/h3&gt;
&lt;p&gt;首先，男生+新生，那注定不能住太好的嘛，肯定是要住最破最舊的，是吧。&lt;/p&gt;
&lt;p&gt;因此我入住了二宿六人房，我們房間的成員組成是：四位資工系和兩位建築系。&lt;/p&gt;
&lt;p&gt;剛進去的時候，大家都超安靜 XD，就我跟柏潁和思遠兩位大佬在講話，不過大家都是好室友，不會做出什麼很誇張的事情，我甚至覺得我是房間最吵的人==有時候動靜都太大，感覺有吵到別人，但我不是故意的 QQ。&lt;/p&gt;
&lt;p&gt;也很高興自己有住進宿舍，在每個夜深人靜的夜晚，總有人陪我奮鬥，雖然聽起來有點哀怨就是了...。&lt;/p&gt;
&lt;h3&gt;社團&lt;/h3&gt;
&lt;h4&gt;熱舞社&lt;/h4&gt;
&lt;p&gt;當初進去前就在想，肯定是要加舞社的吧...。&lt;/p&gt;
&lt;p&gt;所以我就加了。&lt;/p&gt;
&lt;p&gt;但我只有將跳舞當個興趣、運動在跳，沒有花太多時間在舞蹈上，因此我很多活動都沒參加，但沒辦法，有捨才有得 QQ。&lt;/p&gt;
&lt;h4&gt;NTUST GDSC&lt;/h4&gt;
&lt;p&gt;我還有參加臺科的 GDSC(Google Developer Student Clubs)，開源研究社，在裡面學習一些 Google 的工具使用，也透過這社團認識到了很多各領域大佬，算是一個開拓視野、關係的跳板)?。&lt;/p&gt;
&lt;p&gt;但我覺得有點小可惜的是，臺科 GDSC 在社團經營上貌似沒有下很多力氣，甚至沒有專案開發的機會，我只能未來跑其他學校的 GDSC，或是等之後自己上去接幹後，再來開一些專案機會，這算是我的未來布局。&lt;/p&gt;
&lt;p&gt;甚至還去參加了交流會：&lt;a href=&quot;https://viiccwen.github.io/GDSC-NorthDistrict-13-Interaction/&quot;&gt;2023 年北區 13 校 GDSC 聯合交流會&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;家教&lt;/h3&gt;
&lt;p&gt;上大學後，還是在咬牙撐著，甚至又接了一位學生，因此我假日要兩頭跑，很多系上、社團的活動都沒參加到，滿可惜的 QQ。&lt;/p&gt;
&lt;p&gt;不過升上二年級後，應該就把基隆的學生 case 收掉了，不然身心靈真的會嚴重受創。&lt;/p&gt;
&lt;p&gt;後來也發現，原來累的不是上課，反而是通勤，光週五回基隆就塞車塞到爆，兩個半小時才到家，既厭世又累，真的會瘋掉。&lt;/p&gt;
&lt;p&gt;但我覺得大學還是要有份外快，這樣才充實)?。&lt;/p&gt;
&lt;h3&gt;助教&lt;/h3&gt;
&lt;p&gt;當初在頭殼版看到有老師在徵求助教，身為小大一，鼓起勇氣履歷就給他投下去了 XD，真的超瘋狂。&lt;/p&gt;
&lt;p&gt;在開學第一天就跑去 T4 面試，我當時甚至不知道教室編號開頭是樓層 XD，也跟那位應外系的老師聊過很多，雖然最後沒有錄取（我原本就沒想過會錄取），但那位老師說對我印象非常深刻，將我介紹到另外老師底下當助教，於是就開啟我的大一助教生活了...。&lt;/p&gt;
&lt;p&gt;但私心來說，我覺得第二位老師的缺才是好...，首先遠端，不用到課堂上課就已經打趴 99%校內工讀了，對我這種忙人來說可以說是夢幻工作==；再來就是涼，頂多打成績、到 Moodle 上登記分數，其他就沒啥事要做了，每個月等領錢，超讚！&lt;/p&gt;
&lt;p&gt;非常不後悔當初有投出那份履歷！&lt;/p&gt;
&lt;h3&gt;課業&lt;/h3&gt;
&lt;h4&gt;微積分&lt;/h4&gt;
&lt;p&gt;我當初真的超怕我的微積分變成危機分==，因為我數學說實在真的沒有很好，因此我只好當個乖寶寶，乖乖寫作業，乖乖交 Bonus 來加分。&lt;/p&gt;
&lt;p&gt;期中的時候甚至心態有點崩，因為我才考 91/120，旁邊個個都比我高，臉超僵，也意識到：&lt;strong&gt;其實自己沒有多厲害。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;但在期末的時候，我好像太沉浸於 coding 了==，微積分根本沒進度，剩下一周才意識到：「要完蛋了...」。&lt;/p&gt;
&lt;p&gt;於是，經過我一周的追趕，從第六章追到第七章結束，考到還不錯的分數，89 分，最高好像才 96 分而已，最終以微積分(上)總成績 A+以及滿分完結離場~，期待微積分(下)的表現 XD。&lt;/p&gt;
&lt;h4&gt;資工導論&lt;/h4&gt;
&lt;p&gt;我真的不知道這堂課在幹嘛...，就聽老師說傅立葉級數、拉普拉思轉換、JPEG 編碼、美國大學排名、祖刻薄蜥蜴人會不會說中文。&lt;/p&gt;
&lt;p&gt;最痛苦的是，他說：「有講過的都會考喔～」，內心真的崩潰無比，期中考我甚至準備他的時間比微積分多，因為只有選擇不多，幾乎都是簡述題。&lt;/p&gt;
&lt;p&gt;最後我拿到了，99/110 分...，最讓我難以忘懷的是成績分布圖，總共 102 人，有接近四十個拿 90 以上？，我才排第十五名而已？？&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/JlRbXFx.jpg&quot; alt=&quot;臺科資工導論排名&quot; /&gt;&lt;/p&gt;
&lt;p&gt;這對我的心靈衝擊又上了另一個層面。&lt;/p&gt;
&lt;h4&gt;通識課&lt;/h4&gt;
&lt;p&gt;就是一坨報告課+表達課，我這學期寫的報告字數（7000+6000+3000+1200+2500），大概比我前面 18 年加起來的字數還要多==，我有可以很肯定，我花在報告上的時間，絕對可以做一個專案出來==。&lt;/p&gt;
&lt;p&gt;但我覺得我選的通識都是有學到東西的，隊友也都挺 carry 的，最後也都順利 A+下船～可喜可賀！&lt;/p&gt;
&lt;h3&gt;檢定&lt;/h3&gt;
&lt;h4&gt;CPE&lt;/h4&gt;
&lt;p&gt;由於計算機程式設計老師說可以加分，&lt;strong&gt;一題可以加總成績五分&lt;/strong&gt;，那我們怎麼可以錯過可以薅羊毛的機會呢ㄎㄎ，肯定要給他刷爆的啊。&lt;/p&gt;
&lt;p&gt;因此我去考了兩次，兩次都寫 4/7 題，加了 20 分，總成績直接加到 11x 分，老師直接在上課點名我們這種不講武德的人 XD，還叫我們不要來考期末考了（但我還是有去啦哈哈）。&lt;/p&gt;
&lt;p&gt;但我其實滿意外的，原本以為自己頂多兩、三題，結果寫出四題==，第一次前 2.3%，第二次直接 0.9%，等於我是當次的 PR99...，超扯。&lt;/p&gt;
&lt;h4&gt;GPET 中高說寫&lt;/h4&gt;
&lt;p&gt;我很肯定這絕對不會過 XD，我太菜了，我甚至有點不想再考的想法，因為這只有在台灣有用，不如拿去準備多益、托福、雅思...&lt;/p&gt;
&lt;h3&gt;比賽&lt;/h3&gt;
&lt;h4&gt;NCPC&lt;/h4&gt;
&lt;p&gt;我只能說，當時還太菜了，自己太菜，隊友也要好好找...。&lt;/p&gt;
&lt;p&gt;內心已經有適合的人選，應該會再去打，至少要打到 ICPC 吧==。&lt;/p&gt;
&lt;h4&gt;ITSA&lt;/h4&gt;
&lt;p&gt;我和 bubu 兩人組隊去打，但這比賽真的很奇妙，初賽居然可以用網路）？&lt;/p&gt;
&lt;p&gt;很理所當然的，我們就入圍決賽了。&lt;/p&gt;
&lt;p&gt;決賽時，由於地點在台南，我們甚至還去找教授（金主），希望能夠贊助我們的車費和住宿費，不然我真的不會想要去比...。&lt;/p&gt;
&lt;p&gt;幸好最後文凱教授人真的超好，願意資助我們，我真的超感動 QQ。&lt;/p&gt;
&lt;p&gt;備賽時，我們依照前幾年的排名去估，想說拿個前三名應該很輕鬆。沒想到，一屆比一屆難打==，我們甚至只有拿第五名，雖然是我在雷啦...。我跟 bubu 鄭重道歉。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/3czi6ln.jpg&quot; alt=&quot;我和成大的合照&quot; /&gt;&lt;/p&gt;
&lt;p&gt;但我們還是有拿到一人 2500 的獎金（慰問金）啦。&lt;/p&gt;
&lt;p&gt;最後，這也算是開啟我網頁之路的踏門磚！&lt;/p&gt;
&lt;h3&gt;食物&lt;/h3&gt;
&lt;p&gt;臺科食物真的超讚的啊啊啊，三間餐廳，總共大概 20~45 家店家可以選擇，自助餐能花少少錢吃很飽，火鍋、牛丼也都便宜好吃，甚至還有健康餐盒！想吃好吃的炸雞、豬排、拉麵，公館也吃的到，跟台北其他地方相比，也是超便宜的==，為了大家著想，食物詳情請去 IG 上看哈哈哈。&lt;/p&gt;
&lt;h1&gt;結語&lt;/h1&gt;
&lt;p&gt;今年真的非常多采多姿，不只是自己心態上的轉變，抑或是認識了很多身懷絕技的同儕，能跟自己一同成長、一同進步，還是出國見學，這都是非常難得的經驗，希望未來一年比一年精彩！&lt;/p&gt;
</content:encoded></item><item><title>2023年北區13校GDSC聯合交流會</title><link>https://vicwen.app/posts/gdsc-northdistrict-13-interaction/</link><guid isPermaLink="true">https://vicwen.app/posts/gdsc-northdistrict-13-interaction/</guid><pubDate>Tue, 12 Dec 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;前言&lt;/h1&gt;
&lt;p&gt;在 2023/11/12（日）排除萬難，參加了 GDSC 北區 13 校聯合交流會，本來就是抱著想去跟人交流的心情，想去認識更多在各領域發光發熱的人，已激勵自己在這條路上越走越遠。&lt;/p&gt;
&lt;p&gt;活動值不值得呢？我只能說，這大概是我&lt;strong&gt;今年最感謝自己的決定之一&lt;/strong&gt;。&lt;/p&gt;
&lt;h1&gt;活動流程&lt;/h1&gt;
&lt;p&gt;10:00-10:15 報到&lt;/p&gt;
&lt;p&gt;10:15-11:30 各校 GDSC 分享社團規劃&lt;/p&gt;
&lt;p&gt;11:30-11:40 Firebase Auth(李致緯老師)&lt;/p&gt;
&lt;p&gt;11:40-12:00 破冰小遊戲&lt;/p&gt;
&lt;p&gt;12:00-13:00 午餐時間&lt;/p&gt;
&lt;p&gt;13:00-14:00 GDG 簡介(Kevin Chiu 老師)&lt;/p&gt;
&lt;p&gt;14:00-16:00 Dialogflow x LINEbot 工作坊(Kevin Chiu 老師)&lt;/p&gt;
&lt;p&gt;16:00-17:00 自由交流&lt;/p&gt;
&lt;h1&gt;各校 GDSC 分享&lt;/h1&gt;
&lt;p&gt;其實我以為 NTUST GDSC 的活動算是密集了，但是沒想到一山還有一山高，甚至可以用誇張來形容，像是師大、政大、淡江都有許多專案開發的作品及競賽經歷，看的出來在社團參與及技術含量的占比是非常高的，看到那一排的專案，也讓我生出了一些憧憬，也給了自己一個目標，希望&lt;strong&gt;在寒假可以找人共同開發專案和獨力完成自己的作品集&lt;/strong&gt;，給自己增加一些亮點！&lt;/p&gt;
&lt;h1&gt;Firebase Auth(李致緯老師)&lt;/h1&gt;
&lt;p&gt;這位大人物絕對稱得上傳奇...，在大學時期就創立了&lt;a href=&quot;https://www.inside.com.tw/&quot;&gt;INSIDE&lt;/a&gt;與&lt;a href=&quot;https://icook.tw/&quot;&gt;愛料理&lt;/a&gt;，現在是&lt;a href=&quot;https://www.thenewslens.com/&quot;&gt;關鍵評論網&lt;/a&gt;的執行長，我做報告還會從它上面的文章取材...。&lt;/p&gt;
&lt;p&gt;其實光從口才跟想法就能看出他為何成功了，雖然說大多都在介紹 Firebase，但他也有參雜一些創業的經歷、一路走過來遇到的困難和如何去化解，不只是能學到知識，還能聽到創業心得，真的超頂。&lt;/p&gt;
&lt;h1&gt;破冰小遊戲&lt;/h1&gt;
&lt;p&gt;玩 switch，也趁這時候認識旁邊的同學們（其中一位還是我們學校的學長呢）。&lt;/p&gt;
&lt;p&gt;然後有個很會吹口哨的師大資工同學，真的是讓我印象深刻呢哈哈哈，他說他是建中畢業，果然是大神，然後他很有做自己的感覺 XD，很像位藝術家。&lt;/p&gt;
&lt;h1&gt;GDG 簡介 &amp;amp; Dialogflow x LINEbot 工作坊&lt;/h1&gt;
&lt;p&gt;其實我覺得我們後來去吃飯，反而講 GDG 比這時候說的還要清楚 XD，總之就是在介紹 GDG，順便工商一下&lt;a href=&quot;https://gdg.community.dev/events/details/google-gdg-taipei-presents-devfest-taipei-2023/&quot;&gt;DevFest Taipei 2023&lt;/a&gt;哈哈哈。&lt;/p&gt;
&lt;p&gt;後面在學習如何用「零程式」架出一個 LineBot，雖然以前就有自己用 Python 開發過 LineBot，但我覺得 Dialogflow 是個還不錯的工具，相較之下，可以節省滿多時間的，但擴充性偏差就是了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/Cwtrw9r.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;也實作了一次「&lt;strong&gt;敏捷軟體開發&lt;/strong&gt;」XD，五個人用一小時搞出了個 LineBot，每個人分工合作，誰負責哪個區塊，誰負責處理 Linebot，最後終於搞定了，甚至還差點做不出來哈哈哈（但我還用了個 Slides，整個就昇華了）。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/gEBKENv.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;結束後的感想：原來是 Speed Run 啊...，我還以為是 GDSC 北區 13 校聯合交流會呢。&lt;/p&gt;
&lt;h1&gt;自由交流 &amp;amp; 驚豔&lt;/h1&gt;
&lt;p&gt;這次活動我真的鼓起很大勇氣，畢竟都是學長姊，也僅僅是萍水相逢，但我還是衝過去尬聊，不管是請教自己未來該如何發展，抑或是未來有想接&lt;strong&gt;NTUST GDSC CoreTeam/Lead&lt;/strong&gt;（希望），學長姐珍貴的經驗和建言都讓我學到非常多啊啊啊，真的超感謝他們。&lt;/p&gt;
&lt;p&gt;同時，我也很敬佩北醫和淡江的 Lead，小小聲說，他們現在是我的偶像，我也想成為一位不平凡的人！&lt;/p&gt;
&lt;p&gt;最後我還厚著臉皮說要跟他們去吃晚餐 XD。&lt;/p&gt;
&lt;h1&gt;Leader 和兩位小 Member 的晚餐聚會&lt;/h1&gt;
&lt;p&gt;我們跑到 A13 那邊吃火鍋，走超久的。&lt;/p&gt;
&lt;p&gt;在路上，我跟 Kevin 老師聊滿多的，他是位很 NICE 的老師，傾聽我的疑問，並且還給我一些職涯的想法，真的很感謝他，也希望未來仍然有見面的機會！&lt;/p&gt;
&lt;p&gt;還有認識位很會吃的學姐 XD，一個人可以吃掉六盤肉，超級誇張，我就看著他慢慢的看那六盤肉逐漸消失，明明人很小隻，真不知他是怎麼有辦法吃這麼多的...。&lt;/p&gt;
&lt;h1&gt;結語&lt;/h1&gt;
&lt;p&gt;最後，我只想說，很高興有來這場活動，給了我很多繼續奮鬥下去的動力，不只是單純交流這麼簡單，認識到許多貴人、開闊眼界，這些寶貴的人脈和經驗都是金錢絕對無法比擬的。&lt;/p&gt;
&lt;p&gt;也希望未來我能成為像他們一樣的人，一樣在舞台上閃閃發光，一樣在各領域中嶄露頭角，以此文共勉之。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/W8doQnP.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>112學年度新北市電腦修護金手培訓營</title><link>https://vicwen.app/posts/newtaipeicity-computer-repairment-camp-lecturer-records/</link><guid isPermaLink="true">https://vicwen.app/posts/newtaipeicity-computer-repairment-camp-lecturer-records/</guid><pubDate>Tue, 28 Nov 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;前言&lt;/h1&gt;
&lt;p&gt;自從去年拿下電腦修護職種金手一後，原本以為自己將會卸下選手服，專心投入大學，但在今年八月初，當初培訓我們這屆的講師突然找上我，由於事務繁忙無法擔任 112 年的新北市培訓營講師，因此希望我成為今年的講師。於是，就這麼陰錯陽差地又投入到了選手領域中，只是這次的視角是從台下移至了台上。&lt;/p&gt;
&lt;h1&gt;過程&lt;/h1&gt;
&lt;p&gt;接下講師後，當選手的感覺都回來了，皮一樣要繃緊、要猜考題，以及要出考題，只是這次是出給弟妹們寫。在培訓階段，開始準備投影片，公布元件、公布電路、比賽注意事項….等等，許多零零碎碎的事情都必須告訴弟妹們，同時也發現，選手們真的不容易，又想起那段爆肝的時光了…。從開始準備到完成講義資料，總共花了一周，修了又修，改了又改，只為了去蕪存菁，不想讓弟妹們看太多 docs，但也不想讓他們漏學東西。我也自己當時比賽的程式架構及 coding style 無償奉獻給了他們，明確的命名及乾淨清晰的程式撰寫區，完全可以大幅縮減程式撰寫、除錯的時間；過去由於被 VB6 荼毒，一氣之下便將程式全部移植到 VB .NET，這次也派上用場，讓全體都改成寫.NET，舒服多了…。&lt;/p&gt;
&lt;p&gt;也有人問我：「你把所有東西都給了別人，這樣不會有疑慮？」對於我來說，首先這職種程式撰寫占的篇幅沒有很大，我認為是無傷大雅的；其次是身而為師的那種心情，巴不得學生們學越多越好，更何況比賽其實取決於自己，肯努力、肯拚搏的人，在漆黑的夜晚仍掩蓋不住它的光芒，若少了我的協助，我可以很肯定，它的名次絕不會有太大的影響，人總會找到出路的。因此，就算幫一個學生把所有東西都準備好，程式都寫好給他，他不願付出，那肯定是當炮灰的，如同我的某位學長。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/6byKo4Q.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;困難&lt;/h1&gt;
&lt;p&gt;在第一次課程中，由於第一次帶這類性質的課程，時間跟節奏都沒有掌握的很好，甚至有人問到我沒想到的問題，其實對弟妹們滿抱歉的。於是在之後的課程中，我慢慢去修正自己的講話方式，去調整課程步調，同時評估學生狀況，去加深課程內容，從中也讓我學到了很多「教學」的撇步跟經驗，即使自己自認為自己很會「教別人」，但仍然有很大的進步空間。&lt;/p&gt;
&lt;p&gt;出模擬賽題目時，也是絞盡腦汁，每份前前後後修了不只十遍，尤其是 doc 最難改，動一個小環節，每個地方都要修，最後還要檢查數遍，確定不會出任何問題才敢闔上筆電去休息。比完還要依各位的程度去修改下份考題，只能說不是份輕鬆活 QQ。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/6ra2JPA.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/p8RSEvt.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;期許&lt;/h1&gt;
&lt;p&gt;八位同學，每位同學我都是當作自己的學弟妹在看待，有人甚至半夜一兩點私訊我，我也是盡量解答給他，我的想法是「不怕你問，只怕你不問」，問越多，或許就能挖走我沒講到的撇步，練越多、越廣，就越有可能剛好猜到比賽內容，那就是你賺到了，差了這題，你跟別人的名次就拉開了。在練習的時候，看到大家炯炯有神的目光，願意付出努力去練習、解決問題，其實我看了就很開心了，雖然不是我在比賽，但身為各位的老師（額外的），我仍然希望各位皆能獲得好成績，甚至來當我學弟 XD。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/JKMdFwM.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;最後的道別 等待你的好消息&lt;/h1&gt;
&lt;p&gt;最後一次模擬賽，也慎重的告訴大家，比賽該注意的地方、心態調整、搶分秘訣，只希望各位都能奪得好成績，也給了各位一句話：「上去就是想要電翻全場，你就是最強的。」說實在，這句話也是當年比賽，我走進會場內心所想的：自己是最頂的，我就是要電翻全場，或許很幼稚，但是是最有效的激勵方式。最後，也看著一位位同學離開了教室，準備衝刺，迎接上舞台的那天。&lt;/p&gt;
&lt;p&gt;從第一次培訓課程見面，到最後一次模擬賽結束離開，各位的轉變，付出的努力和那份不顧一切往前的心、氣場跟態度，我都看在眼裡，也很期待各位的好消息。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/mnw6BpK.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;結語&lt;/h1&gt;
&lt;p&gt;而 112 學年度全國高級中等學校工業類學生技藝競賽在今年 11 月 23 號正式落幕，培訓的八位電腦修護職種的學弟妹們，全體入圍優勝，本職種總共八座金手獎，新北市就搜刮走一半，甚至出了冠軍。當時我是坐在電腦前面看直播的，看到前十名有五位，還把冠軍給拿走，我也是跟著各位一起開心的，也很高興各位在選手室埋頭苦練多月，最終得來了回報，站在頒獎台上的那份榮譽感是最難能可貴的。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/eRS9tuH.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>2023年台日扶少團公益交流活動</title><link>https://vicwen.app/posts/interactclub-taiwan-japan-records/</link><guid isPermaLink="true">https://vicwen.app/posts/interactclub-taiwan-japan-records/</guid><pubDate>Thu, 03 Aug 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;前言&lt;/h1&gt;
&lt;p&gt;在 2023 年 6 月時，被 uncle 詢問是否願意參加基隆東區扶輪社主辦的台日扶少團交流活動，在 2023/7/24~2023/7/27 負責接待、陪伴日本遠道而來的八代南扶輪社的扶輪先進以及八代互動社的同學們，並且一起去進行友誼、體育、公益交流，去淨灘、幫忙清理獨居老人的家裡，當時思考了幾天，覺得這是個很不錯的經驗！便決定參加了，甚至在之後還推辭了瑞中 AID Summer Vocation 的邀請 QQ。&lt;/p&gt;
&lt;h1&gt;第一天&lt;/h1&gt;
&lt;p&gt;第一天的行程有：&lt;strong&gt;桃園機場接機、和平島、基隆廟口。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;第一天（2023/7/24）我和小創以及基隆東區、新北瑞芳扶輪社的 uncle、autie 們前往了桃園機場，準備接日本團員的機 XD，那天還剛好是萬安演習，只能提前一小時跑去，才不會卡在路上。&lt;/p&gt;
&lt;p&gt;在等待的過程中，autie 還請我們喝星巴克，我喝巧克力可可碎片（L 的$160...），我跟小創喝到快吐了 XD（他喝 XL...），同時也在翻閱著大家的照片及名稱，期待與大家的會合。我還問了小創一些日文，畢竟我只會幾句而已。&lt;/p&gt;
&lt;p&gt;接到機後，發現，怎麼來的都是女生 XD，而且都挺害羞的，讓我們的第一天充滿著尷尬，儘管我們有試圖要拉近和他們的距離，但我們兩個大男人的力量實在太微薄了...（其他人快來啊啊啊啊！）。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/T5HB9NG.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我們接機完後，便前往了和平島，走一走、聊個天，甚至還有下去泡個腳（腳濕濕的，好麻煩）。也有和日本的同學們拍個照，認識彼此，記住各自的名字（Minami、Nana、Miyu、Mao、Mei、Ai），大家都有各自的特色~會在後面提到。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/HbqcZLQ.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/fV1P0zR.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/zlkSkRr.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/PQj7mzT.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;晚上去了某間海鮮餐廳吃飯！透過翻譯大姊的幫忙，有詢問日本跟台灣的差異，像是臺灣為什麼那麼多機車、上下學時間（他們高中 8:40~4:00...超級幸福）、課後社團（有棒球隊、也有人是羽球隊）、興趣（有人喜歡看動漫、打棒球、聽音樂、看書），算是拉近了一些距離了！&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/KdsdV2d.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/qHo96Hc.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/CzRCAgk.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/7FU1J5Q.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/9q5TteZ.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;最後，餘興節目！我們一起去了基隆廟口，但是台灣的夏天真的很可怕==，超悶熱，廟口又超多人...其實滿對不起他們的哈哈哈，連我們都熱到快受不了了，他們每個都流超多汗 XD。&lt;/p&gt;
&lt;p&gt;有吃到了地瓜球、章魚燒、紅茶（珍珠沒了ＱＱ哭哇）。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/PYD9xg7.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;第一天的行程便在 10:00 結束了（他們有門禁，不能太晚回家），啊我 11:20 才到家...，超累，回家直接洗完澡躺平睡覺。&lt;/p&gt;
&lt;h1&gt;第二天&lt;/h1&gt;
&lt;p&gt;第二天我們的行程有：&lt;strong&gt;海大教博、科教館、羽毛球對決、台日扶輪例會、凱悅 KTV 唱歌。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;早上在海大教育博覽會碰面，並且來了其他的團員（終於...），我們團多了三位，恆毅也來了三位同學！並進行了自我介紹，認識彼此，隨後便是枯燥的大合照環節...，真的 Hen 愛拍照。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/AsvJvzs.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/z9FvbBy.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/tb9A3BJ.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;開始導覽！我們用我們的破日文英文加上比手畫腳，帶著他們逛博覽會，主題基本上都圍繞在海洋上，畢竟是海大，甚至有教育廣播電台來讓大家體驗在錄音室的感覺！非常有趣，但我沒體驗（以前就被抓過去採訪了 owo）。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/JST2dmC.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/5jsF33B.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/R5frylk.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/cM6bYMf.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/0AusXWC.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/buTsSif.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/QcuWt2h.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/yEVyfJw.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;在途中也慢慢跟大家拉近關係，但好像都是我們跟恆毅在聊天哈哈哈，他們也是滿有趣的幾位朋友。&lt;/p&gt;
&lt;p&gt;接下來我們前往了科教館。&lt;/p&gt;
&lt;p&gt;在裡面看到了許多平常看不到的海洋生物，魟魚、小鯊魚、竹筴魚、珊瑚、水母（還有倒立水母 XD），連我們自己都覺得很有趣。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/MP8Tr53.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/G4NEkva.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/LiZuL91.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;還有體驗畫魚，透過掃描機投影在大螢幕上，也在看大家畫畫的功力，很明顯的，小創畫畫功力一流，但他選擇畫暴走族魚...，Minami 畫了一隻熊本熊，滿傳神的，啊羽那組就...哀，畫得很好，下次別畫了，去讀法律吧?_?。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/8Dcijzi.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/OWX3FVl.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我們就繼續逛啊逛，千盼萬盼總算到了午餐環節！我們去吃築間，超讚，也和恆毅的還有八代的同學拉近關係，聊了一些彼此的事，各自的興趣之類的。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/QzfxqHd.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;他們對於吃到飽這件事感到有點陌生 XD，還有我們都吃很多，他們都吃一點點而已，很有日本人的 Fu...。&lt;/p&gt;
&lt;p&gt;吃完飯後，便去瑞工打羽毛球，這邊可說是彼此關係的轉捩點。&lt;/p&gt;
&lt;p&gt;分組、打球，非常普通，但，他們居然都全副武裝 XD，羽球鞋、運動服全部都穿上，超級敬業的！看得出他們很重視每個行程，相較於臺灣人的隨便、懶惰，他們真的超認真哈哈哈。&lt;/p&gt;
&lt;p&gt;其中，Mao、Nana 感覺就是羽球校隊的！動作很標準，也很厲害，給他們一個讚。&lt;/p&gt;
&lt;p&gt;Miyu 可能是動漫看太多，打球都會啊、呀的叫，非常經典，也謝謝他的叫聲，大家的關係拉近了許多 XD。&lt;/p&gt;
&lt;p&gt;大家整體都很認真！讓人印象很好，不會讓人覺得很難相處、隨便，反而我們還比較隨便一點，很怕被他們討厭哈哈哈。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/5zBtJna.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/CGGRhIj.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;打完球後，便前往了基隆東區扶輪社社館，開例會、吃晚餐。恆毅的同學一直說我很有領導的氣息？＿？這我是真不懂，我只是小菜雞 QQ。晚餐時還被日本同學抓過去吃飯聊天！也在這時候問了他們很多問題，整體氣氛融洽，總算不是尷尬的了...。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/iWmdewf.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/fauuVfJ.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;吃完飯後，其他人還在玩喝青蛙湯遊戲，超級瘋狂，我是不敢吃青蛙啦...，各位慢慢玩~&lt;/p&gt;
&lt;p&gt;此時，又有插播節目了！我們去凱悅唱歌～（我也是第一次去），但恆毅的同學不能太晚回家，就沒參與到唱歌的行程了，頗可惜 QQ。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/geeV4GC.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/FHf8BOO.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;說到唱歌，就要說到小創了，他真的超瘋哈哈哈，又嗨又會唱日文歌，當日 MVP 非他莫屬，至於日本的同學，他們唱歌很好聽！我還問另個同學，應該不是我錯覺吧，他們唱歌很好聽對吧...。聽的歌也很符合 15、16 歲女高中生會聽的歌 XD。小創在那天就是非常瘋狂，超級嗨，日文歌也能跟著唱，超級佩服ㄉ。&lt;/p&gt;
&lt;p&gt;礙於時間，他們有門禁 10:00，因此就先行告退了，但我們剩下幾個待到了 10:30 才離開，真的是唱不夠，也很難得有這種機會跟大家聚在一起，唱歌唱到那麼晚哈哈哈。&lt;/p&gt;
&lt;p&gt;最後，我載小創回家，但颱風剛好這幾天來，風超大、雨也是 G8 大，我騎著小摩托車，就像大浪前的一葉扁舟，隨時都有傾覆的可能 XD，只好在雨中作樂了，至於到家時間...又是 11 點多了...，不說了，洗澡睡覺，明天最後一天ㄌ。&lt;/p&gt;
&lt;h1&gt;第三天&lt;/h1&gt;
&lt;p&gt;第三天行程有：&lt;strong&gt;社區服務活動（獨居老人居家清潔）、淨灘（改成黃金博物館）、台日扶少團活動心得分享會暨歡送會。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;今天是颱風影響北部最嚴重的一天，從早上就開始下間歇性陣雨，加上風很大，因此原本的淨灘活動改成了去黃金博物館的行程，挺可惜的。&lt;/p&gt;
&lt;p&gt;早上我們跟某個里的里長達成共識，去了獨居老人的家裡幫忙她打掃她家，第一個阿嬤已經高齡 92 歲了，阿嬤的家就是很有阿嬤的味道，很多東西看起來好幾年沒碰了，他卻說他還要用...，裡面有個瓜已經被螞蟻啃到爛了，還不丟...，我們就幫他丟掉囉；還有他丟個可樂瓶在地板上，不知道放多久了，他還說他女兒要用...，認真的？＿？，總之就是掃掃掃，跟旁邊的邊聊邊清，滿快樂的！阿嬤還跟我們聊天，看起來還很健康！&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/yMEzNnr.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/XmnFttu.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/AqJbMsq.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/2NV9JjQ.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/u2VNH3R.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/S0q06Ib.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;第二個阿嬤已經 102 歲了...，過去的時候他在抽菸？＿？超級問號，原來這就是長壽的秘訣嗎）？我分配到的是擦廚房玻璃的部分，我幫她把廚房擦的亮晶晶的，原本都被油煙染成黃色的。擦的時候都在跟小創還有恆毅的同學聊天，度過開心的早晨～&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/zxWIyBK.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/U9CXZ13.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/jc6JGwV.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/PWjhkDb.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/NnQ0lEq.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/R2IYFEf.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/Czu8L0N.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;結束後，我們到他們里去吃飯。幫忙端菜，夾菜給長輩吃，其他桌的大人比我們嗨多了...。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/K7NrAr9.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/DXFTr1t.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;下午我們跑到了黃金博物館，因為我沒去過，但據小創說的，他說那裡變很多，原本只有一塊黃金而已，現在多了很多礦物和水晶，滿有趣的，但我看到那塊大黃金的時候，一直在想既然那塊黃金那麼值錢，都不會被偷嗎？？？？目前仍持懷疑態度。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/5MeZrl7.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/LqV3HVW.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/OJeGgNs.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/AoHUePa.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我們還跑到礦洞裡，看看以前的礦工在何種環境工作的，裡面溼滑又黑暗，我待在裡面滿不舒服的...很佩服他們，為了工作、為了家庭，能夠做出如此偉大的犧牲。&lt;/p&gt;
&lt;p&gt;結束後，我們被送去了某間旅館，租了一小時，說是要讓我們換衣服、休息一下，我心想：太扯了吧...，真的很在乎我們的感受 QQ 快哭了。&lt;/p&gt;
&lt;p&gt;所以我很捧場，我直接洗澡，都住了，該洗一下吧...。&lt;/p&gt;
&lt;p&gt;時間到了，我們便前往長榮桂冠內準備參加活動心得分享會暨歡送會，日本的朋友明天就要離開了嗚嗚嗚 QQ。&lt;/p&gt;
&lt;p&gt;接下來就是吃飯、聊天，小創有發表他的心得，我覺得他有段話說得很好：『這三天我們去了好多地方，這些回憶我都不會忘記，但我最忘不了的是你們日本的朋友。』（類似這樣的話），聽到真的，雞皮疙瘩都起來了，太秀了老哥。&lt;/p&gt;
&lt;p&gt;後面我還被拉上去唱歌，大家都好嗨、好開心，不論是大人還是扶少團的同學，不論是臺灣人抑或是日本過來的朋友，此時此刻大家都笑得很開心，這種場面真的永生難忘 QQ 很高興能夠參加這次的活動。&lt;/p&gt;
&lt;p&gt;最後要離去的時候，大家都依依不捨，不斷地拍照、講話，甚至遊覽車都到了，大家還不願離去，最後甚至我們直接跟他們上車，送他們到飯店，在外面還聊了很久。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/s3E4WR5.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/DB2cQ9i.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/gFa0LTw.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/ZRaSoNF.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/EeRMSvd.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://imgur.com/C46qOyU.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;雖然外面狂風暴雨、冷颼颼的，但我們的心卻是炙熱無比的。&lt;/p&gt;
&lt;p&gt;我們甚至約定了，明年三月，換我們過去做他們的客人！&lt;/p&gt;
&lt;p&gt;謝謝你們的到訪，這三天雖然很累、行程很滿，但大家都過得很充實，也期待著下次的見面。&lt;/p&gt;
&lt;p&gt;後會有期！&lt;/p&gt;
&lt;h1&gt;結語&lt;/h1&gt;
&lt;p&gt;在這段時間內看到了許多台日差異，他們是非常不願意麻煩別人的，即使飯店冷氣沒辦法調節、太熱，都不會向我們反應，我問了他們才會簡單說明，如果是台灣人早就瘋狂抱怨了哈哈哈。&lt;/p&gt;
&lt;p&gt;還有他們對做一件事會做到非常完美，即使是一場簡單的羽毛球對決，他們也會全力以赴、全副武裝，讓我們都很驚訝。&lt;/p&gt;
&lt;p&gt;也在這次的活動中認識了許多人，不管是恆毅高中扶少團，還是日本八代南互動社，都是一次美好的回憶！雖然剛開始有點害羞、羞澀，不知從何認識起，也因為語言的隔閡，無法正常交流，但人總會找到出路的，透過英文加上手語，成功地進行交流 XD，讓人非常感動，我們根本外交大使）x。&lt;/p&gt;
&lt;p&gt;雖然這幾天超累、颱風侵襲，仍擋不住我們的熱情！&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/rd84EZk.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;願台日友誼長存，以此文紀錄為禮物，獻給所有人。&lt;/p&gt;
</content:encoded></item><item><title>新北市薦送海外技職研習：澳大利亞之旅</title><link>https://vicwen.app/posts/newtaipeicity-australia-records/</link><guid isPermaLink="true">https://vicwen.app/posts/newtaipeicity-australia-records/</guid><pubDate>Wed, 03 May 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;前言&lt;/h1&gt;
&lt;p&gt;在 2022/10/24 時，取得了工科技藝競賽-電腦修護職種之冠軍，因此受到新北市的贊助，邀請全新北市在技藝領域表現優異的學生（技藝競賽金手獎），共同前往&lt;strong&gt;美國、澳洲、日本三國進行為期兩周的海外技職研習，&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;海外研習之目的在於：&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;結識身處不同文化的人 - 交換彼此文化、學習之歷程。&amp;lt;br&amp;gt;&lt;/li&gt;
&lt;li&gt;學習他國知識與技能 - 轉化為自身能力、帶回臺灣分享。&amp;lt;br&amp;gt;&lt;/li&gt;
&lt;li&gt;開拓視野 - 拓展對世界的認知，不管是技藝、文化、景觀皆是。&amp;lt;br&amp;gt;&lt;/li&gt;
&lt;li&gt;培養溝通技巧、英語對話能力（以美澳團的角度）。&amp;lt;br&amp;gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在此要感謝用力支持本項計畫的新北市市長：&lt;strong&gt;侯友宜市長&lt;/strong&gt;，及幫助推動的局長、處長等，還有陪同我們出去、在準備比賽期間盡心盡力教導我們的&lt;strong&gt;選手老師&lt;/strong&gt;，若當中缺了一環，我們便無法獲得此項殊榮，更無法得到出國開眼界的機會，新北市更是&lt;strong&gt;全台唯一一個縣市有出國研習的機會&lt;/strong&gt;，此乃德政啊，在此向新北市深深一鞠躬。&lt;/p&gt;
&lt;h1&gt;行前培訓&lt;/h1&gt;
&lt;p&gt;當得知能夠出國後，當然要進行行前培訓啦！畢竟我們是學校、縣市、乃至台灣頂尖技藝的代表，肯定不能出去丟臉，於是在 2023/04/15-16 兩日，進行了行前培訓，以我處在的澳洲團，我們進行了全英文培訓，包含：社交情境、買賣文化（如小費文化）、企業參訪、學習成果簡報，並安排了曾帶過赴美團的一位鶯歌高工老師的經驗分享，從兩天的課程中了解了許多，也對兩個禮拜後的研習之旅有更加清楚的認識，並加強了自己的英文實力，以應付不同的場合，如詢問問題的語言美學、溝通技巧、買賣文化。&lt;/p&gt;
&lt;p&gt;也因為在場的皆是當屆全國技藝頂尖的同學們，也是大開眼界了，當時培訓時坐我旁邊的是全國冠軍（金手一），我前面也是冠軍，後來前面坐一個台大的，甚至有世界拿牌的選手就在跟我聊天，讓我覺得好不真實，大家也都散發著超強氣場，全場就我最弱，在角落瑟瑟發抖......。&lt;/p&gt;
&lt;p&gt;聊天時大家也是很熱情，有說有笑的，由於在場皆是不同職種，從資電類的電腦修護到園藝造景的選手都有，因此大家能聊的真的很多，完全沒有選手的那種肅殺，也看不出來在與你聊天的那人其實是打敗全國 40、50 位的頂尖好手。但，當聊到專業時，大家都彷彿變了一個人，變得認真、有自信了，或許這就是身為選手的魅力吧，那自信且侃侃而談的神情，背後付出了多少汗水及淚水。&lt;/p&gt;
&lt;p&gt;實在是高手如雲，讓我感受到自身的渺小，激起了想持續進步的慾望，因此也讓我對此趟旅程懷抱著無限期待， 2023/05/01 期待與各位展開一場澳洲之旅。&lt;/p&gt;
&lt;h1&gt;澳洲之旅&lt;/h1&gt;
&lt;h2&gt;第一天&lt;/h2&gt;
&lt;p&gt;今天是赴澳洲研習的第一天，在 2023/5/1 凌晨三點半攜帶著行李和期待出發前往學校，經過幾小時的搭車，抵達了桃園機場！&lt;/p&gt;
&lt;p&gt;隨後分發護照、交代注意事項，便將行李托運、過海關以及出境，最終搭上了前往 Brisbane 的班機！&lt;/p&gt;
&lt;p&gt;由於這是我的第一次出國，一切都新奇且陌生，抬頭看向窗外的風景，底下的建築物隨著飛機的升高逐漸縮小，曾經如此高大的建築在此時皆如小小星點般，是那麼陌生且渺小，也意味著我們正式踏上了澳洲參訪研習的旅行。&lt;/p&gt;
&lt;p&gt;但是坐飛機真的是件非常折磨人的事...整趟旅程大概最枯燥的便是坐飛機了，儘管前面有電視給你看、玩，但那狹小的位置，加上漫長的座機時間，對於現代人實在是種酷刑。&lt;/p&gt;
&lt;p&gt;這也是我第一次吃到傳說中的飛機餐，雖然飛機餐吃起來有點冷凍食品的感覺，但有吃到餐包配紐西蘭的奶油，還滿新奇的。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/qv3rBE5.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;而我在這八小時的過程中做了：睡覺、起床吃飯、看風景、看微積分、睡覺、起床吃飯、看微積分（沒風景看，都是雲）。&lt;/p&gt;
&lt;p&gt;最終抵達了 Brisbane。在這邊科普一下，布里斯本（Brisbane）位於澳大利亞（Australia）的昆士蘭州（Queensland），為昆士蘭州人口最多之城市。位於澳洲本土的東部，北緣陽光海岸，南鄰國際觀光勝地黃金海岸市。大都會區人口 230 萬，是澳洲人口第三大的都會，僅次於雪梨與墨爾本。以布里斯本為中心的昆士蘭州東南部城市群人口逾 327 萬。本次旅途中所會停留的地方便是這裡，包含 Brisbane 和黃金海岸（gold coastline）。&lt;/p&gt;
&lt;p&gt;前往飯店的路途中，我可是開了大大的眼界，高大又莊嚴且富含藝術氣息的建築，我只在網路上看過，看到也都是說句好看便不以為意地滑過去了。沒想到親臨現場後，那種震撼感是無以復加的，幾乎整車人都在低聲誇讚這裡的建築物，與臺灣相差甚遠，臺灣的...算了，完全輾壓，但也有可能是因為這裡是澳洲數一數二的大都市，都市和鄉下是有非常巨大的區別的。&lt;/p&gt;
&lt;p&gt;順帶一提，當時為澳洲時間晚上八點（為臺灣時間+2hr）路上、建築中卻已沒有了半個人，但街上仍然燈火通明，貌似是政府要求不能熄火。也從側面驗證了澳洲連大都市也是下午四點便陸陸續續的關門收工了。&lt;/p&gt;
&lt;p&gt;光是第一天就感受到與台灣許多不一樣的地方，也不斷刺激著我們的大腦，引誘著我們繼續探索下去，也更加期待未來的 11 天，會遇到什麼樣的插曲及挑戰。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/3U8torp.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;這是澳洲的一家賭場，我一開始還以為是一家博物館...查了一下，好像是英式建築的風格。未滿二十不能進入，門口站著兩位保全，若是國際觀光客會看護照。&lt;/p&gt;
&lt;p&gt;由於時間也晚了，我們入住的飯店是 hotel indigo，在市區這個寸土寸金的地方，僅僅占有一小塊地方，但價格仍然是十分昂貴的...查到的時候也是嚇到...只能說非常感謝教育局金主爸爸的資助，我一定會在這裡好好的學習的。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/5BF3Rja.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;這是間非常具藝術氣息的飯店，一入口就看到幾件藝術品，讓人忍不住停下多看幾眼。而這裡的入口會叫 G 樓層，意思為 Ground，若要找服務人員做 check in/out，需要搭乘電梯至 1F。&lt;/p&gt;
&lt;p&gt;領完房卡，堅堅主任交代完事情後，便回房間了。打開房間後，只能說是我膚淺了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/iQrhDmp.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/Yxe1KFC.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;不能說喜歡這間房間，只能說是非常喜歡，果然是金錢的力量...。但它的燈很多都是連通的，真的很難直觀的開關燈，還有他的浴室不能鎖門啊啊啊，幸好我是跟指導老師住，否則跟男生住的話，那個情況想想就很慘。&lt;/p&gt;
&lt;p&gt;整理好行李、隨行包包後，坐在床上，還是覺得這一切都好像一場夢，沒想到我就這樣到澳洲了，踏上一片未知的土地，一切都是那麼的陌生，跟指導老師說了幾句，才覺得我是在現實世界中。&lt;/p&gt;
&lt;p&gt;哀，太累了，先睡覺...。&lt;/p&gt;
&lt;h2&gt;第二天&lt;/h2&gt;
&lt;p&gt;今天是澳洲研習的第二天，在 2023/5/2 早上 7:00 吃完早餐後出發前往 TAFE Queensland South Bank Campus。&lt;/p&gt;
&lt;p&gt;坐在車上就覺得車外的風景很優美，沒有令人窒息的鐵皮屋、滿地的垃圾、小到哭的人行道，取而代之的是河、充滿藝術氣息的建築、樹木和在路上自由翱翔的烏鴉、鳥（在這裡烏鴉是吉祥的意思），也有將部分片段錄下來，但畫質有點差囧。&lt;/p&gt;
&lt;p&gt;&amp;lt;video src=&quot;https://imgur.com/5R1D3xV.mp4&quot; controls width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&amp;lt;/video&amp;gt;&lt;/p&gt;
&lt;p&gt;抵達澳洲昆士蘭 TAFE 後，首先進行課程開幕儀式，講解了短期課程所獲得的結業證書和畢業證書之間的區別，以及介紹了本次推動計畫的人員、老師，交代些許事項，順利圓滿的進行開幕儀式，同時也象徵著本次課程有個好的開始，也期望能夠圓滿、順利地結業，並從中學習到許多新知識。&lt;/p&gt;
&lt;p&gt;再來是參觀校園環節！校園是非常開放的，沒有圍牆，只有藝術氣息的建築，和綠意盎然的花草樹木，讓人看了心情都好起來了。我們有參觀了衛護大樓，看到了牙醫專用儀器、處理流程、工具等等，學生們有年輕人，也有老年人，只要是想學習的人，TAFE 學院便不吝嗇的教導他們，這種精神也是我們需要學習的！&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/RZqYOYW.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;還有參觀了牙磨大樓，許多學生在練習磨牙、洗牙，桌上擺放著各式儀器，老師在教導著學生，真材實料的透過手作學習知識、加強能力，讓我也回想起了指導老師當初不辭辛苦，在旁用心地教導我，指導老師是我們人生中很重要的一位貴人。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/MY6kguM.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;以及圖書館！&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/vFU6syz.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;下午則前往了昆士蘭現代藝術館和博物科學中心，了解當地的文藝活動，民眾的參與程度也是十分高的，大家都很 chill、自由自在的感覺，空氣中都瀰漫著藝術的味道。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/lGRIFe6.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/jasSi0d.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/lDvzwe2.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然後路上超多上圖這種鳥的，他叫「垃圾鳥」，他們喜歡在垃圾桶中翻找食物的澳洲白䴉（Australian white ibis），且被提名，有望成為 2032 布里斯本奧運會吉祥物。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/5tNsL0b.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我只能說，看不懂的藝術就叫藝術...。&lt;/p&gt;
&lt;p&gt;最後前往了兩岸公園，看看昆士蘭的建築風格，了解民眾們的生活步調，當地人生活是十分愜意的，平日僅僅工作到下午四點，放假了在沙灘曬日光浴、在草地野餐、在湖泊旁邊烤肉，就是完完全全的「Play life」，令人十分嚮往、羨慕，難怪大家喜歡到澳洲度假~&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/Z1DKvxN.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/HTdevlz.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/IhOca9H.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;第三天&lt;/h2&gt;
&lt;p&gt;今天是澳洲研習的第三天，我們前往了 TAFE Queensland Acacia Ridge Campus。&lt;/p&gt;
&lt;p&gt;到了現場後，負責教導的老師介紹了油電車主要組件，包含了：電池、Inverter（逆變器）、motor generator（電動發電器）……等等，雖然這並不是我的專業，但是我還是很認真的聽老師講解構造（雖然有時候會小小的 log out...）。介紹完後，便讓我們看了車子的車底，利用實際的例子來加深我們的印象。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/VU8Jqm3.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/SUfmA8X.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;隨後便是操作環節，首先被告知電壓的危險，高電壓這種極度危險的工作環境，是不能開玩笑的，必須要配戴安全裝備（套超多層手套...還有絕緣墊）。之後便讓我們實際嘗試測量電壓，實作環節。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/nNVLmid.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;同時也發現了，與臺灣那得過且過的公安意識，澳洲的安全措施簡直是做的滴水不漏，在場每一個在練習的人都是全副武裝，安全措施做到滿，即使那個手套又悶又熱，他們仍然會套上好幾層，非常佩服他們。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/lyPPYWD.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/M9Cty7B.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/H8jZYrc.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然後他們的工廠外有一台車超帥的...可惜我不懂汽車 QQ。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/YmuIz6v.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;隨後帶我們到他們測試汽車各項素質的地方，他們會利用履帶搭配科技，測出汽車的馬力、速度等等，&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/x73gfwJ.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/GJJRybD.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然後我那時候超累...&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/tluxxQ0.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/7fFBTUT.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;回家的路上也拍了一些照片，風景真的很頂...。&lt;/p&gt;
&lt;p&gt;不說了，回飯店寫作業了 QQ，期待明天的行程。&lt;/p&gt;
&lt;h2&gt;第四天&lt;/h2&gt;
&lt;p&gt;今天是澳洲研習的第四天，前往的地點是 TAFE Queensland South Bank Campus。今天用走的去學校...但風景很棒，要我走在這些風景旁邊，我是心甘情願的。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/2QQ35Xr.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/zPk4lv2.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/WZQOyY8.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;準備上 Peter 老師的創業課！&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/PLPsX7N.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/ZQMHHwJ.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/J5SxE4i.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;跟我們講解了創業（entrepreneurship）和商業（business）的不同，及須具備的心態、特質等等。&lt;/p&gt;
&lt;p&gt;Peter 老師本人很 nice，很幽默也很照顧我們，有不懂的他都會耐心解釋，也會換句話說，讓我們比較好吸收內容。&lt;/p&gt;
&lt;p&gt;我還留了一張照片！&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/FFgA6xG.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;晚上我們還去超商採購一些物資，但只買的起餅乾、軟糖，其他是真買不起...。&lt;/p&gt;
&lt;p&gt;然後牛奶比可樂還便宜，牛奶萬歲！&lt;/p&gt;
&lt;h2&gt;第五天&lt;/h2&gt;
&lt;p&gt;今天是澳洲研習的第五天，今日仍然是 Peter 老師教學，教學內容為；商業計畫選項、新商業計畫模板、商業模式介紹、價值主張介紹。&lt;/p&gt;
&lt;p&gt;並且在最後給了我們一個大任務，那就是在下個禮拜二各組需上台 presentation，要上台展示自己的學習成果，述說自己開創的汽車維修廠所需具備的商業計畫。&lt;/p&gt;
&lt;p&gt;我們組分到的是：Key Activities、Key Partners、Key Resources，需要我們發揮團隊精神，規劃屬於自己的 Business，但很明顯的，我們組的學術部分太爛了...，懂得都懂。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/OdVWZEX.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/c9Yx23W.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;同時也在這天總結了一下這幾天的心情，今天是星期五，這禮拜最後一天上課了，也是最後一天住在這間飯店了（下禮拜一才會回來），因此為了感謝路途中幫助我們的老師、接待以及辛苦的飯店人員，因此準備了一些薄禮，鳳梨酥、科學麵、台灣特色磁鐵、鑰匙圈給了一路上認識的跨國朋友，感謝他們這幾天的幫忙，大家收到禮物也都露出開心的笑容，一直說著謝謝，讓我們也感到很開心。&lt;/p&gt;
&lt;p&gt;我跟台大姊婕紜在出發前一起買了一些伴手禮，想說會有學伴，可以分享。但很顯然的，完全沒半個，只好拿來都送給其他人囉。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/IT4mJwx.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;這是飯店的服務人員！中間那位是臺灣人！&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/Uzfxm3Z.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;這位就是我們的 Peter 老師啦！&lt;/p&gt;
&lt;p&gt;能夠參加此次澳洲研習，不只學到了異國文化、看到了異國風景，還體會了人們的熱情，是趟非常充實的旅程！&lt;/p&gt;
&lt;p&gt;由於假日沒人在上課的（還要上課就太殘忍了），因今天早上收拾收拾行李，晚上下課後就出發去黃金海岸（Gold coastline），準備好好的放鬆放鬆～&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/tfR2vwx.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;夜晚的餘暉～&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/9bwivuW.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;抵達黃金海岸後，果然是大家的狂歡聖地，晚上還有許多人在逛街，街頭藝人在街上唱歌跳舞，還有一個籃球黑人把我們攔住，看他表演，最後還跟他拍了張照。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/9IlECTZ.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;還有看到有人在打沙灘排球！&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/eYH2rUP.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;上了一個禮拜的課，終於可以好好放鬆了～真是可喜可賀。&lt;/p&gt;
&lt;h2&gt;第六天&lt;/h2&gt;
&lt;p&gt;由於第六七天是偏觀賞、參觀黃金海岸的景色，因此就不太多做紀錄，僅將部分令人印象深刻地記錄下來。&lt;/p&gt;
&lt;p&gt;早上我們一大早 5:40 就起床，準備前往沙灘看 6:15 的日出！沒想到第一次看日出是跟許多技職好手一起看。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/Hb70j2Y.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/oOzI2j9.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/PvSZijZ.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我真的覺得我拍得很好，他們要感謝我。&lt;/p&gt;
&lt;p&gt;早上吹著冷冽的風，等待著日出的到來，即使大家前天嘴上說著好累、不想來，但隨著時間推移，每個人都出現在沙灘旁，站著、坐著、奔跑著，靜靜地等待著一天的升起，成為了一次刻骨銘心的回憶。&lt;/p&gt;
&lt;p&gt;然後我們又去了博物館看展覽。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/nzF6TSx.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;恩，我是真沒看懂。&lt;/p&gt;
&lt;p&gt;還有去坐船，吃炸魚薯條！&lt;/p&gt;
&lt;p&gt;&amp;lt;video src=&quot;https://imgur.com/1lHwPjl.mp4&quot; controls width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&amp;lt;/video&amp;gt;&lt;/p&gt;
&lt;p&gt;摸摸海水、看看風景、跟船長聊天，超 Chill...。&lt;/p&gt;
&lt;p&gt;&amp;lt;video src=&quot;https://imgur.com/imQXgBD.mp4&quot; controls width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&amp;lt;/video&amp;gt;&lt;/p&gt;
&lt;p&gt;坐在路邊吃薯條看鵜鶘（小時候很可愛，長大超醜...），超大隻，有點噁心。&lt;/p&gt;
&lt;p&gt;&amp;lt;video src=&quot;https://imgur.com/cfhWTij.mp4&quot; controls width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&amp;lt;/video&amp;gt;&lt;/p&gt;
&lt;p&gt;還有餵食秀。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/dHF6D01.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;晚上，我們在飯店泳池游泳。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/adMOXQA.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;還有跟來自布里斯本的兩個小孩打籃球！算是一次文化交流吧）？&lt;/p&gt;
&lt;h2&gt;第七天&lt;/h2&gt;
&lt;p&gt;今天到澳洲的某個市集！有賣許多異國美食，我們買了超鮮的鮮蚵，三四個一起吃，坐在草地上，看風景，聽著街頭藝人彈樂唱歌，好不快活！&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/6H2Mugt.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;真的超 chill。&lt;/p&gt;
&lt;p&gt;&amp;lt;video src=&quot;https://imgur.com/oBCsPdl.mp4&quot; controls width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&amp;lt;/video&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/ZV5tsNz.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;就不多說甚麼了，身在那個環境才能切切實實的體會到。&lt;/p&gt;
&lt;p&gt;後來我們到了 sky point，類似於台北 101 的方式，需要花台幣 2000 多塊上去...，實在是有點貴（感謝新北爸爸）。&lt;/p&gt;
&lt;p&gt;上去需要坐一台超快的電梯...，有人直接耳鳴、不舒服。&lt;/p&gt;
&lt;p&gt;&amp;lt;video src=&quot;https://imgur.com/6nkRCai.mp4&quot; controls width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&amp;lt;/video&amp;gt;&lt;/p&gt;
&lt;p&gt;上面的風景很震撼，一望無際的大海，盡收澳洲的城市風景，有河有海有自然，真的是什麼都不缺了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/S7OwdOM.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;啊，缺美食，來人，上美食！&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgur.com/CjlzCUH.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我們一大波人吃了四個 pizza，一人一杯奶昔，桌子都放不下了...，還放到角落邊。&lt;/p&gt;
&lt;p&gt;又觀賞了一下風景，我們便動身前往動物園！&lt;/p&gt;
&lt;p&gt;有鴨子、袋鼠、無尾熊、袋熊、色彩繽紛的鳥...等等。然後可以付費跟無尾熊拍照，但要澳幣 29 塊...(台幣六百多元)，我是沒那麼盤啦...所以我跟另個同學跑去坐免費小火車了。&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/OJqjVM5.jpeg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;我還跟袋鼠達成統一澳洲的協議。&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/BCCBfZr.jpeg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/dRMu3DX.jpeg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;video src=&quot;https://i.imgur.com/HIOJvVk.mp4&quot; controls width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&amp;lt;/video&amp;gt;&lt;/p&gt;
&lt;p&gt;還看到了超可愛的無尾熊！&lt;/p&gt;
&lt;p&gt;&amp;lt;video src=&quot;https://i.imgur.com/88jZTI2.mp4&quot; controls width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&amp;lt;/video&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/0Wm13dY.jpeg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;後面又跑回 sky point 看了一下夜景，還有同學跑去坐輕軌，但他們是冤大頭。人家澳洲價格是算 zone 的，不是算站的...只坐一站，花了幾百塊哈哈哈。&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/wGjVjQU.jpeg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;h2&gt;第八天&lt;/h2&gt;
&lt;p&gt;白天的行程有點不記得了 QQ，想到再補上來。&lt;/p&gt;
&lt;p&gt;晚上我們使用飯店的設施：游泳池、籃球場。在籃球場遇到兩位來自布里斯本的「球友」（我們當時在黃金海岸，非布里斯本），一大群男生下去跟他們一起打籃球，即使英文不怎麼好，但還是能正常交流、打球，算是一次特別的經驗）？&lt;/p&gt;
&lt;p&gt;最後也合照一張，聊了一下，成為了此趟研習中的一個小插曲。&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/7ry8alt.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;h2&gt;第九天&lt;/h2&gt;
&lt;p&gt;今天是我們在澳洲研習的第九天，最終之戰終於還是到來了，需要上台發表展示我們的學習成果。以全英文講解簡報內容。&lt;/p&gt;
&lt;p&gt;早上到達 TAFE 校園後，大家都充滿了熱情，不斷練習、修稿，期望表現最好的一面出來，即使大部分人的英文程度不太好，但所有人都是全力以赴，以「Do our best」的精神再準備簡報，讓 Peter 老師非常驚喜、開心，畢竟大家遠赴海外，在此認識了許多人、學習到不同面向的知識，都不想抱著遺憾回家，希望帶著自己最好的一面給大家看，讓 Peter 老師不會感到失望。&lt;/p&gt;
&lt;p&gt;各組發表完畢後，Peter 老師和負責海外課程的另一位女老師立即拍手稱讚，他們認為我們的表現超出了預期，做得非常出色。每個人都盡力展現自己最好的一面，這讓他們感到欣慰。&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/hybGxt7.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/OzXcopV.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/jRaPBhi.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/IMFjtHG.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/w61kjeD.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/IjNM78W.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;結束簡報後，就是感傷的時候了。研習即將進入尾聲，再過兩天我們就要啟程回家，要與 TAFE 的老師們告別了。到了這時，氣氛有些感傷。大家紛紛表達自己的不捨和熱情，不斷地送禮物給兩位老師，禮物都多到沒手拿了。這也展現了臺灣人最自豪的熱情！我們擁抱、拍照、聊天，向他們表達我們的不捨之情。他們也表示會永遠記得這段回憶，記得我們這些人。&lt;/p&gt;
&lt;p&gt;還有兩天就要回家了，我們帶著豐富的知識、人文和回憶回到台灣，與大家分享。讓大家知道我們在這裡過得非常辛苦！每天都要上課、做報告，比在台灣還要認真。&lt;/p&gt;
&lt;p&gt;畢竟，這是一個特別的經驗。&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/JpYsM3y.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/vBhpE20.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/vMTv3Iu.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/7DIo36x.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;告別後，我們便坐船去 UQ（昆士蘭大學），途中還遇到一位臺灣人，他說他國中畢業後就在 QUT（昆士蘭科技大學）讀書了，很久沒遇到臺灣人了！他說他很想念家鄉的感覺，也好久沒有說中文了，都有點生疏了，跟我們聊天很開心！&lt;/p&gt;
&lt;p&gt;緣分就是這樣，在茫茫人海中相遇，僅算是萍水相逢，卻能讓你印象深刻。&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/5LPRjnr.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/XSuXR7j.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/HkUJkSn.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;隨後我們到了昆士蘭大學，是澳大利亞昆士蘭州最古老、最大的綜合性研究型大學之一，成立於 1909 年。被譽為澳大利亞最美麗的校園之一。&lt;/p&gt;
&lt;p&gt;UQ 有幾大特點：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;學術聲譽：昆士蘭大學在多個領域享有國際聲譽，特別是在生命科學、工程、科技和健康科學等領域。該大學「目前」在全球大學排名中位於 50 名的位置。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;研究與創新：昆士蘭大學是一所偏向研究導向的大學，致力於推動科學研究和創新。該大學在各種研究領域開展了重要的科學項目，與政府、產業和社區建立了廣泛的合作關係。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;國際化：昆士蘭大學吸引了來自世界各地的學生和教職員，並提供了豐富的國際交流和合作機會。該大學與全球多個頂尖大學建立了學術合作關係，並積極推動國際學生交換計劃。很多學生都是華裔的！大家都是世界各地的菁英。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;校園環境：昆士蘭大學的校園環境優美，擁有寬敞的草坪、湖泊和植物園。甚至還有 Club...雖然領隊說很少學生會去，好像很 low 哈哈哈。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;感觸最深的真的就是環境了，圖書館、教學樓每棟都超級漂亮...，好像來到巨大博物館一樣。&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/9VC9gza.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/SdTUqQM.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/ohNj6ts.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/x6tyUXR.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/ulSRreZ.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;並且我們有訪問當地學生，他們說學校有種樹，花是紫色的，到了花季的時候校園會很壯觀、漂亮，但領隊說被掉下來的花打到會被當 XD。&lt;/p&gt;
&lt;p&gt;隨後我們便去吃晚餐了～&lt;/p&gt;
&lt;p&gt;一樣是坐船去！但大家太累了，在船上都睡著了...。&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/WvCa3gD.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/ZKhgW49.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/ABTkpF9.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/oGivwjp.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/gM44Gk8.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;h2&gt;第十天&lt;/h2&gt;
&lt;p&gt;今天是澳洲參訪的第十天，今天的主題是：企業參訪。&lt;/p&gt;
&lt;p&gt;我們早上前往了兩家澳洲 Toyota 的分店，參觀了分店的車輛、了解澳臺兩地銷售車輛的差異。例如，在澳洲，車輛保固期可以長達七年，而任何汽車配件都需要另外購買。所有的安裝工作都由車廠負責，一旦交車到銷售分店，就不再進行任何調整或服務，最多只會提供簡單的清潔服務，以確保保固的完整性。此外，當澳洲本地人的車輛保固期結束後，他們會選擇將車子出售並換購一輛新車。&lt;/p&gt;
&lt;p&gt;分店的工作人員非常熱情，不僅耐心地詳細介紹店內環境和情況，對於我們提出的問題也能即時回答。他們甚至給了我們每個人一頂 Toyota 帽子作為禮物！這讓我們每個人都帶著豐富的知識和禮物回國。&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/iS2ZkT7.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/67BilCA.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/FKz5oxB.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/C0fxpFg.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/zKO1WqH.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/ps1bfOY.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;隨後，我們前往了 TAFE Queensland Acacia Ridge Campus，參加了一堂關於一種新型能源車輛——Toyota Mirai 的課程。&lt;/p&gt;
&lt;p&gt;科普一下，Toyota Mirai 是一款全電動車，利用燃料電池技術將氫氣轉化為電能，並以電力驅動車輛。這項技術能夠在行駛過程中持續提供電能，無需充電，同時也不會產生有害的尾氣排放物。這種新型能源車輛有助於減少空氣污染和溫室氣體排放，同時減少對有限石油資源的依賴。&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/WZLWJvf.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;同時，我們還了解到成為 Toyota 技術員的要求。首先，需要參加由 Toyota 提供的培訓，培訓期為 3 至 4 年，內容相當豐富。若想成為一位優秀的技術員，則需要取得結業證書並進行約 50 天的實習，由經驗豐富的技師指導，培養出優秀的技能。&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/znPlbmC.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/FUKXHMZ.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;明天將是我們參加 TAFE 短期課程的結業典禮，12 天的時間過得很快。我們珍惜這段時間所學到的知識和回憶，以及與各種人、事和風景的相遇。回到台灣後，我們將整理這些經歷，製作成一份作品，展示我們在這 12 天中所學到的一切！&lt;/p&gt;
&lt;h2&gt;第十一天&lt;/h2&gt;
&lt;p&gt;今天是澳洲研習的第十一天，是我們 TAFE 短期課程的結業典禮。&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/qq4CTs0.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;隨後在 TAFE 吃了最後一頓飯，便結束了這次的短期課程！&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.imgur.com/jCcE3hd.jpg&quot; width=&quot;50%&quot; height=&quot;50%&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;10:30 PM 的飛機，從 Brisbane 出發，回到桃園機場。&lt;/p&gt;
&lt;h2&gt;第十二天&lt;/h2&gt;
&lt;p&gt;經過八小時的折磨，4:50 AM 抵達了溫暖的家鄉。&lt;/p&gt;
&lt;p&gt;一下飛機，熟悉感都來了，潮濕的空氣環境、親切的繁體中文。但同時也提醒著我們，這趟旅程要畫下一個句點了。莫名地有些感傷。十二天前，大家都還不熟悉彼此，經過一天天的相處，氣氛逐漸熱絡起來，但卻要說再見了。慶幸的是，在場的幾乎都是留北的同學，甚至同校（北科就有三位了...），因此未來還有機會再見！&lt;/p&gt;
&lt;p&gt;臨走前，跟大家道別，氣氛有些感傷，但每個人都心照不宣，離開是人生必經的過程，是緣分將我們牽在一起，很高興遇到新北市的各位，也很高興與各位留下一個特別的回憶，我會永遠記得這段經歷的！&lt;/p&gt;
&lt;h1&gt;結語&lt;/h1&gt;
&lt;p&gt;經過這次的研習，由於這是我第一次出國，因此所有事物都很新奇，很感謝這次有機會能夠參與到新北市教育局資助的赴海外研習。&lt;/p&gt;
&lt;p&gt;以下我會就我的個人看法闡述澳洲「都市」的優缺點（我們行動的地點都在都市，都市跟其他地方差距想必是巨大的），以及台灣需要改善的地方。&lt;/p&gt;
&lt;p&gt;優點：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. 富含藝術氣息的建築物&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;幾乎每棟建築物都像精心設計過的，能讓人感受到有用心在市容上。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. 生活貼近大自然&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;即便身在都市，路邊出現澳洲白䴉（垃圾鳥）、大火雞都是見怪不怪的。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. 建築物都是貼著地形蓋的&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;不會亂砍樹破壞大自然，路樹的數量也是很多，綠化程度很高。&lt;/p&gt;
&lt;p&gt;缺點：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. 物價高&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;澳洲時薪大多是台灣的二至三倍，所以對他們算正常。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. 氣候乾燥&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;每天都要擦護唇膏 QQ，或跑廁所喝水...。（Weiting 表示：我不會再陪你喝廁所水了）&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. 小便斗沒有擋板！&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;每天都生活在被小便斗支配的恐懼中，不然就是完全沒小便斗，只有一條溝...。國外廁所內的小便斗數量也是少得可憐，常常只有一個。&lt;/p&gt;
&lt;p&gt;一些相異的地方：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. 盛行自煮食&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;大家幾乎都在超商買食材回家煮，或帶自己用的便當在外吃，但外食幾乎都要 15 塊以上，要是我也是花不下去啦...而且幾乎都是漢堡、牛排、炸魚薯條這種重複性超高的食物。吃到快吐了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. 重視工安問題&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;當時在 TAFE 的汽車維修工廠時，接觸到大電壓的部件，儘管天氣炎熱，仍然會戴上手套（三層...）、戴頭盔、腳上放絕緣墊，跟臺灣的「得過且過，老師傅道理」差很多。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Work Life Balance&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;平日就是正常上班，但大部分人只工作到下午四點就下班了，假日則會去野餐，去看看大自然，博物館走走，晚上去跑個 party，每天都是充實的。（還有個離奇的事，平日接觸的澳洲人都很正常，到了晚上，一堆喝醉、腦袋打結的澳洲人在路上遊蕩。）&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. 水可以直接生飲&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;雖然喝起來生生的，但能喝就好。&lt;/p&gt;
&lt;p&gt;這次的分享就到這邊！最後再次感謝新北市教育局的資助、指導老師、家人朋友們的支持鼓勵，沒有你們就沒有我！&lt;/p&gt;
</content:encoded></item></channel></rss>