ClickHouse問題分析:刪除系統表時卡住,長時間不恢復
問題現象
使用drop table system.query_log sync時,發現客戶端一直卡住,長時間無法恢復。日志打印如下:
DatabaseCatalog: Waiting for table bd5888b9-a84c-41a8-bc7e-cefadf81cc43 to be finally dropped
進一步試驗發現,如果我在卡住期間通過http連接后執行新的sql,卡住的命令就能正常執行了。但是,如果沒有新的sql執行,會一直卡住。
問題分析
1、先了解下drop table xxx sync的流程。
# drop table system.query_log sync InterpreterDropQuery::execute executeToTable executeToTableImpl checkTableCanBeDropped StorageMergeTree::shutdown DatabaseAtomic::dropTable detachTableUnlocked tryRemoveSymlink enqueueDroppedTableCleanup if no delay tables_marked_dropped.push_front // 放在list的開頭 else tables_marked_dropped.push_back // 默認8min后刪除 tables_marked_dropped_ids.insert // 在set中存入需要刪除的表 (*drop_task)->schedule() // dropTableDataTask 在DatabaseCatalog::loadDatabases中初始化 if no delay // 有sync或者no delay或者database_atomic_wait_for_drop_and_detach_synchronously為true waitForTableToBeActuallyDroppedOrDetached // 等待table被實際刪除 waitTableFinallyDropped // wait_table_finally_dropped.wait // wait_table_finally_dropped 被通知,且 tables_marked_dropped_ids 中沒有此表 # drop table的后臺線程 DatabaseCatalog::dropTableDataTask // 只有不再使用且drop時間早于當前時間的table,才能被刪除 if table 可以被刪 tables_marked_dropped.erase dropTableFinally StorageMergeTree::drop // 刪數據 shutdown // 直接就return dropAllData // 真正開始刪除了 clearPartsFromFilesystem // 文件系統 disk->removeRecursive // 磁盤 remove // 刪元數據 removeUUIDMappingFinally // 刪uuid映射 tables_marked_dropped_ids.erase wait_table_finally_dropped.notify_all() // 通知不再wait if 還有表需要被刪 dropTableDataTask // 繼續刪表,否則不再調度
從流程上可以知道:
(1)如果不加sync(即no delay),則默認是至少8min后才會真正刪除數據。而在添加了sync后,則將需要刪除的表放在list的開頭,也不用等待8min。
(2)drop的主流程,也不會真正刪除表,而是存入一個tables_marked_dropped中,由后臺線程dropTableDataTask來操刀。如果設置了sync,drop的主流程,會等待后臺線程的通知,然后才會返回客戶端刪除成功。
(3)真正刪除時,需要保證該table沒有流程在使用,通過查看該table的shared_ptr是否是unique來確定。
(4)每次只會刪除一張表。如果還有待刪除的表,則會繼續,不需再等待。如果沒有表需要刪除,則不在觸發此任務。
(5)每次執行完drop table后,其實都會觸發query_log的記錄,進而重建該表,因此,刪除query_log是沒有意義的。
2、再了解下query_log的流程
# query_log的機制 # query_log建立過程 Server::main global_context->initializeSystemLogs SystemLogs::SystemLogs // 初始化各個system log,包括query log createSystemLog // 根據config中的配置生成 SystemLog
這里比較特別的地方是:
(1)query_log在有log記錄的時候,如果發現沒有表的話,會走創建表的流程。如果已經有表了,則會比較列的結構是否一致,不一致的話,需要重建表,并將舊表重命名。
(2)如果在config.xml中修改表級的TTL,即使重啟server,query_log也是不會變化的,因為已經存在該表,且表的列信息是一樣的。如要讓它生效,則需要刪除query_log(會自動重建),或者采用ALTER TABLE的方式。這也是為什么第一次在config.xml中配置query_log時可以生效,而后續修改或者配置時又無法生效的原因。
3、問題分析
結合刪除表時會卡住的時候報錯以及正常流程時應該要有的日志(Removing metadata {} of dropped table),可知,是在dropTableDataTask中判斷table是否沒有地方在使用時出現了問題。
auto it = std::find_if(tables_marked_dropped.begin(), tables_marked_dropped.end(), [&](const auto & elem) { bool not_in_use = !elem.table || elem.table.unique(); bool old_enough = elem.drop_time <= current_time; min_drop_time = std::min(min_drop_time, elem.drop_time); tables_in_use_count += !not_in_use; return not_in_use && old_enough; });
即elem.table.unique()不滿足。
然后,梳理整個流程和現象(在卡住期間通過http連接后執行新的sql,卡住的命令就能正常執行了)可以推理出,應該去看添加新log的流程。
template
在prepareTable中,得到了table的副本,而且一直沒有釋放,elem.table.unique()就一直無法滿足。只有在新添加日志的時候,table變量被覆蓋,原來的query_log的智能指針就被釋放了,從而卡住的流程可以繼續。
ClickHouse
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。