page fault带来的性能问题

3 minute read

背景

Linux进程如何访问内存

Linux下,进程并不是直接访问物理内存,而是通过内存管理单元(MMU)来访问内存资源。

原因后面会讲到。

为什么需要虚拟内存地址空间

假设某个进程需要4MB的空间,内存假设是1MB的,如果进程直接使用物理地址,这个进程会因为内存不足跑不起来。

既然进程不是直接访问物理内存,那么进程中涉及的内存地址当然也不是物理内存地址。

而是虚拟的内存地址,虚拟的内存地址和物理的内存地址之间保持一种映射关系,这种关系由MMU进行管理。

每个进程都有自己独立的虚拟地址空间。

什么是MMU

pic

MMU全称是内存管理单元,它将物理内存分割成多个pages,MMU管理进程的虚拟地址空间中的PAGE和物理内存中的PAGE之间的映射关系。

因为是映射,所以随时都可能发生变化,例如某个进程虚拟内存空间中的PAGE1,在不同的时间点,可能出现在物理内存中的不同位置(当发生了页交换时)。

什么是page fault

当进程访问它的虚拟地址空间中的PAGE时,如果这个PAGE目前还不在物理内存中,此时CPU是不能干活的,

Linux会产生一个hard page fault中断。

系统需要从慢速设备(如磁盘)将对应的数据PAGE读入物理内存,并建立物理内存地址与虚拟地址空间PAGE的映射关系。

然后进程才能访问这部分虚拟地址空间的内存。

page fault 又分为几种,major page fault、 minor page fault、 invalid(segment fault)。

major page fault也称为hard page fault, 指需要访问的内存不在虚拟地址空间,也不在物理内存中,需要从慢速设备载入。从swap回到物理内存也是hard page fault。

minor page fault也称为soft page fault, 指需要访问的内存不在虚拟地址空间,但是在物理内存中,只需要MMU建立物理内存和虚拟地址空间的映射关系即可。

(通常是多个进程访问同一个共享内存中的数据,可能某些进程还没有建立起映射关系,所以访问时会出现soft page fault)

invalid fault也称为segment fault, 指进程需要访问的内存地址不在它的虚拟地址空间范围内,属于越界访问,内核会报segment fault错误。

什么是进程的working set

进程的working set是指当前在物理内存中,属于该进程的pages组成的集合。

这个集合中的page数随着系统的运行,可能扩大也可能缩小。

扩大working set是指进程正在访问更多的内存时。

缩小working set是指其他进程正在访问更多的内存,并且整个物理内存的空间不足,需要将某些干净的内存页free掉或者一些脏的内存页swap到交换分区去,如果这个操作设计到当前进程,对当前进程来说就是shrink working set。

缩小working set需要遵循lru (内核的内存老化)算法,并不是随便挑选PAGE进行shrink的。

什么是swap

swap和前面提到的shrink working set有关,如果是干净页(即从读入虚拟地址空间后,没有修改过的页),则直接标记为free,不需要写盘,也不会发生swap。

如果是脏页,那么它需要写盘,或者需要swap 到交换分区。

如何优化swap

建议使用IOPS和读写带宽很高的盘作为SWAP分区,例如PCI-E SSD。

什么时候需要加内存

如果你发现经常发生swap in , out。

说明进程的脏页被换出到swap后,紧接着这些页可能又需要被进程访问,从而这些页需要从swap写入物理内存,发生了hard page fault。

这种情况下,你就需要加内存了,确实是内存不够用了。

hard page fault非常损耗性能,因为发生page fault时,是需要等待的,而且IO通常来说都是比较慢的,容易成为性能瓶颈。

什么是oom, 为什么会发生OOM

前面讲到了shrink working set,是指系统在内存调度时,使用的一种手段,尽可能的让系统能使用有限的内存资源,支撑更多的并发任务。

oom是指系统已经没有足够的内存给进程使用,即能free的都已经free了,能swap out的也已经swap out了,再也不能挤出物理内存的情况。

如果遇到这种情况就会发生OOM,表示系统内存以及不足,Linux会挑选并KILL一些进程,来释放内存。

Linux page相关统计信息

sar

