Linux 文件缓存 (一)
缓存印象
缓存给人的感觉就是可以提高程序运行速度,比如在桌面环境中,第一次打开一个大型程序可能需要10秒,但是关闭程序后再次打开可能只需5秒了。这是因为运行程序需要的代码、数据文件在操作系统中得到了缓存,第二次运行程序时可以直接中内存中读取不需要经过磁盘的读取了。除了文件内容外,系统还对文件系统的目录项进行了缓存,这样就不用依次重新从磁盘上查找目录和文件了。从此也可以看出文件/目录缓存是与文件关联的,而不是与某个特定的进程关联的。因为进程结束后,文件缓存依然存在。
缓存查看
Linux中的文件缓存可以通过free命令来进行查看,下面是从一台运行数据库服务的服务器上得到的结果:
[cadmin@bigdb ~]$ free -h total used free shared buffers cached Mem: 188G 187G 799M 33G 0B 72G -/+ buffers/cache: 115G 73G Swap: 63G 2.8G 60G
-h参数表示使用易于查看的数据单位(默认使用byte)
可以看到第一行的free列中空间只有799MB了,不过不必慌张,cached列中显示占用的空间中72GB用来做了缓存,缓存不是必须存在的,在内存空间紧张时可以减少缓存项。也可以手工进行释放。
丢弃缓存
[cadmin@bigdb ~]$ ls /proc/sys/vm/ admin_reserve_kbytes hugepages_treat_as_movable mmap_min_addr page-cluster block_dump hugetlb_shm_group nr_hugepages panic_on_oom compact_memory laptop_mode nr_hugepages_mempolicy percpu_pagelist_fraction dirty_background_bytes legacy_va_layout nr_overcommit_hugepages scan_unevictable_pages dirty_background_ratio lowmem_reserve_ratio nr_pdflush_threads stat_interval dirty_bytes max_map_count numa_zonelist_order swappiness dirty_expire_centisecs memory_failure_early_kill oom_dump_tasks user_reserve_kbytes dirty_ratio memory_failure_recovery oom_kill_allocating_task vfs_cache_pressure dirty_writeback_centisecs min_free_kbytes overcommit_kbytes zone_reclaim_mode drop_caches min_slab_ratio overcommit_memory extfrag_threshold min_unmapped_ratio overcommit_ratio
在/proc/sys/vm目录中列出了一些当前虚拟内存系统的状态和参数。我们可以 手工向drop_caches这个文件写入数值进行相应的丢弃缓存操作
1 - 丢弃页缓存(页用来缓存文件内容)
2 - 丢弃文件目录节点缓存(用来缓存元数据,目录结构)
3 - 丢弃上述两者
[root@bigdb cadmin]# sync [root@bigdb cadmin]# echo "1" > /proc/sys/vm/drop_caches [root@bigdb cadmin]# free -h total used free shared buffers cached Mem: 188G 114G 74G 33G 0B 70M -/+ buffers/cache: 114G 74G Swap: 63G 2.8G 60G
可以看到此时cached一项已经从72GB一下子减少到了70MB,不过这样做一般情况下是不应该的。我们需要让系统尽可能的利用可用内存来提供程序运行速度,光有内存可用量并没什么实质的用处。缓存主要还是被页缓存占用,执行第一项后基本也就不会缩小了。执行这个操作不会对以后的缓存策略造成影响,这个操作只是手工触发一次清空(刷出)缓存的操作。
缓存验证
首先生成一个随机文件,大小为1GB
dd if=/dev/urandom of=rnd.dat bs=1MB count=1024
如果刚刚清空了页面缓存此时在用free命令时cached一项应该就在1GB左右
root@controller:~# free -h total used free shared buffers cached Mem: 62G 13G 49G 904K 69M 1.0G -/+ buffers/cache: 12G 50G Swap: 63G 9.9M 63G
(另外一台服务器上的结果),可见对文件的输出也会生成对应的缓存页。为了以后的测试我们先清空一下缓存。
root@controller:~# free -h total used free shared buffers cached Mem: 62G 12G 50G 904K 4.4M 37M -/+ buffers/cache: 12G 50G Swap: 63G 9.9M 63G
读取速度
然后我们来测试文件的读入速度,连续两次使用dd命令
root@controller:~# dd if=rnd.dat of=/dev/null bs=1MB 1024+0 records in 1024+0 records out 1024000000 bytes (1.0 GB) copied, 2.58692 s, 396 MB/s root@controller:~# free -h total used free shared buffers cached Mem: 62G 13G 49G 904K 1.3M 1.0G -/+ buffers/cache: 12G 50G Swap: 63G 9.9M 63G root@controller:~# dd if=rnd.dat of=/dev/null bs=1MB 1024+0 records in 1024+0 records out 1024000000 bytes (1.0 GB) copied, 0.393087 s, 2.6 GB/s
root@controller:~# dd if=rnd.dat of=/dev/null bs=1MB
1024+0 records in
1024+0 records out
1024000000 bytes (1.0 GB) copied, 0.323672 s, 3.2 GB/s
可以看到第一次在无系统缓存的情况下读取是将近400MB/s,而第二次在有了缓存的情况下是2.6GB/s,最后一次则达到了3.2GB/s(这里使用的分区是RAID1上的,RAID卡自带缓存)。读取上的速度提升非常明显。
写入速度
首先先复制一份1GB的数据文件以备后用。使用/dev/zero作为输入来源,使用dd命令进行写入测试(不丢弃原来的缓存)
root@controller:~# dd if=/dev/zero of=rnd.dat bs=1024MB count=1 1+0 records in 1+0 records out 1024000000 bytes (1.0 GB) copied, 3.91219 s, 262 MB/s root@controller:~# dd if=/dev/zero of=rnd.dat bs=1024MB count=1 oflag=direct 1+0 records in 1+0 records out 1024000000 bytes (1.0 GB) copied, 2.66657 s, 384 MB/s
direct即不使用缓存直接进行写入操作,direct模式居然比使用缓存的还要快,这非常不合理。如果使用strace跟踪第一条命令的话,会发现很多时间其实用在了close调用上。
root@controller:~# strace -tt dd if=/dev/zero of=rnd.dat bs=1024MB count=1 20:24:00.207073 execve("/bin/dd", ["dd", "if=/dev/zero", "of=rnd.dat", "bs=1024MB", "count=1"], [/* 23 vars */]) = 0 20:24:00.207857 brk(0) = 0x84c000 ....... 20:24:00.212135 open("/dev/zero", O_RDONLY) = 3 20:24:00.212246 dup2(3, 0) = 0 20:24:00.212342 close(3) = 0 20:24:00.212432 lseek(0, 0, SEEK_CUR) = 0 20:24:00.212528 open("rnd.dat", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3 20:24:00.625270 dup2(3, 1) = 1 20:24:00.625401 close(3) = 0 20:24:00.625502 mmap(NULL, 1024012288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1cd975c000 20:24:00.625646 read(0, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024000000) = 1024000000 20:24:01.183562 write(1, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 1024000000) = 1024000000 20:24:02.454654 close(0) = 0 20:24:02.454884 close(1) = 0 20:24:04.815079 open("/usr/share/locale/locale.alias", O_RDONLY|O_CLOEXEC) = 0 20:24:04.815331 fstat(0, {st_mode=S_IFREG|0644, st_size=2570, ...}) = 0 20:24:04.815444 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1d16dd3000 20:24:04.815553 read(0, "# Locale name alias data base.\n#"..., 4096) = 2570 20:24:04.815704 read(0, "", 4096) = 0 20:24:04.815796 close(0) = 0 20:24:04.815885 munmap(0x7f1d16dd3000, 4096) = 0 20:24:04.816016 open("/usr/share/locale/en_HK/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory) 20:24:04.816120 open("/usr/share/locale/en/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory) 20:24:04.816215 open("/usr/share/locale-langpack/en_HK/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory) 20:24:04.816307 open("/usr/share/locale-langpack/en/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory) 20:24:04.816442 write(2, "1+0 records in\n1+0 records out\n", 311+0 records in 1+0 records out ) = 31 20:24:04.816594 write(2, "1024000000 bytes (1.0 GB) copied", 321024000000 bytes (1.0 GB) copied) = 32 20:24:04.816747 write(2, ", 4.1912 s, 244 MB/s\n", 21, 4.1912 s, 244 MB/s ) = 21 20:24:04.816869 close(2) = 0 20:24:04.816959 exit_group(0) = ? 20:24:04.822877 +++ exited with 0 +++
close(1)用了比write还多的时间,有理由相信,在close调用上面进行了相关的文件同步写入工作。