使用大数据,别忘了linux内存管理

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


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


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


客户的工作数据是某一天的查询日志。一旦他们看完了某一天的数据,他们会开始查询下一天的数据。
他们的查询大都是连续的I/O操作,使用索引的情况并不多。
某一天的数据会占用一个节点超过60%的内存(但还是大大小于整个可用的内存)。他们实例上没有别的使用内存的程序。
我们假设,因为每一天的数据可以容易的放进内存,Linux 内存管理器最终会把那一天的数据都放进页缓存,一旦客户开始查询下一天的日志时,新的数据会进入页缓存,至少,这是一个使用LRU退化策略的简单缓存(管理器)会做的事情。


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


举例说明。假设你的内核版本号高于2.6.31 ,而你在使用一个内存为68GB的EC2集群,比如你有两天的点击流数据。每一天的数据都能超过60%的总的内存,单个来看,都很容易能放进内存。
1
2
3
$ ls -lh clickstream.csv.*
-rw-rw-r-- ec2-user ec2-user 42G Nov 25 19:45 clickstream.csv.1
-rw-rw-r-- ec2-user ec2-user 42G Nov 25 19:47 clickstream.csv.2

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


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


第一次我们运行该命令时,Linux内存管理器会将该文件页放进页缓存,下一次运行时,会直接从内存里面读取。
1
2
3
4
5
6
7
8
9
$ time wc -l clickstream.csv.1
336006288 clickstream.csv.1
real    10m4.575s
...
 
$ time wc -l clickstream.csv.1
336006288 clickstream.csv.1
 
real    0m18.858s

现在我们切换到第二天的点击流文件。我们再多次运行 wc 命令来把文件装进内存。使用一个类LRU的策略会将第一天的数据淘汰,并将第二天的数据装进内存。不幸的是,在这种情况下,不管你运行多少次,Linux 内存管理器都不会把第二天的数据装进内存。
1
2
3
4
5
6
7
8
9
$ time wc -l clickstream.csv.2
336027448 clickstream.csv.2
 
real    9m50.542s
 
$ time wc -l clickstream.csv.2
336027448 clickstream.csv.2
 
real    9m52.265s

事实上,如果你遇到这种情况,唯一能把第二天的数据装进内存的办法就是手动清除掉页缓存,很明显,这个做法会比问题带来的危害更大,但单就我们的这个小测试而言,确实奏效了。
1
2
3
4
5
6
7
8
9
10
11
12
$ echo 1 | sudo tee /proc/sys/vm/drop_caches
1
 
$ time wc -l clickstream.csv.2
336027448 clickstream.csv.2
 
real    9m51.906s
 
$ time wc -l clickstream.csv.2
336027448 clickstream.csv.2
 
real    0m17.874s

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


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


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


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


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


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


http://blog.jobbole.com/52898/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值