de  >       -B     Report paging statistics.  The following values are displayed:  
  
              pgpgin/s  
                     Total number of kilobytes the system paged in from disk per second.  
  
              pgpgout/s  
                     Total number of kilobytes the system paged out to disk per second.  
  
              fault/s  
                     Number of page faults (major + minor) made by the system per second.  This is not a count of page faults that generate I/O, because some page faults can be resolved without I/O.  
  
              majflt/s  
                     Number of major faults the system has made per second, those which have required loading a memory page from disk.  
  
              pgfree/s  
                     Number of pages placed on the free list by the system per second.  
  
              pgscank/s  
                     Number of pages scanned by the kswapd daemon per second.  
  
              pgscand/s  
                     Number of pages scanned directly per second.  
  
              pgsteal/s  
                     Number of pages the system has reclaimed from cache (pagecache and swapcache) per second to satisfy its memory demands.  
  
              %vmeff  
                     Calculated as pgsteal / pgscan, this is a metric of the efficiency of page reclaim. If it is near 100% then almost every page coming off the tail of the inactive list is being reaped. If it gets too  low  (e.g.  
                     less than 30%) then the virtual memory is having some difficulty.  This field is displayed as zero if no pages have been scanned during the interval of time.  
de>  

一些CASE

1. 如果PostgreSQL 设置了非常大的shared buffer, 为什么会有一段性能低潮(指全力压测时,平时估计感觉不到)

在PostgreSQL shared buffer设得非常大的情况下,Shared buffer作为进程的虚拟地址空间中的一部分,刚启动时这些虚拟地址空间还没有在物理内存中,所以填充过程中会发生page fault,性能较低。

当shared buffer被快速填满后,page fault减少,性能会上来。

例如在压测持续高并发的COPY数据入库时,看这个case https://yq.aliyun.com/articles/8528

在前面几百GB,page fault较多,产生较多的中断(这个case是软的,因为是在shared buffer中进行extend block,么有落盘),性能比shared buffer填满后有一定的差异。

240GB shared buffer,

shared buffer 填满前,2.8GB/s 导入速度

de  >12:13:46 AM  pgpgin/s pgpgout/s   fault/s  majflt/s  pgfree/s pgscank/s pgscand/s pgsteal/s    %vmeff  
12:13:47 AM      8.08 1699410.10  40058.59      0.00 142064.65 137696.97      0.00 137632.32     99.95  
12:13:48 AM     15.84 1634396.04  38605.94      0.00 412391.09 411564.36      0.00 411627.72    100.02  
  
 22  44  33   0   0   0|  32k 2763M| 239B  364B|   0     0 | 184k  335k  
 22  44  34   0   0   0|  12k 2791M| 120B  544B|   0     0 | 184k  336k  
 22  45  33   0   0   0|  12k 2768M| 471B 1198B|   0     0 | 185k  341k  
 22  44  33   0   0   0|  16k 2764M| 210B  454B|   0     0 | 183k  336k  
 22  44  34   0   0   0|  16k 2777M| 239B  364B|   0     0 | 185k  341k  
de>  

shared buffer 填满后, 4.7GB/s导入速度

de  >12:14:40 AM  pgpgin/s pgpgout/s   fault/s  majflt/s  pgfree/s pgscank/s pgscand/s pgsteal/s    %vmeff  
12:14:41 AM     16.33 2831730.61    823.47      0.00 735045.92 725126.53      0.00 724985.71     99.98  
12:14:42 AM     12.00 2760160.00    743.00      0.00 738037.00 728352.00      0.00 728370.00    100.00  
12:14:43 AM     11.88 2751279.21    326.73      0.00 720171.29 715310.89      0.00 715374.26    100.01  
12:14:44 AM     20.00 2788468.00    338.00      0.00 727221.00 720480.00      0.00 720480.00    100.00  
12:14:45 AM     12.50 2858970.83    425.00      0.00 753507.29 740266.67      0.00 740166.67     99.99  
  
 24  18  56   0   0   1|  28k 4703M| 120B  364B|   0     0 | 366k  655k  
 24  18  57   0   0   1|  16k 5031M| 892B  994B|   0     0 | 382k  690k  
 24  19  56   0   0   0|  12k 4833M| 120B  364B|   0     0 | 374k  672k  
 24  19  56   0   0   1|8192B 4737M| 239B  544B|   0     0 | 371k  668k  
 24  19  56   0   0   1|  20k 4826M| 120B  454B|   0     0 | 375k  674k  
de>  

参考

https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/3/html/Introduction_to_System_Administration/s1-memory-virt-details.html

https://en.wikipedia.org/wiki/Page_fault

http://www.cnblogs.com/xelatex/p/3491305.html

https://en.wikipedia.org/wiki/Memory_management_unit

http://pages.cs.wisc.edu/~remzi/OSTEP/vm-segmentation.pdf?spm=5176.100239.blogcont55820.33.KDVPoF&file=vm-segmentation.pdf

http://www.tldp.org/LDP/tlk/mm/memory.html?spm=5176.100239.blogcont55820.44.tOZ1UX

Flag Counter

digoal’s 大量PostgreSQL文章入口