原文:http://blog.jobbole.com/52898/linux
本文由伯乐在线-高磊翻译自Metin Doslu。欢迎加入技术翻译小组。转载请参见文章末尾处的要求。
算法
声明:咱们经常觉得,一旦咱们(的代码)出了什么情况,那确定是操做系统在做祟,而在99%的状况下,结果都会是别的缘由。所以咱们会谨慎地做出是操做系统致使了某个问题这样的假设,除非你遇到了与下面的例子相似的状况。
一切从咱们的一个客户报告了他们的CitusDB集群的性能问题开始。这个客户设计的集群使得他们的工做数据集合能够放进内存,可是他们的查询次数显示他们的查询已经须要访问磁盘。这天然会致使查询效率降低10倍到100倍。
咱们开始着手研究这个问题,首先检查CitusDB的查询分发机制,而后再检查机器上安装的PostgreSQL实例。发现都不是致使该问题出现的缘由。接下来的一些发现:
客户的工做数据是某一天的查询日志。一旦他们看完了某一天的数据,他们会开始查询下一天的数据。
他们的查询大都是连续的I/O操做,使用索引的状况并很少。
某一天的数据会占用一个节点超过60%的内存(但仍是大大小于整个可用的内存)。他们实例上没有别的使用内存的程序。
咱们假设,由于每一天的数据能够容易的放进内存,Linux 内存管理器最终会把那一天的数据都放进页缓存,一旦客户开始查询下一天的日志时,新的数据会进入页缓存,至少,这是一个使用LRU退化策略的简单缓存(管理器)会作的事情。
可是LRU在用做页替换策略算法时有两个缺陷。第一,精确的LRU实如今一个系统环境下成本过高了;第二,内存管理器还须要把数据使用的频率考虑在内,读入一 个大文件时并不会立刻清除整个cache,所以。Linux使用了比 LRU 更复杂的算法,而这个算法与咱们以前描述过的问题协做的效果并很差。
举例说明。假设你的内核版本号高于2.6.31 ,而你在使用一个内存为68GB的EC2集群,好比你有两天的点击流数据。每一天的数据都能超过60%的总的内存,单个来看,都很容易能放进内存。
$ 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内存管理器会将该文件页放进页缓存,下一次运行时,会直接从内存里面读取。
$ timewc-l clickstream.csv.1
336006288 clickstream.csv.1
real 10m4.575s
...
$ timewc-l clickstream.csv.1
336006288 clickstream.csv.1
real 0m18.858s
如今咱们切换到次日的点击流文件。咱们再屡次运行 wc 命令来把文件装进内存。使用一个类LRU的策略会将第一天的数据淘汰,并将次日的数据装进内存。不幸的是,在这种状况下,无论你运行多少次,Linux 内存管理器都不会把次日的数据装进内存。
$ timewc-l clickstream.csv.2
336027448 clickstream.csv.2
real 9m50.542s
$ timewc-l clickstream.csv.2
336027448 clickstream.csv.2
real 9m52.265s
事实上,若是你遇到这种状况,惟一能把次日的数据装进内存的办法就是手动清除掉页缓存,很明显,这个作法会比问题带来的危害更大,但单就咱们的这个小测试而言,确实凑效了。
$ echo1 | sudotee/proc/sys/vm/drop_caches
1
$ timewc-l clickstream.csv.2
336027448 clickstream.csv.2
real 9m51.906s
$ timewc-l clickstream.csv.2
336027448 clickstream.csv.2
real 0m17.874s
回到上一步,这儿的问题在于Linux如何管理本身的页缓存。Linux内存管理器会将文件系统的页面放到两种类型的队列里面。一个队列(临近访问内存队列,下面简称:临近队列)放了最近访问到的页面。另外一个队列(频率访问内存队列,下面简称:频率队列)保留了那些被屡次访问到的页面。
在最新的内核版本中,内存管理器将可用的内存公平的分发给两个队列,尽可能在保护频繁访问的页面和探测最近使用的页面之间达到一个折衷的平衡。换言之,内核为频率队列保留了50%的可用内存。
在以前的例子里,两个列表一开始都是空的。当第一天的数据被引用的时候,会先进入临近队列。在第二次被引用的时候,被提高到了频率队列。
接下来,当用户想使用次日的数据进行工做时,数据文件大于可用内存的50%,可是临近队列的空闲空间却没那么大。所以,对这个文件的顺序扫描就致使了内存的置换震荡。 第二个文件中的第一个文件系统的页面会先进入临近队列,可是一旦临近队列空间被占满了之后,这个页就被从队列中置换出来了。所以,第二个文件中没有两个页面会在临近队列中停留足够长的时间,由于他们的引用数一直在递增。
幸运的是,这个问题只有在当你知足以上咱们列出的三点要素时才会发生。当咱们在这里讨论的时候,问题正在被修复中。若是感兴趣的话,你能够在Linux邮件列表下阅读更多关于原始问题报告以及提议的一些修复办法。
对于咱们来讲,真正利索的是很容易就定位到了问题所在。由于Citus继承自PostgreSQL,一旦咱们发现了这个问题,就能够很快的在Postgres上复现,以后咱们向linux邮件组提交了咱们的发现,今后社区开始接手。
想发表评论?请加入hacker news的讨论。
原文连接:
Metin Doslu
翻译:
伯乐在线
-
高磊
译文连接:
http://blog.jobbole.com/52898/
[
转载必须在正文中标注并保留原文连接、译文连接和译者等信息。
]