使用大数据时,别忘了关注Linux内存管理器

系统
我们常常以为,一旦我们(的代码)出了什么状况,那肯定是操作系统在作祟,而在99%的情况下,结果都会是别的原因。因此我们会谨慎地作出是操作系统导致了某个问题这样的假设,其实不然,本文带大家一起研究下。

声明:我们常常以为,一旦我们(的代码)出了什么状况,那肯定是操作系统在作祟,而在99%的情况下,结果都会是别的原因。因此我们会谨慎地作出是操作系统导致了某个问题这样的假设,除非你遇到了与下面的例子类似的情况。

一切从我们的一个客户报告了他们的CitusDB集群的性能问题开始。这个客户设计的集群使得他们的工作数据集合可以放进内存,但是他们的查询次数显示他们的查询已经需要访问磁盘。这自然会导致查询效率下降10倍到100倍。

我们开始着手研究这个问题,首先检查CitusDB的查询分发机制,然后再检查机器上安装的PostgreSQL实例。发现都不是导致该问题出现的原因。接下来的一些发现:

 

  1. 客户的工作数据是某一天的查询日志。一旦他们看完了某一天的数据,他们会开始查询下一天的数据。
  2. 他们的查询大都是连续的I/O操作,使用索引的情况并不多。
  3. 某一天的数据会占用一个节点超过60%的内存(但还是大大小于整个可用的内存)。他们实例上没有别的使用内存的程序。

 

我们假设,因为每一天的数据可以容易的放进内存,Linux 内存管理器最终会把那一天的数据都放进页缓存,一旦客户开始查询下一天的日志时,新的数据会进入页缓存,至少,这是一个使用LRU退化策略的简单缓存(管理器)会做的事情。

但是LRU在用作页替换策略算法时有两个缺陷。***,精确的LRU实现在一个系统环境下成本太高了;第二,内存管理器还需要把数据使用的频率考虑在内,读入一 个大文件时并不会马上清除整个cache,因此。Linux使用了比 LRU 更复杂的算法,而这个算法与我们之前描述过的问题协作的效果并不好。

举例说明。假设你的内核版本号高于2.6.31 ,而你在使用一个内存为68GB的EC2集群,比如你有两天的点击流数据。每一天的数据都能超过60%的总的内存,单个来看,都很容易能放进内存。

  1. $ ls -lh clickstream.csv.* 
  2. -rw-rw-r-- ec2-user ec2-user 42G Nov 25 19:45 clickstream.csv.1 
  3. -rw-rw-r-- ec2-user ec2-user 42G Nov 25 19:47 clickstream.csv.2 

现在,我们通过对点击流文件运行多次 wc 命令来将该天的数据装进内存。

注意这两次所用的时间差。

***次我们运行该命令时,Linux内存管理器会将该文件页放进页缓存,下一次运行时,会直接从内存里面读取。

  1. $ time wc -l clickstream.csv.1 
  2. 336006288 clickstream.csv.1 
  3. real    10m4.575s 
  4. ... 
  5. $ time wc -l clickstream.csv.1 
  6. 336006288 clickstream.csv.1 
  7. real    0m18.858s 

现在我们切换到第二天的点击流文件。我们再多次运行 wc 命令来把文件装进内存。使用一个类LRU的策略会将***天的数据淘汰,并将第二天的数据装进内存。不幸的是,在这种情况下,不管你运行多少次,Linux 内存管理器都不会把第二天的数据装进内存。

  1. $ time wc -l clickstream.csv.2 
  2. 336027448 clickstream.csv.2 
  3. real    9m50.542s 
  4. $ time wc -l clickstream.csv.2 
  5. 336027448 clickstream.csv.2 
  6. real    9m52.265s 

事实上,如果你遇到这种情况,唯一能把第二天的数据装进内存的办法就是手动清除掉页缓存,很明显,这个做法会比问题带来的危害更大,但单就我们的这个小测试而言,确实凑效了。

  1. $ echo 1 | sudo tee /proc/sys/vm/drop_caches 
  2. $ time wc -l clickstream.csv.2 
  3. 336027448 clickstream.csv.2 
  4. real    9m51.906s 
  5. $ time wc -l clickstream.csv.2 
  6. 336027448 clickstream.csv.2 
  7. real    0m17.874s 

回到上一步,这儿的问题在于Linux如何管理自己的页缓存。Linux内存管理器会将文件系统的页面放到两种类型的队列里面。一个队列(临近访问内存队列,下面简称:临近队列)放了最近访问到的页面。另一个队列(频率访问内存队列,下面简称:频率队列)保留了那些被多次访问到的页面。

在***的内核版本中,内存管理器将可用的内存公平的分发给两个队列,尽量在保护频繁访问的页面和探测最近使用的页面之间达到一个折衷的平衡。换言之,内核为频率队列保留了50%的可用内存。

在之前的例子里,两个列表一开始都是空的。当***天的数据被引用的时候,会先进入临近队列。在第二次被引用的时候,被提升到了频率队列。

接下来,当用户想使用第二天的数据进行工作时,数据文件大于可用内存的50%,但是临近队列的空闲空间却没那么大。因此,对这个文件的顺序扫描就导致了内存的置换震荡。 第二个文件中的***个文件系统的页面会先进入临近队列,但是一旦临近队列空间被占满了以后,这个页就被从队列中置换出来了。因此,第二个文件中没有两个页面会在临近队列中停留足够长的时间,因为他们的引用数一直在递增。

幸运的是,这个问题只有在当你满足以上我们列出的三点要素时才会发生。当我们在这里讨论的时候,问题正在被修复中。如果感兴趣的话,你可以在Linux邮件列表下阅读更多关于原始问题报告以及提议的一些修复办法

对于我们来说,真正利索的是很容易就定位到了问题所在。因为Citus继承自PostgreSQL,一旦我们发现了这个问题,就可以很快的在Postgres上复现,之后我们向linux邮件组提交了我们的发现,从此社区开始接手。

想发表评论?请加入hacker news的讨论。

原文链接: Metin Doslu   翻译: 伯乐在线 高磊
译文链接: http://blog.jobbole.com/52898/

责任编辑:黄丹 来源: 伯乐在线
相关推荐

2016-08-08 17:37:23

大数据搜索

2021-02-21 10:26:41

人工智能AI机器学习

2013-09-04 11:31:45

2017-11-02 10:15:12

时间 1元

2013-09-22 14:16:51

2013-07-05 13:13:15

App

2009-08-22 20:53:41

企业综合布线网络测试布线安装

2013-09-22 17:17:25

2015-02-26 14:58:56

云计算云测试云安全

2012-06-18 09:54:22

2013-03-29 12:52:14

Android位置管理

2013-11-20 15:32:13

红帽RedHatYUM包

2022-03-21 21:28:00

Homebrew包管理器Linux

2013-04-01 12:04:46

数据备份数据备份日

2011-06-16 10:52:10

SUSE管理器Linux

2023-08-15 15:09:18

LinuxCompiz

2017-10-26 11:21:18

IDC

2013-11-25 13:49:50

2023-10-19 08:16:22

LinuxSQL Server

2021-12-09 09:27:22

MacOSHomebrew包管理器
点赞
收藏

51CTO技术栈公众号