配置ClickHouse分布式DDL記錄自動(dòng)清理

Saturday night萤皂,繼續(xù)超短文模式。

在ClickHouse集群中,我們可以在DDL語(yǔ)句上附加ON CLUSTER <cluster_name>的語(yǔ)法人芽,使得該DDL語(yǔ)句執(zhí)行一次即可在集群中所有實(shí)例上都執(zhí)行,簡(jiǎn)單方便绩脆。每執(zhí)行一條分布式DDL萤厅,會(huì)在配置文件中<distributed_ddl><path>指定的ZooKeeper路徑上寫一條執(zhí)行記錄(路徑默認(rèn)為/clickhouse/task_queue/ddl)。如下圖所示靴迫。

但是惕味,這個(gè)隊(duì)列默認(rèn)似乎不會(huì)自動(dòng)清理,造成znode不斷增長(zhǎng)玉锌,官方文檔中也沒有提供對(duì)應(yīng)的參數(shù)來控制名挥。考慮到手動(dòng)刪除znode可能會(huì)有風(fēng)險(xiǎn)主守,遂去ClickHouse源碼中尋找蛛絲馬跡禀倔,最終在dbms/src/interpreters/DDLWorker.h里找到如下定義:

/// Cleaning starts after new node event is received if the last cleaning wasn't made sooner than N seconds ago
Int64 cleanup_delay_period = 60; // minute (in seconds)
/// Delete node if its age is greater than that
Int64 task_max_lifetime = 7 * 24 * 60 * 60; // week (in seconds)
/// How many tasks could be in the queue
size_t max_tasks_in_queue = 1000;
  • cleanup_delay_period:檢查DDL記錄清理的間隔榄融,單位為秒,默認(rèn)60秒救湖。
  • task_max_lifetime:分布式DDL記錄可以保留的最大時(shí)長(zhǎng)愧杯,單位為秒,默認(rèn)保留7天鞋既。
  • max_tasks_in_queue:分布式DDL隊(duì)列中可以保留的最大記錄數(shù)力九,默認(rèn)為1000條。

將以上參數(shù)加入config.xml的<distributed_ddl>一節(jié)即可涛救。

<distributed_ddl>
  <!-- Path in ZooKeeper to queue with DDL queries -->
  <path>/clickhouse/task_queue/ddl</path>
  <cleanup_delay_period>60</cleanup_delay_period>
  <task_max_lifetime>86400</task_max_lifetime>
  <max_tasks_in_queue>200</max_tasks_in_queue>
</distributed_ddl>

ClickHouse內(nèi)部有專門的線程來清理DDL隊(duì)列畏邢,具體邏輯位于DDLWorker.cpp中,不難检吆,代碼錄如下舒萎。

void DDLWorker::runCleanupThread()
{
    setThreadName("DDLWorkerClnr");
    LOG_DEBUG(log, "Started DDLWorker cleanup thread");

    Int64 last_cleanup_time_seconds = 0;
    while (!stop_flag)
    {
        try
        {
            cleanup_event->wait();
            if (stop_flag)
                break;

            Int64 current_time_seconds = Poco::Timestamp().epochTime();
            if (last_cleanup_time_seconds && current_time_seconds < last_cleanup_time_seconds + cleanup_delay_period)
            {
                LOG_TRACE(log, "Too early to clean queue, will do it later.");
                continue;
            }

            auto zookeeper = tryGetZooKeeper();
            if (zookeeper->expired())
                continue;

            cleanupQueue(current_time_seconds, zookeeper);
            last_cleanup_time_seconds = current_time_seconds;
        }
        catch (...)
        {
            tryLogCurrentException(log, __PRETTY_FUNCTION__);
        }
    }
}

