我们的clickhouse版本比较高,看社区记录,clickhouse官方将之前的min_part_size_for_direct_merge改成为min_merge_bytes_to_use_direct_io,Minimal amount of bytes to enable O_DIRECT in merge (0 - disabled)。默认超过10GB时会使用direct_io的方式进行merge。
那我将min_merge_bytes_to_use_direct_io设置足够小,甚至是1byte,是不是就可以完全避免对cache的使用了?答案是否定的,原因是:min_merge_bytes_to_use_direct_io只是读写表数据时使用了direct_io,替换了常用的buffer_io。也就是说只是在数据传输过程不使用cache,节省的是这个环节的cache内存消耗。merge完成后,先通过direct_io将数据写入到磁盘,同时会继续使用cache缓存merge完成后的数据,方便为下一次与其它分区进行快速merge做准备。因为每次merge都是merge旧数据与新数据,因此新合成的分区所使用的cache只会比merge前的更大。direct_io与buffer_io的区别如下图所示。
需要注意的是,设置min_merge_bytes_to_use_direct_io还有一个副作用,当发生merge行为时会导致磁盘IO急剧拉高。因为direct_io是对磁盘进行直接操作,这种IO方式与buffer_io(使用了page cache做缓冲层)相比,带给磁盘的冲击更大。但是如果钞票比较多,可以做磁盘raid,或者增加了SSD,磁盘能够扛住direct_io冲击的同时,还能支持前端的丝滑查询,那就另当别论啦!
另外还设置过相关参数:max_bytes_to_merge_at_max_space_in_pool,用处不大,就不继续介绍了,读者可以自行验证。只能说clickhouse当前没有提供待merge分区文件所占用cache的清理机制。 五、cache清理 一顿操作猛如虎,定睛一看原地杵。clickhouse自身无法限制cahce消耗;“echo 1 > /proc/sys/vm/drop_caches”又太粗暴,会清理掉其它进程加载到cache中的内容。只想搞掉clickhouse占用的大量cache,该怎么办? 有时候你不得不相信车到山前必有路,船到桥头自然直。再次请出vmtouch! vmtouch的help中有一个“-e”的选项,即“evict pages from memory”,顾名思义将page cache从内存中驱逐出去。既然vmtouch可以统计指定文件或目录占用的cache,那自然就可以实现对指定文件或目录的cahce清理! 先来看看执行效果。执行前有内存分布,以及某个目录下cache的使用情况: 执行:vmtouch -e ./* 后,内存分布如下图28所示。惊喜吗?刚好减少了某个目录下占用的30GB cache,同时free内存增加了30GB,实现了对指定目录cache消耗的定点清理!
很好奇vmtouch实现指定目录内存清理的方法,去看了看源码,简单贴两张图就大致明白了。 1、传递指定path;
2、如果该path下无目录,则通过vmtouch_file函数执行cache的释放操作;如果该目录下存在其他文件(包括子目录),则遍历该目录下的所有文件,并通过vmtouch_crawl实现递归调用,回到第1步。 那vmtouch_file函数如何实现cache的释放操作呢?答案就是通过系统调用:posix_fadvise,并使用POSIX_FADV_DONTNEED宏。定义如下: - <font size="3">1 int posix_fadvise(int fd, off_t offset, off_t len, int advice);
- 2
- 3 advice:
- 4 POSIX_FADV_DONTNEED,指定的数据在未来的一段时间内不会被访问,丢弃page cache中的数据</font>
复制代码当advice为POSIX_FADV_DONTNEED时,内核会先将脏页刷盘,再清除相关page cache,从而达到cache释放的目的! 但是查阅资料,发现 posix_fadvise 实现脏页刷盘使用的参数是:WB_SYNC_NONE,即posix_fadvise 不会同步等待其它进程发起的脏页刷盘行为,这可能会带来一个问题:不能完全释放指定路径下的cache,因此可以在执行cache释放前先使用 fsync,将所有的脏页进行回写,完成后再调用 posix_fadvise,实现指定path下所有cache的释放。如果不关心脏页使用的cache是否可以被全被释放,那直接使用posix_fadvise 就好了。
写到这里,可能有些读者会产生一个问题,清理cache就真的很好吗?答案是肯定的。为什么这么说呢,我想可以概括为以下两点: 一是有些服务使用完cache后,这部分cache再也不会被访问,这一类cache消耗当然是需要干掉的; 二是类似我们环境clickhouse这一类应用,它们会再次使用之前使用过的cache。这种情形确实不好办,但是如果不办它,最后就是大家都别玩了,鱼和熊掌不可兼得。因此,对这一类应用,定期清理cache是必要的,通过一个折中的办法让大家共存下来。对依赖cache这一类应用,cache清理后就只有辛苦下磁盘了(人在家中坐,锅从天上来)。 六、结语 摸索cache的过程曲折且漫长,还好最后找到了一个大家都可以接受的办法。文章篇幅比较长,简单总结一下全文吧,概括起来可归纳为以下四点: 1、验证了 hcache 无法统计cache的全面消耗情况,推荐了一种通过 lsof+fincore 探测进程中活跃目录以及文件的方法,并以此作为cache消耗的关键调查线索。这种方法适用于多种场景下的cache消耗调查,不仅仅是clickhosue。 2、通过vmtouch可以统计出某个文件、目录,甚至是嵌套目录下各文件真正使用cache的大小,从而明确cache消耗的分布情况。 3、分析了clickhouse大量消耗cache的原因,探索了clickhouse自身是否具备减少cache使用的能力和机制。 4、提供了清理各文件,或者各指定目录所占用cache的通用方法,属于定点清除的骚操作。 技术是不断实践积累的,在此分享出来与大家一起共勉!
|