1. 前言
Out Of Memory (OOM)到目前為止已經(jīng)出現(xiàn)了 40 年。大概就是當(dāng)某個(gè)應(yīng)用想要使用的內(nèi)存超過了現(xiàn)有可用的內(nèi)存總和嘿悬,本文將不會(huì)具體進(jìn)行贅述。為了防止發(fā)生OOM 脚祟,采取了各種各樣的方式绍在,最常見的就是,當(dāng)申請(qǐng)內(nèi)存時(shí)斗锭,發(fā)現(xiàn)無法申請(qǐng)所需要的內(nèi)存地淀,系統(tǒng)主動(dòng) kill
當(dāng)前內(nèi)存占用最大的應(yīng)用。
這樣帶來的好處是岖是,當(dāng)前應(yīng)用可以正常使用了帮毁。但是,缺點(diǎn)也是顯而易見的:會(huì)讓當(dāng)前最大內(nèi)存占用的應(yīng)用無法正常運(yùn)行豺撑。
在數(shù)據(jù)庫(kù)中烈疚,這尤為常見。比如聪轿,在一臺(tái)數(shù)據(jù)庫(kù)服務(wù)器中運(yùn)行了 MySQL Server 爷肝,同時(shí)要對(duì)這個(gè)數(shù)據(jù)庫(kù)進(jìn)行備份,而執(zhí)行 mysqldump 或者 xtrabackup 或者 mydumper 等等備份命令時(shí)陆错,由于機(jī)器內(nèi)存不足以運(yùn)行備份命令灯抛,而 kill
掉了內(nèi)存占用最大的 MySQL Server
。這帶來的后果就很直接了音瓷,業(yè)務(wù)讀寫失敗对嚼。
同樣的,當(dāng)下的 OLAP 驕子 ClickHouse
也是存在這種問題绳慎,那么他的 OOM 一般是如何引起的呢纵竖,又要如何避免呢?
2. 發(fā)生 OOM 的原因
OOM 是什么我們已經(jīng)知道了偷线,那么磨确,ClickHouse OOM 的原因其實(shí)應(yīng)該主要分為
查詢導(dǎo)致 OOM
寫入導(dǎo)致 OOM
查詢導(dǎo)致 OOM 比較好理解,就是声邦,當(dāng)某個(gè) query 要讀取的數(shù)據(jù)量過大了乏奥,內(nèi)存不夠用。
寫入會(huì)導(dǎo)致 OOM 可能大家不是很理解亥曹,有人如果認(rèn)為 insert into select 是屬于寫入導(dǎo)致 OOM 邓了,這應(yīng)該不算全對(duì)恨诱,畢竟還進(jìn)行了查詢。所以骗炉,接下來照宝,我們仔細(xì)區(qū)分一下這兩類原因。
2.1. 查詢導(dǎo)致OOM
比如某個(gè)大數(shù)據(jù)量的表做聚合排序( GROUP BY
和 ORDER BY
)操作句葵,導(dǎo)致需要將大量的數(shù)據(jù)讀取到內(nèi)存中厕鹃,然后按照 SQL 要求進(jìn)行分組和排序。數(shù)據(jù)對(duì)內(nèi)存的消耗基本是 數(shù)據(jù):內(nèi)存> 1:1 的乍丈。也就是剂碴,如果有 1GB 數(shù)據(jù)需要做聚合排序操作,那么他需要的內(nèi)存是要超過 1GB 的轻专。數(shù)據(jù)量越大忆矛,所需內(nèi)存會(huì)更加巨大。常見的機(jī)器是無法滿足這種需求的请垛。
2.2. 寫入導(dǎo)致OOM
對(duì)于大多數(shù)小內(nèi)存的數(shù)據(jù)庫(kù)服務(wù)器來說催训,如果一次寫入的batch過大都有可能會(huì)引起OOM。但是宗收,對(duì)于 ClickHouse 來說漫拭,可能卻不是這樣。并發(fā)線程數(shù)目20 镜雨,每個(gè)線程只寫入一行嫂侍,每行寫入的列只有 15 個(gè)儿捧。都有可能引發(fā) OOM 荚坞。
可能看到這里會(huì)覺得 ClickHouse 一定設(shè)計(jì)的不合理,要不然為什么如此小的數(shù)據(jù)寫入都會(huì)引發(fā) OOM 呢菲盾?
如果會(huì)有這樣的問題颓影,說明不是很理解 ClickHouse 。ClickHouse 支持多種表引擎懒鉴。其中有一個(gè) MergeTree 族群表引擎诡挂,MergeTree 在 ClickHouse 的地位基本等同于 Innodb 在 MySQL 的地位。MergeTree 引擎是基于 LSM 算法實(shí)現(xiàn)的临谱。每次寫入就會(huì)生成一個(gè)小文件璃俗,然后 ClickHouse Server 再去合并每個(gè)小文件到數(shù)據(jù)文件中。關(guān)于 MergeTree 原理將會(huì)在后面的文章中進(jìn)行詳細(xì)介紹悉默。
理解了MergeTree的 merge 工作后城豁,就比較清晰了。當(dāng)多個(gè)線程每次只寫入一行數(shù)據(jù)時(shí)抄课, insert query 的每個(gè)列會(huì)生成兩個(gè)文件唱星。因此雳旅,按照上面的寫入方式,計(jì)算出消耗的內(nèi)存為:
2MB * 15 * 20 = 600 MB
600MB 可能不大间聊,但是攒盈,換算一下百分比,如果機(jī)器內(nèi)存是 8GB 哎榴,8GB 的 10% 內(nèi)存也只有 800MB 左右呀型豁。而一個(gè)數(shù)據(jù)庫(kù)機(jī)器,一般負(fù)載下內(nèi)存占用達(dá)到機(jī)器的 70% 左右尚蝌。如果突然來這么一次20行數(shù)據(jù)的寫入偷遗,內(nèi)存就會(huì)飆升 10% ,我想這會(huì)是很令人疑惑的一件事驼壶。而且氏豌,如果有人在 AP 中一次寫入只包含一行數(shù)據(jù),那他可能確實(shí)沒有理解 AP 數(shù)據(jù)庫(kù)的精髓所在热凹。
3. 如何避免 OOM
OOM 的原因我們簡(jiǎn)單分析過了泵喘,主要是查詢和不正當(dāng)寫入導(dǎo)致的。因此般妙,避免 OOM 也就變得簡(jiǎn)單起來纪铺。
3.1. 避免查詢時(shí) OOM
對(duì)于如何避免查詢時(shí)發(fā)生 OOM ,數(shù)據(jù)庫(kù)常見的做法就是外排到磁盤碟渺。比如鲜锚,眾所周知的,MySQL 查詢慢了苫拍,就去 explain芜繁,看到結(jié)果有臭名昭著的 Using filesort
而剛好要排序的數(shù)據(jù)量大于session的 sort_buffer 時(shí),就會(huì)自動(dòng)使用磁盤排序了绒极。
而 ClickHouse 的做法也是比較類似骏令。同樣也有 setting 進(jìn)行控制:
max_bytes_before_external_group_by:The max_bytes_before_external_group_by setting determines the threshold RAM consumption for dumping GROUP BY temporary data to the file system. If set to 0 (the default), it is disabled.
max_bytes_before_external_sort:If there is not enough RAM, it is possible to perform sorting in external memory (creating temporary files on a disk). Use the setting max_bytes_before_external_sort for this purpose. If it is set to 0 (the default), external sorting is disabled. If it is enabled, when the volume of data to sort reaches the specified number of bytes, the collected data is sorted and dumped into a temporary file. After all data is read, all the sorted files are merged and the results are output. Files are written to the /var/lib/clickhouse/tmp/ directory in the config (by default, but you can use the tmp_path parameter to change this setting).
3.2. 避免寫入時(shí) OOM
而避免寫入時(shí)OOM ,就不應(yīng)該在強(qiáng)求 ClickHouse 來實(shí)現(xiàn)了垄提。而是需要對(duì)業(yè)務(wù)做一些修改榔袋,比如,降低并發(fā)線程數(shù)铡俐;每個(gè) insert 中采用更大的 batch 凰兑。畢竟,我們同樣不能苛責(zé)向小型 MySQL Server 服務(wù)器 一次寫入 10萬(wàn)行數(shù)據(jù)時(shí)性能不佳呀审丘。
本作品采用 知識(shí)共享署名 4.0 國(guó)際許可協(xié)議 進(jìn)行許可吏够。 轉(zhuǎn)載時(shí)請(qǐng)注明原文鏈接。From TCeason