void DDLWorker::cleanupQueue(Int64 current_time_seconds, const ZooKeeperPtr & zookeeper)
{
    LOG_DEBUG(log, "Cleaning queue");

    Strings queue_nodes = zookeeper->getChildren(queue_dir);
    filterAndSortQueueNodes(queue_nodes);

    size_t num_outdated_nodes = (queue_nodes.size() > max_tasks_in_queue) ? queue_nodes.size() - max_tasks_in_queue : 0;
    auto first_non_outdated_node = queue_nodes.begin() + num_outdated_nodes;

    for (auto it = queue_nodes.cbegin(); it < queue_nodes.cend(); ++it)
    {
        if (stop_flag)
            return;

        String node_name = *it;
        String node_path = queue_dir + "/" + node_name;
        String lock_path = node_path + "/lock";

        Coordination::Stat stat;
        String dummy;

        try
        {
            /// Already deleted
            if (!zookeeper->exists(node_path, &stat))
                continue;

            /// Delete node if its lifetime is expired (according to task_max_lifetime parameter)
            constexpr UInt64 zookeeper_time_resolution = 1000;
            Int64 zookeeper_time_seconds = stat.ctime / zookeeper_time_resolution;
            bool node_lifetime_is_expired = zookeeper_time_seconds + task_max_lifetime < current_time_seconds;

            /// If too many nodes in task queue (> max_tasks_in_queue), delete oldest one
            bool node_is_outside_max_window = it < first_non_outdated_node;

            if (!node_lifetime_is_expired && !node_is_outside_max_window)
                continue;

            /// Skip if there are active nodes (it is weak guard)
            if (zookeeper->exists(node_path + "/active", &stat) && stat.numChildren > 0)
            {
                LOG_INFO(log, "Task " << node_name << " should be deleted, but there are active workers. Skipping it.");
                continue;
            }

            /// Usage of the lock is not necessary now (tryRemoveRecursive correctly removes node in a presence of concurrent cleaners)
            /// But the lock will be required to implement system.distributed_ddl_queue table
            auto lock = createSimpleZooKeeperLock(zookeeper, node_path, "lock", host_fqdn_id);
            if (!lock->tryLock())
            {
                LOG_INFO(log, "Task " << node_name << " should be deleted, but it is locked. Skipping it.");
                continue;
            }

            if (node_lifetime_is_expired)
                LOG_INFO(log, "Lifetime of task " << node_name << " is expired, deleting it");
            else if (node_is_outside_max_window)
                LOG_INFO(log, "Task " << node_name << " is outdated, deleting it");

            /// Deleting
            {
                Strings childs = zookeeper->getChildren(node_path);
                for (const String & child : childs)
                {
                    if (child != "lock")
                        zookeeper->tryRemoveRecursive(node_path + "/" + child);
                }

                /// Remove the lock node and its parent atomically
                Coordination::Requests ops;
                ops.emplace_back(zkutil::makeRemoveRequest(lock_path, -1));
                ops.emplace_back(zkutil::makeRemoveRequest(node_path, -1));
                zookeeper->multi(ops);

                lock->unlockAssumeLockNodeRemovedManually();
            }
        }
        catch (...)
        {
            LOG_INFO(log, "An error occured while checking and cleaning task " + node_name + " from queue: " + getCurrentExceptionMessage(false));
        }
    }
}

民那晚安晚安。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蹭沛,一起剝皮案震驚了整個(gè)濱河市臂寝,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌摊灭,老刑警劉巖咆贬,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異帚呼,居然都是意外死亡掏缎,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門煤杀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來眷蜈,“玉大人,你說我怎么就攤上這事沈自∽萌澹” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵枯途,是天一觀的道長(zhǎng)忌怎。 經(jīng)常有香客問我,道長(zhǎng)酪夷,這世上最難降的妖魔是什么榴啸? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮晚岭,結(jié)果婚禮上插掂,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好辅甥,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布酝润。 她就那樣靜靜地躺著,像睡著了一般璃弄。 火紅的嫁衣襯著肌膚如雪要销。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天夏块,我揣著相機(jī)與錄音疏咐,去河邊找鬼。 笑死脐供,一個(gè)胖子當(dāng)著我的面吹牛浑塞,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播政己,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼酌壕,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了歇由?” 一聲冷哼從身側(cè)響起卵牍,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎沦泌,沒想到半個(gè)月后糊昙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡谢谦,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年释牺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片回挽。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡没咙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出厅各,到底是詐尸還是另有隱情镜撩,我是刑警寧澤预柒,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布队塘,位于F島的核電站,受9級(jí)特大地震影響宜鸯,放射性物質(zhì)發(fā)生泄漏憔古。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一淋袖、第九天 我趴在偏房一處隱蔽的房頂上張望鸿市。 院中可真熱鬧,春花似錦、人聲如沸焰情。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)内舟。三九已至合敦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間验游,已是汗流浹背充岛。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留耕蝉,地道東北人崔梗。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像垒在,于是被迫代替她去往敵國(guó)和親蒜魄。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344