当前位置:首页>Linux>深入理解Linux内存缓存:Cache的底层原理

深入理解Linux内存缓存:Cache的底层原理

  • 2026-06-27 02:07:30
深入理解Linux内存缓存:Cache的底层原理

大家好,我是蟹老板~

在 Linux 系统的运维与开发中,很多人对 buff/cache 有误解。比如有人觉得 free 内存越少,系统内存压力就越大;把定期执行drop_cache当成“万能清内存神器”;甚至认为 Direct IO 一定比 Buffered IO 性能更好。这些错误认知,很容易让我们在优化系统、排查故障时走弯路,做出得不偿失的操作。

实际上,这些问题的根源都在于对 Linux 内存 Cache 机制的不了解。

红色的地方就是Page Cache,Page Cache是内核管理的内存,它属于内核。

一、 误区解答

第一个误区:free内存越少,系统内存压力越大。其实这是典型的“只看表面”,Linux的内存管理没这么简单。buff/cache占用的内存,并不是“占死不放”的——当系统需要更多内存时,内核会自动回收Cache里不常用的部分,分配给其他进程。所以千万别只看free内存的多少,不然很容易误判系统状态。

第二个误区:定期执行drop_cache能解决所有内存问题。很多人看到内存使用率高,就下意识执行drop_cache,觉得清掉缓存就万事大吉。但在生产环境里,盲目执行这个操作,很可能导致性能抖动、IO瞬间被打满、业务超时,反而帮了倒忙——毕竟清掉缓存后,后续的文件读写都得直接访问磁盘,IO压力瞬间就上来了。

第三个误区:Direct IO一定比Buffered IO性能好。确实,某些场景下Direct IO能绕开Page Cache,减少缓存管理的开销,但它也有“小脾气”——要求用户缓冲区按特定规则对齐,还会失去Cache的缓存优势。大多数常规场景下,Buffered IO借助Page Cache,反而能让文件读写更快、更高效。

二、Linux 内存 Cache 核心概念

2.1 此 Cache 非彼 Cache

在聊Linux内存Cache之前,咱们先分清几个容易混淆的“Cache”,不然越学越乱,避免后续理解出现偏差。

首先是内存Page Cache和CPU Cache——虽然都叫“Cache”,但俩者完全不是一回事,层级和作用差得很远。

CPU Cache是硬件层面的缓存,在CPU和主存之间,主要作用是缓解CPU和主存的速度差距,把CPU近期可能用到的数据、指令提前存起来,让CPU能快速获取,不用每次都去主存找。

而Page Cache是Linux内核搞的软件层面缓存,存在于内存中,专门用来缓存磁盘文件数据,减少磁盘IO操作,让文件读写更快。简单说,CPU Cache服务于CPU,Page Cache服务于文件系统,俩者各司其职,别搞混啦。

另外,还有内核态Cache和用户态缓存的区别。内核态Cache是操作系统内核自己实现的,比如Page Cache、块设备缓存,由内核统一管理,所有进程都能“共享”,不用我们手动操作,核心目的是提升整个系统的性能。用户态缓存是应用程序自己搞的,比如Redis缓存,由开发者根据业务需求设计,和内核态Cache没关系,主要用来优化应用自身的性能,比如减少数据库查询次数,让接口响应更快。

2.2 Linux Page Cache 是什么?

Linux Page Cache,说白了就是内存里的“磁盘文件中转站”,在Linux内存体系里占着核心位置。内核之所以搞这么个东西,就是为了弥补磁盘IO和内存之间的“速度鸿沟”——毕竟磁盘读写速度比内存慢太多,有了这个中转站,能大大减少磁盘访问次数,提升系统整体效率。

Page Cache的最小缓存单位是内存页(Page),32位系统默认4KB,64位系统有些是8KB,这个设计是为了让内存和磁盘的数据交换更高效。因为磁盘读写按“块”来,内存管理按“页”来,两者大小匹配,能减少数据传输的开销。比如我们读一个文件,内核会把文件按内存页大小拆分,缓存到Page Cache里;后续再读这个文件的相同内容,直接从缓存里拿,不用再去访问磁盘,速度能快很多。

咱们可以做个简单实验,直观感受下Page Cache的作用:找一个100MB的文件,用“time cat /path/to/file”命令读一次,记下时间;再读一次,你会发现第二次的时间明显缩短——这就是Page Cache的功劳,第一次读的时候,文件数据已经被加载到缓存里了,第二次直接从缓存读取,自然更快。这也能看出来,Page Cache在弥补磁盘和内存的性能差距上,确实很给力。

2.3 Buffer vs Cache 区别

关于Buffer和Cache的区别,估计很多人都纠结过,甚至吵过架。其实只要搞懂它们的“前世今生”,这个问题就迎刃而解了。

早期Linux内核(2.4版本之前),Buffer和Cache是两个独立的“小伙伴”,分工很明确:Buffer专门缓存块设备数据,比如磁盘的扇区数据,相当于块设备和文件系统之间的“缓冲垫”。比如有很多小的写请求同时过来,Buffer会先把这些数据存起来,合并成一个大的写请求,再一次性写入磁盘,这样能减少磁盘的寻道时间,提高效率。

而Cache呢,主要缓存文件的实际内容——比如我们读一个文档、一张图片,文件的数据会被缓存到Cache里,后续再访问,直接从内存拿就行,不用再去磁盘折腾,大大提升读速度,尤其适合那些经常被访问的文件。

不过从Linux 2.4版本开始,内核进行了优化,Buffer慢慢“融入”了Page Cache体系,变成了它的一个子集,主要负责块设备数据的缓冲。现在两者功能有重叠,但还是有细微区别:Buffers主要缓存还没写入磁盘的原始块数据,Cached主要缓存已经从磁盘读到内存的文件内容。

想更直观地区分它们,咱们可以看/proc/meminfo文件:Buffers字段是块设备缓冲区占用的内存,Cached是文件缓存占用的内存,SReclaimable是可回收的Slab缓存(里面也包含一部分和Page Cache相关的内存)。看这几个字段,就能清楚知道系统里Buffer和Cache的使用情况,后续管理内存也更有方向。

2.4 快速查看系统Cache状态

快速了解系统的Cache状态,几条简单命令就能搞定。

首先是free命令,在终端输入“free -h”:

total        used        free      shared  buff/cache   availableMem:           7.7G        1.5G        1.2G        847M        5.0G        6.0GSwap:          2.0G          0B        2.0G

这里面,buff/cache字段就是缓冲区和缓存区总共占用的内存,包含了Buffers和Cached两部分,一眼就能看出系统缓存用了多少。而available字段更关键,它表示系统当前实际可用的内存,包含了free内存和可回收的Cache内存,比free字段更能反映系统的内存真实状态。

输入“vmstat -s”,会输出一堆系统统计信息,其中“buffers”和“cached”字段,分别对应free命令里的Buffers和Cached,能更详细地看到两者的具体大小。除此之外,vmstat还能看到内存换页、CPU、磁盘等信息,帮我们全面了解系统运行状态。

有时候我们还想知道,具体哪些文件占用了Page Cache,这时候可以用pcstat工具。比如想查看/var/log/syslog这个文件的缓存情况,输入“pcstat -p /var/log/syslog”,就能看到这个文件缓存了多少页、占用了多少内存。知道这些,就能判断哪些文件被频繁访问,Cache用得合理不合理,后续优化也更有针对性。

三、核心底层原理:Page Cache 设计与工作流程

3.1 为什么操作系统必须要有 Page Cache?

咱们先搞明白一个核心问题:操作系统为啥非要搞个Page Cache?

答案很简单——内存、机械硬盘、SSD的速度差距太大了,不搞个“中转站”,系统性能会被磁盘拖垮。

咱们量化一下这个差距,更直观:内存的访问延迟是纳秒级(比如DDR4内存,延迟也就几十纳秒);机械硬盘是毫秒级(5-15毫秒,毕竟要靠机械臂寻道、盘片旋转,速度慢得很);SSD好一些,是微秒级(几十到几百微秒),但和内存比还是差了好几个量级。吞吐量方面,内存能到数GB每秒,机械硬盘也就几十MB每秒,就算是高性能SSD,顺序读写也就几千MB每秒,和内存比还是差远了。如果应用程序直接和磁盘交互,系统性能会被磁盘IO拖得巨慢,所以Page Cache必须存在。

而Page Cache的设计灵魂,就是“局部性原理”,分为时间局部性和空间局部性,说起来很简单,咱们举个例子就懂了。时间局部性:一个数据被访问过,短期内大概率会被再次访问——比如Web服务器里,一个热门网页被访问后,很快会有其他用户再访问,把它缓存起来,后续访问就能直接从内存拿。空间局部性:一个数据被访问,它相邻的数据大概率也会被访问——比如读视频文件,读了某一帧,下一帧大概率也会被播放,内核提前把相邻帧缓存起来,播放就不会卡顿。

基于这个原理,Page Cache能实现两个核心好处:一是减少磁盘IO次数,频繁访问的数据缓存到内存,不用每次都去磁盘读;二是降低IO响应延迟,内存比磁盘快太多,从缓存拿数据,应用程序能更快响应。比如数据库系统,Page Cache缓存索引和数据页,查询时不用频繁访问磁盘,速度能提升一大截,并发能力也会更强。

3.2 Page Cache 的核心数据结构拆解

Page Cache能高效工作,全靠内核里几个精心设计的数据结构,咱们不用搞太复杂的源码,重点搞懂三个核心:address_space、XArray、struct page,弄明白它们各自的作用,就懂了Page Cache的管理逻辑。

第一个是address_space,它和inode是“绑定搭档”,相当于每个文件的Cache“管家”。

inode大家都知道,是文件的“身份证”,存着文件的权限、大小、创建时间等元数据;而address_space是和inode绑定的结构体,专门管理这个文件在Page Cache里的缓存页面。比如我们要读一个文件,内核先通过文件路径找到inode,再通过inode找到对应的address_space,就能快速找到这个文件的缓存页面,管理起来特别高效。

第二个是XArray,它的作用是“管理海量缓存页”,是从早期的Radix Tree优化来的。

早期用Radix Tree管理缓存页,随着缓存页越来越多,慢慢出现了性能瓶颈;后来内核引入XArray,优化了树形结构和内存布局,查找速度更快、内存占用更少。XArray以文件偏移量为“钥匙”,通过节点的shift偏移计算和slots数组寻址,能快速定位到目标缓存页——哪怕有亿万级的缓存页,也能很快找到,不会卡顿。

第三个是struct page,相当于每个缓存页的“状态卡片”,管控着缓存页的一生。每个缓存页都有一个对应的struct page,里面记录着缓存页的各种状态:比如是不是脏页(PG_dirty标志位)、是不是被锁定(PG_locked)、数据是不是最新的(PG_uptodate)。内核通过这些状态,就能知道这个缓存页该怎么处理——比如标记为脏页,就知道后续要回写磁盘;检查PG_uptodate,就知道能不能直接用这个缓存页的数据。除此之外,struct page还能关联到address_space,让内核能快速关联不同的数据结构,实现对缓存页的全面管理。

3.3 读操作全链路

3.3.1 系统调用入口:read ()/pread () 流程

咱们先看读操作的“第一步”:应用程序调用read()或pread()读文件时,会从用户态“切换”到内核态,这个过程其实很简单,咱们用通俗的话拆解一下。

以read()函数为例,它是通过软中断触发系统调用的——比如x86架构下,用int 0x80或syscall指令,相当于给内核“发个信号”,让内核来处理读操作。内核收到信号后,会根据系统调用号,找到对应的处理函数sys_read()。

sys_read()先做个“检查”:比如文件描述符是不是有效、用户提供的缓冲区指针是不是合法,没问题的话,就通过文件描述符找到对应的file结构体(里面存着文件的打开状态、读写位置等信息),然后调用VFS(虚拟文件系统)层的vfs_read()函数。VFS是内核的“文件系统中介”,不管是什么类型的文件系统(ext4、XFS等),它都能统一处理,接下来就会进入具体的文件系统,开始在Page Cache里找数据。

3.3.2 缓存查找:XArray 定位逻辑

进入文件系统后,内核的核心任务就是在Page Cache里找目标数据,这时候XArray就该“发力”了,查找逻辑其实很简单,就是“按钥匙找东西”。

内核以文件偏移量为“钥匙”,在XArray的树形结构里逐层查找。XArray的每个节点都有shift偏移和slots数组,内核先根据shift值,计算出文件偏移量在当前节点的索引位置index,然后通过index在slots数组里找到对应的子节点指针;如果子节点存在,就继续在子节点里查找,直到找到目标缓存页。

举个例子:读一个大文件,文件偏移量是10KB(10240字节),XArray节点的shift值是12(对应4KB的页大小),计算index = (10240 >> 12) & (XARRAY_SLOTS - 1),结果是2。内核通过这个索引,在当前节点的slots数组里找到子节点,再继续查找,很快就能找到包含10KB偏移量数据的缓存页。这种层级查找的方式,哪怕缓存页再多,也能快速定位,效率很高。

3.3.3 缓存命中:零磁盘IO快路径

如果内核通过XArray找到了目标缓存页,而且这个缓存页的状态是PG_uptodate(表示数据是最新的,已经从磁盘读到内存了),这就叫“缓存命中”——这是最理想的情况,不用访问磁盘,直接从内存拿数据,速度飞快。

具体流程很简单:内核会调用copy_to_user()函数,把缓存页里的数据,按用户请求的长度,拷贝到用户态的缓冲区里。比如应用程序调用read(fd, buf, 1024),想读1024字节的数据,缓存命中后,内核直接从缓存页里拿1024字节,拷贝到buf里,然后返回读取的字节数,整个过程没有磁盘IO,速度特别快,这就是读操作的“快路径”。

3.3.4 缓存未命中:缺页异常与处理

如果内核在XArray里没找到目标缓存页,或者找到的缓存页状态不是PG_uptodate(数据不是最新的),就会触发“缓存未命中”,进而引发“缺页异常”——简单说,就是内存里没有需要的数据,得去磁盘拿。

第一步,内核先检查内存里有没有空闲的内存页:有就直接申请;没有的话,就根据内存回收策略,回收一些不常用的缓存页,腾出空间。

申请到空闲内存页后,内核会通过文件系统驱动,向磁盘发“读请求”——比如ext4文件系统,会根据inode里的块映射表,找到数据在磁盘上的物理位置,然后让磁盘控制器把数据读到内存里。数据读完后,内核把数据填充到申请的空闲内存页,标记为PG_uptodate(表示数据最新),然后把这个内存页加入到Page Cache里,最后再把数据拷贝到用户态缓冲区,完成读操作。

比如我们读一个从未访问过的文件区域,就会触发缺页异常:内核申请空闲页,通过文件系统驱动读磁盘数据,填充到内存页,加入Page Cache,再拷贝给用户——整个过程虽然多了磁盘IO,但后续再读这个区域,就能直接从缓存拿数据了。

3.3.5 预读机制:预判读行为,减少缺页

内核还有个“智能操作”——预读机制,简单说就是“猜你接下来要读什么,提前把数据加载到缓存里”,减少缺页次数,让读操作更快。

比如我们以顺序方式读一个大文件(比如看视频、读日志),内核发现这种“顺序读”模式后,就会预判:你现在读了这个4KB的页,接下来大概率会读下一个、下下个页,于是提前把后续的几个页从磁盘加载到Page Cache里。这样等你真的要读后续数据时,数据已经在缓存里了,不用再等磁盘IO,速度自然更快。

而且预读窗口(提前加载的页数)不是固定的,内核会“动态调整”:如果发现你一直保持顺序读,就慢慢扩大预读窗口,加载更多后续数据;如果发现你变成随机读(比如数据库索引查找),就缩小预读窗口,避免加载没用的数据,浪费磁盘IO和内存。比如看视频时,内核会慢慢扩大预读窗口,提前加载更多视频帧,确保播放流畅,不会因为缺页卡顿。

3.4 写操作全链路

3.4.1 系统调用入口:write/pwrite流程

写操作和读操作类似,先从用户态切换到内核态,咱们还是以write()函数为例,拆解一下流程,很容易理解。

应用程序调用write()后,通过软中断陷入内核态,内核找到sys_write()函数,先做参数检查(文件描述符、缓冲区、写入长度是否合法),然后通过文件描述符找到对应的file结构体,调用VFS层的vfs_write()函数,进而找到对应的inode和address_space。

接下来是核心步骤:内核把用户态缓冲区里的数据,拷贝到内核态的Page Cache里。如果目标缓存页不存在,就先申请一个空闲内存页,加入到Page Cache,再调用copy_from_user()函数,把用户态的数据拷贝到缓存页里。这里要注意:Buffered IO是“异步写入”,数据写到Page Cache后,write()函数就直接返回了,不用等数据真正写到磁盘——这也是写操作比较快的原因。比如你调用write(fd, buf, 1024)写数据,内核把buf里的数据拷贝到Page Cache,就告诉你“写好了”,至于什么时候写到磁盘,内核会后续处理。

3.4.2 写回机制:并非写时复制

很多人会把Page Cache的写机制和“写时复制”搞混,其实两者完全不一样,咱们重点说清楚Page Cache的“写回(Write Back)”机制——核心就是“先写内存,再慢慢写磁盘”,提升写效率。

写回机制的逻辑很简单:数据写到Page Cache后,不立即写磁盘,先在内存里缓存起来,同时把这个缓存页标记为“脏页”(通过struct page的PG_dirty标志位),然后就告诉应用程序“写完成”了。后续内核会根据一定的策略,把脏页里的数据异步写到磁盘,这样能减少磁盘IO的次数,提升系统性能。

比如你频繁写小数据,内核不会每次都写磁盘,而是先把这些数据缓存到脏页里,等积累到一定量,再一次性写到磁盘——这样能减少磁盘的寻道时间和旋转延迟,比每次都写磁盘快很多。而且应用程序不用等待磁盘写完成,响应速度也会更快。

3.4.3 脏页的标记与生命周期

脏页的诞生很简单:只要应用程序把数据写到Page Cache,内核就会把对应的缓存页标记为PG_dirty,这个缓存页就变成了脏页,进入内核的“脏页管理体系”,开始它的“一生”。

脏页的生命周期很清晰:首先是“等待刷盘”——被标记为脏页后,就等着内核把它写到磁盘;等待期间,如果这个脏页再次被修改,内核会更新数据,继续保持PG_dirty状态;等满足刷盘条件(比如脏页占比太高、存活时间太长),内核就会把它写到磁盘,刷盘完成后,清除PG_dirty标志位,变成“干净页”,继续留在Page Cache里,供后续读取使用。

举个例子:数据库修改表数据,数据写到Page Cache后,对应的缓存页变成脏页;如果后续又修改了这个数据,脏页的数据会被更新,继续保持脏页状态;等脏页占比达到系统阈值,内核就会把它刷到磁盘,清除PG_dirty,变成干净页——这样既保证了数据不丢失,又提升了写效率。

3.4.4 异步回写:内核线程工作机制

脏页的“异步回写”,是由内核线程flusher/bdi_writeback负责的,相当于专门的“脏页清理工”,什么时候工作、怎么工作,都有明确的规则。

触发脏页刷盘的两个核心条件:一是脏页占系统总内存的比例,达到vm.dirty_background_ratio阈值(比如10%),这时候内核会启动后台回写线程,慢慢清理脏页,避免脏页太多占用内存;二是脏页的存活时间,超过dirty_expire_centisecs(比如30秒),哪怕脏页占比不高,也会被优先刷盘,保证数据的一致性。

回写线程的工作流程很简单,分四步:① 批量收集满足刷盘条件的脏页;② 为每个脏页构造BIO请求(相当于“写磁盘的指令”,包含数据、磁盘位置等信息);③ 把BIO请求提交给块设备驱动,驱动再发给磁盘控制器,完成刷盘;④ 刷盘成功后,清除脏页的PG_dirty标志位,变成干净页。

比如系统脏页占比达到10%(vm.dirty_background_ratio=10%),flusher线程就会启动,遍历Page Cache里的脏页,收集起来构造BIO请求,提交给驱动刷盘,刷完后把脏页变成干净页,整个过程不影响应用程序的正常写操作,很高效。

3.4.5 同步刷盘:fsync/fdatasync实现

有时候我们需要确保数据“真的写到磁盘”,比如数据库提交事务、保存重要文件,这时候就需要用到fsync或fdatasync,它们的作用是“强制刷盘”,保证数据不丢失,咱们说清楚两者的区别和底层逻辑。

首先是区别:fsync会同步“数据+元数据”,比如你写一个文件,调用fsync后,不仅会把文件的数据写到磁盘,还会把文件的inode元数据(大小、修改时间等)也写到磁盘,确保文件系统的一致性;而fdatasync只同步“必要的元数据”,比如只保证数据能被找到,不用同步所有元数据,效率比fsync高一些。

它们的底层实现很简单:调用fsync/fdatasync后,内核会强制触发目标文件的脏页同步回写,不管有没有达到刷盘条件,都会立即把脏页写到磁盘,直到刷盘完成,函数才会返回。这样就能确保数据真的落盘,不会因为系统崩溃、断电等情况丢失——这也是数据库、金融系统等对数据一致性要求高的场景,必须用fsync的原因。

3.5 缓存回收机制:LRU算法解析

Page Cache占用的内存不是“无限增长”的,当系统内存紧张时,内核会自动回收缓存页,腾出空间给应用程序,这个过程靠“内存水位线”和“LRU算法”来实现,咱们拆解开讲,很容易理解。

首先是触发条件:内核会通过“内存水位线”(low、min、high)判断内存压力——当可用内存低于“低水位线(low)”时,就会触发缓存回收,确保系统有足够的可用内存。

然后是回收策略,核心是LRU双链表(活跃链表+不活跃链表),解决了传统LRU算法的缺陷。简单说,内核会把常用的缓存页放到“活跃链表”里,不常用的放到“不活跃链表”里;回收时,优先回收不活跃链表里的缓存页,这样既能释放内存,又能保留常用的缓存,不影响系统性能。

还有一个关键规则:文件页(Page Cache)优先于匿名页(比如进程的堆、栈内存)回收。因为文件页的数据可以重新从磁盘读取,回收风险低;而匿名页的数据只能存在于内存中,回收后无法恢复,所以内核会优先回收文件页。

具体回收逻辑:如果是干净页(没被修改过),直接释放,腾出内存;如果是脏页,先把它回写到磁盘,变成干净页后,再释放。这样既保证了数据不丢失,又能高效释放内存,确保系统稳定运行。

四、进阶深度拆解:内核级 Cache 特性

4.1 预读机制的内核级实现:顺序预读 vs 随机预读

预读机制分两种:顺序预读和随机预读,适用场景不一样,内核的处理方式也不同,咱们结合实际场景,把两者讲清楚,后续优化也能对症下药。

顺序预读是内核的“主力优化方向”,适合大文件顺序读场景,比如看视频、读日志、拷贝大文件。当内核检测到你在“顺序读”(比如从文件开头读到结尾,不跳着读),就会预判后续要读的数据,提前加载到Page Cache里,而且预读窗口会动态调整——你一直顺序读,窗口就慢慢扩大,加载更多后续数据,减少缺页次数;如果中途停止或跳着读,窗口就缩小,避免浪费资源。

比如读一个1GB的视频文件,一开始内核可能只预读后续4个页(16KB),随着你持续播放,预读窗口扩大到16个页(64KB),提前加载更多视频帧,确保播放流畅,不会卡顿——这就是顺序预读的优势。

而随机预读,就比较“鸡肋”了。随机读是跳着读(比如数据库索引查找,一会儿读这个位置,一会儿读那个位置),内核很难预判你接下来要读什么,这时候如果还按顺序预读,大概率会加载到没用的数据,浪费磁盘IO和内存。所以内核会自动收缩预读窗口,甚至停止预读,避免做无用功。

优化建议也很简单:大文件顺序读,就适当增大预读参数(比如readahead_kb),扩大预读窗口,充分发挥预读优势;小文件随机读,就减少预读,或者用应用层缓存(比如Redis)分担压力,避免预读失效导致的性能浪费。

4.2 脏页回写的内核规则与参数底层逻辑

脏页回写的核心,是四个内核参数,搞懂这四个参数的含义和作用,就能轻松优化脏页回写,避免回写风暴,咱们一个个讲,不用记复杂的源码,重点记“怎么用”。

第一个:dirty_background_ratio,后台回写阈值。当脏页占系统总内存的比例达到这个值,内核会启动后台回写线程(flusher),异步清理脏页,比如设为10%,就是脏页占比到10%,就开始慢慢清理,避免脏页堆积。

第二个:dirty_ratio,同步回写阈值,比dirty_background_ratio更严格。当脏页占比达到这个值,正在写数据的进程会被“阻塞”,直到脏页被刷盘完成,才能继续写操作——比如设为20%,脏页占比到20%,写进程就会卡住,强制清理脏页,防止脏页过多耗尽内存。

第三个:dirty_expire_centisecs,脏页过期时间,单位是厘秒(1厘秒=0.01秒)。比如设为3000厘秒(30秒),一个脏页在内存里存了超过30秒,不管脏页占比多少,都会被优先刷盘,保证数据的一致性,避免脏页长期存留在内存里。

第四个:dirty_writeback_centisecs,后台回写线程的检查周期。比如设为500厘秒(5秒),后台回写线程每5秒检查一次脏页,看看有没有需要刷盘的,确保脏页不会长期堆积。

这里要注意一个“坑”:回写风暴——短时间内大量脏页同时刷盘,导致磁盘IO瞬间被打满,系统load飙升,业务延迟。规避方法很简单:内核会根据系统负载和磁盘IO繁忙程度,调整回写速度,比如磁盘忙的时候,就放慢回写速度,避免占用过多IO资源。

不同场景的调优建议:高吞吐日志场景(频繁写日志,产生大量脏页),可以适当增大dirty_background_ratio和dirty_ratio,延长脏页在内存的停留时间,减少同步回写对写操作的阻塞;低延迟数据库场景(对数据一致性要求高),可以缩短dirty_expire_centisecs,让脏页尽快刷盘,同时降低dirty_background_ratio,让脏页及时被清理,避免延迟抖动。

4.3 特殊场景的 Cache 交互逻辑

4.3.1 Direct IO:如何彻底绕开 Page Cache?

有些场景下,我们需要绕开Page Cache,直接和磁盘交互,这就是Direct IO,不过它有“使用条件”,也有“性能代价”,咱们讲清楚,避免盲目使用。

绕开Page Cache的两个核心条件:① 用户缓冲区的地址,必须按4KB边界对齐(比如用memalign函数分配内存,指定4096字节对齐);② 缓冲区的长度,必须是4KB的整数倍——这是因为磁盘读写按4KB块进行,对齐后才能避免数据传输错误。

适用场景:主要是数据库系统,比如MySQL的InnoDB引擎,它有自己的缓冲池(应用层缓存),如果再用Page Cache,就会出现“双重缓存”——内存被重复占用,还可能导致数据一致性问题,所以用Direct IO绕开Page Cache,直接管理磁盘读写,更高效、更安全。

性能代价:绕开Page Cache,就失去了缓存的优势,每次读写都要直接访问磁盘,IO延迟会大幅增加,尤其是随机读写场景,磁盘的寻道时间和旋转延迟会让性能大打折扣。比如小文件随机读写,用Direct IO的速度,可能比Buffered IO慢很多——因为Buffered IO能利用Page Cache,减少磁盘IO,而Direct IO只能频繁访问磁盘。

4.3.2 共享内存(Shmem)/Tmpfs:为什么它们的内存占用会算在 Cache 里?

很多人疑惑:Shmem(共享内存)和Tmpfs(临时文件系统),明明是用内存存储数据,为什么它们的内存占用会被算在buff/cache里?其实答案很简单——它们的底层实现,依赖Page Cache。

Shmem和Tmpfs,本质上是“把内存当磁盘用”,它们的文件数据,其实都是存在Page Cache里的,和普通文件的缓存逻辑一样。Linux内核的Page Cache,是统一管理所有文件系统的文件数据的,不管是磁盘上的文件,还是Shmem/Tmpfs里的文件,只要是“文件数据”,都会被缓存到Page Cache里,方便后续快速访问。

举个例子:在Tmpfs里创建一个文件,写入数据,这些数据会存在内存里,同时也会被缓存到Page Cache里;后续再读这个文件,内核直接从Page Cache里拿数据,速度很快。正因为如此,它们的内存占用,会被统计在buff/cache里——其实就是Page Cache的占用。

适用场景:适合存储临时数据、进程间共享数据,比如临时日志、进程间传递的大数据,因为数据存在内存里,读写速度快;但要注意,系统重启后,这些数据会丢失,不能用来存储需要持久化的数据。

4.3.3 大页(HugePage)与 Page Cache:大页是否会使用 Page Cache?

大页(HugePage)和Page Cache的关系,很多人搞不清楚,其实核心结论很简单:默认情况下,大页不参与Page Cache的文件缓存,两者是“各司其职”的。

大页的设计初衷,是优化虚拟内存管理,减少页表项的数量,降低内存管理的开销——比如数据库的大内存表、大数据分析的大型数据集,用大页能显著提升内存访问效率,因为页表更少,内存寻址更快。而Page Cache的核心作用是缓存磁盘文件数据,两者的设计目标不一样,所以默认情况下,大页不参与Page Cache。

比如数据库用大页存储数据,会直接向系统申请大页内存,把数据存在大页里,不会把数据缓存到Page Cache中——这样能避免Page Cache的管理开销,让大页的优势充分发挥。

特殊情况:如果应用程序需要把大页里的数据,和磁盘文件进行交互(比如把大页数据写入文件),这时候就需要通过Page Cache来同步数据,但这种情况很少见,需要特殊配置和编程实现。

注意事项:大页的大小是固定的(比普通页大很多),使用时要提前规划内存,避免大页占用过多内存,导致其他进程申请不到内存;而且大页的管理方式和普通页不同,内存回收和分配时,可能会有性能问题,需要合理优化。

4.4 并发场景下的 Cache 锁机制

4.4.1 Page Lock:缓存页的并发访问保护,什么时候会触发锁竞争?

多线程、多进程同时访问Page Cache时,很容易出现“数据冲突”——比如一个线程在读缓存页,另一个线程在写这个缓存页,可能会读到脏数据;或者多个线程同时写一个缓存页,导致数据错乱。所以内核引入了Page Lock(页锁),保护缓存页的并发访问安全。

触发锁竞争的常见场景有两个:

第一个:多线程同时读写同一文件的缓存页。比如Web服务器的多个线程,同时读、写同一个配置文件的缓存页——写线程需要“独占”缓存页,确保数据修改完整;读线程可以并发,但写线程在操作时,读线程必须等待,这就会触发锁竞争,导致线程等待,影响并发性能。

第二个:脏页回写与读取并行。比如一个缓存页是脏页,正在被回写线程刷盘(回写时会锁定缓存页),这时候有线程要读这个缓存页,就必须等待回写完成,才能获取锁,进而读取数据——这也会触发锁竞争,增加读取延迟。

4.4.2 XArray 的细粒度锁设计:如何解决高并发下的缓存查找性能瓶颈?

早期内核用Radix Tree管理缓存页,在高并发场景下,有个严重的问题:全局锁——所有线程查找缓存页时,都要抢同一个全局锁,锁竞争特别激烈,导致缓存查找速度变慢,成为性能瓶颈。

为了解决这个问题,内核引入了XArray的“细粒度锁”设计——每个XArray节点,都有自己独立的锁,线程查找缓存页时,只需要获取当前节点的锁,不用抢全局锁。这样一来,不同线程可以同时在XArray的不同节点上查找,互不干扰,锁竞争大幅减少,缓存查找的性能也提升了很多。

举个例子:系统有大量缓存页,多个线程同时查找不同文件偏移量的缓存页——因为每个节点有自己的锁,线程A在节点1查找,线程B在节点2查找,不用互相等待,能并行查找,并发性能大幅提升,这就是细粒度锁的优势。

4.4.3 常见锁竞争场景

多线程读写同一文件,是最常见的锁竞争场景,也是影响系统并发性能的关键,咱们给出几个简单易操作的优化方案,直接就能用到工作中。

方案一:减少同一文件的并发写操作。如果业务允许,把写操作串行化(比如用锁控制,同一时间只有一个线程写),避免多个线程同时写同一个文件,从根源上减少锁竞争。

方案二:合理设置预读参数。增大预读窗口,提前加载更多数据到Page Cache,减少因缺页导致的缓存查找次数——查找次数少了,锁竞争的概率也会降低。比如读频繁的文件,适当增大readahead_kb,让线程更多地从缓存拿数据,减少对缓存页的查找操作。

方案三:文件分区处理。把一个大文件,拆分成多个小文件,或者把不同的数据,存在不同的文件分区里,让不同的线程读写不同的分区——这样就不会争夺同一个文件的缓存页锁,并发性能会明显提升。比如数据库,把不同的数据表存在不同的分区,多个线程并行读写不同分区,互不干扰。

五、实战篇:Cache 故障排查与调优

5.1 排查工具集

5.1.1 基础监控工具

free命令:最常用的内存监控工具,输入“free -h”,就能快速看到系统内存的整体情况,重点看buff/cache字段(缓存总占用)和available字段(实际可用内存)。比如buff/cache持续增加,说明系统频繁读写文件,缓存了大量数据;如果available内存很少,说明系统内存真的紧张,需要进一步排查。

vmstat命令:比free更详细,输入“vmstat -s”,能看到Buffers、Cached的具体大小,还有内存换页、CPU、磁盘等信息。比如内存换页次数(si、so)频繁增加,说明系统内存不足,正在频繁交换内存,这时候可以看看buff/cache是不是占用太多,能不能回收。

iostat命令:专门分析磁盘IO性能,输入“iostat -x”,能看到磁盘的读写速率、IO等待时间、IO请求数等。排查Cache问题时,重点看磁盘IO是不是很高——如果磁盘IO飙升,但buff/cache占用也很高,可能是Cache命中率太低,导致大量数据需要从磁盘读取,这时候就需要优化Cache。

sar命令:系统活动报告工具,输入“sar -b”,能看到系统的IO传送速率,比如每秒读、写的块数,每秒传输的字节数。通过sar,能观察一段时间内的IO变化,判断Cache和磁盘IO的关联——比如某段时间读操作突然增加,而Cache命中率很低,大概率是Cache出现了问题。

5.1.2 进阶诊断工具

如果基础工具找不到问题根源,就需要用进阶工具,能精准定位Cache的具体问题,比如哪个文件占用缓存多、Cache命中率低、哪个进程占用Cache多。

pcstat:查看指定文件的Cache占用情况。比如想知道/var/log/syslog这个文件,在Page Cache里缓存了多少,输入“pcstat -p /var/log/syslog”,就能看到缓存页数、占用内存大小。如果发现某个不常用的文件,占用了大量Cache,就可以针对性优化(比如调整文件访问方式)。

cachestat:统计系统层面的Cache命中率。输入“cachestat”,会实时输出读请求数、读命中数、读命中率,还有写请求数、写命中数。如果读命中率很低(比如低于80%),说明Cache没发挥作用,需要优化(比如调整预读参数、优化文件访问模式)。

cachetop:实时监控进程级的Cache读写情况。输入“cachetop”,能看到每个进程的Cache读写统计,比如进程ID、名称、读操作数、写操作数、缓存命中率。如果发现某个进程的Cache命中率极低,而且读写频繁,就可以深入分析这个进程的代码,看看是不是文件读写逻辑不合理,导致Cache无法生效。

5.1.3 内核级调试工具

如果基础工具、进阶工具都搞不定,就需要用到内核级调试工具,page-types和/proc/pid/pagemap,这俩工具能帮咱们“穿透”到缓存页的底层细节,精准定位那些隐蔽的Cache问题,不过操作稍微复杂一点。

page-types,它的核心作用是“查看系统中所有内存页的类型和状态”,比如哪些是Page Cache的文件页、哪些是匿名页、哪些是脏页、哪些被锁定了,用它能快速摸清缓存页的整体分布,排查缓存页异常占用的问题。

举个常用用法:输入“page-types -a”,就能列出系统中所有内存页的详细信息,包括页的地址、类型(File、Anon等,File就是Page Cache相关的文件页)、状态(脏页、锁定页等)。如果发现系统里有大量异常的脏页堆积,或者有很多被长期锁定的缓存页,就可以用这个命令定位,看看是哪个进程、哪个文件导致的。

再比如,想专门查看Page Cache相关的文件页,输入“page-types -a -t File”,就能过滤出所有文件页,重点看它们的状态——如果有很多文件页长期处于“脏页”状态,说明回写机制可能出了问题,或者磁盘IO太忙,导致脏页刷盘不及时,这时候就需要进一步排查磁盘或回写参数。

然后是/proc/pid/pagemap,这个文件更“精准”,专门查看某个进程占用的内存页详情,比如进程的内存页哪些是缓存页、哪些是匿名页,缓存页对应的是哪个文件,甚至能看到缓存页的命中情况,适合排查进程级的Cache异常。

用法很简单:先找到目标进程的PID(比如用ps命令查看),然后查看“/proc/[PID]/pagemap”文件,不过这个文件的内容是二进制的,不能直接查看,需要用工具解析(比如pagemap工具)。解析后能看到每一个内存页的信息,比如“是否为缓存页”“对应的文件inode”“缓存页是否命中”等。

举个实际场景:某个进程内存占用异常高,怀疑是它占用了大量Page Cache,就可以用pagemap工具解析这个进程的pagemap文件,看看它的内存页中,文件页(Cache)占比多少,对应的是哪些文件。如果发现它缓存了很多不常用的大文件,就可以针对性优化,比如调整文件访问逻辑,或者手动释放对应的缓存。

这里提醒一句:这两个工具是内核级调试工具,需要root权限才能使用,而且输出信息比较多,新手不用全部看懂,重点关注“文件页类型”“脏页状态”“进程关联的缓存页”这几个关键信息,就能解决大部分隐蔽的Cache问题。

5.2 常见故障排查

5.2.1 故障1:buff/cache 持续飙升,free 内存越来越少,系统卡顿

这是最常见的Cache相关故障,很多小伙伴遇到这种情况,第一反应就是“清缓存”,但盲目清缓存可能适得其反,咱们一步步排查、解决,既治标又治本。

第一步:先判断是不是“假异常”。用free -h查看available内存,如果available内存还比较充足(比如大于系统总内存的20%),说明buff/cache的飙升是正常的——Linux内核会尽量利用空闲内存做缓存,提升系统性能,这种情况不用处理,系统后续会自动回收不常用的缓存。

第二步:如果available内存很少(小于系统总内存的10%),且系统出现卡顿,就需要排查原因。先用pcstat工具,查看哪些文件占用了大量Cache,命令是“pcstat -a”,能列出所有被缓存的文件及其占用的缓存大小。

第三步:根据排查结果处理。如果是大量不常用的大文件占用了Cache,比如日志文件、临时文件,就可以先删除这些文件(如果不需要的话),或者用“echo 3 > /proc/sys/vm/drop_caches”手动释放缓存(注意:生产环境尽量避开业务高峰操作,避免IO波动);如果是常用文件占用Cache,说明缓存使用合理,此时系统卡顿可能是内存不足,需要扩容内存,或者优化应用,减少内存占用。

避坑提醒:不要定期执行drop_caches,比如写定时任务每小时清一次缓存,这样会导致Cache频繁被清空,后续文件读写都要访问磁盘,IO压力飙升,反而让系统更卡。只有在available内存不足、系统卡顿,且确认是无用缓存占用时,才手动释放。

5.2.2 故障2:IO 延迟飙升,Cache 命中率极低(低于 80%)

Cache命中率低,意味着大部分文件读写都要直接访问磁盘,IO延迟自然会飙升,尤其是随机读写场景,这个问题会更明显,咱们分步骤排查解决。

第一步:用cachestat工具确认命中率。输入“cachestat”,实时查看读命中率(read hit%),如果持续低于80%,就说明Cache没发挥作用,需要优化。

第二步:排查命中率低的原因,主要有两种情况:一是文件访问模式不合理,比如大量小文件随机读写,内核很难缓存,导致命中率低;二是预读参数设置不当,预读窗口太小,无法提前加载足够的数据,导致频繁缺页。

第三步:针对性优化。如果是大量小文件随机读写(比如数据库查询、小文件存储),可以用应用层缓存(比如Redis),把频繁访问的小文件数据缓存起来,减少对Page Cache的依赖;如果是预读参数不当,针对大文件顺序读场景,适当增大预读窗口,比如执行“blockdev --setra 16384 /dev/sda”(将预读大小设为16KB),扩大预读范围,提升命中率。

补充技巧:如果是数据库场景,还可以调整数据库的缓存配置(比如MySQL的innodb_buffer_pool_size),让数据库自身的缓存承担更多压力,减少对Page Cache的依赖,间接提升整体缓存命中率。

5.2.3 故障3:脏页堆积,触发同步回写,导致业务写延迟抖动

这种故障常见于高吞吐写场景,比如日志写入、数据备份,表现为业务写操作偶尔卡顿,查看系统状态会发现,脏页占比飙升到dirty_ratio阈值,写进程被阻塞,直到脏页刷盘完成。

第一步:用vmstat -s查看脏页情况,重点看“dirty”字段,再用iostat -x查看磁盘IO,会发现磁盘写IO(wMB/s)瞬间飙升,IO等待时间(%util)接近100%,说明脏页刷盘导致磁盘IO被打满。

第二步:优化脏页回写参数,根据业务场景调整,核心是避免脏页堆积到同步回写阈值。比如高吞吐日志场景,执行以下命令调整参数(临时生效,重启失效):

# 提高后台回写阈值,让脏页更早开始后台回写echo 15 > /proc/sys/vm/dirty_background_ratio# 提高同步回写阈值,减少写进程阻塞的概率echo 30 > /proc/sys/vm/dirty_ratio# 缩短脏页过期时间,让脏页尽快刷盘,避免堆积echo 2000 > /proc/sys/vm/dirty_expire_centisecs# 缩短后台回写线程检查周期,及时清理脏页echo 300 > /proc/sys/vm/dirty_writeback_centisecs

第三步:如果调整参数后还是有抖动,说明磁盘IO性能不足,需要升级磁盘(比如从机械硬盘换成SSD),或者做磁盘分区,将写操作分散到多个磁盘,减轻单个磁盘的IO压力。

5.2.4 故障4:多线程读写同一文件,出现锁竞争,并发性能下降

这种故障常见于Web服务器、日志服务等多线程场景,表现为系统CPU使用率不高,但业务并发上不去,查看进程状态会发现,很多线程处于“等待锁”状态(比如用top命令查看,线程状态为D或R+)。

第一步:用cachetop工具查看,会发现该文件的Cache读写频繁,且命中率不高,同时多线程竞争同一个文件的缓存页锁。

第二步:按之前提到的优化方案操作,优先选择“文件分区处理”——把一个大文件拆分成多个小文件,比如日志文件按小时拆分,让不同的线程读写不同的小文件,避免争夺同一个文件的缓存页锁;如果业务不允许拆分文件,就把写操作串行化,用锁控制同一时间只有一个线程写文件,虽然会降低写并发,但能避免锁竞争导致的整体性能下降。

补充优化:适当增大预读参数,让线程更多地从Cache读取数据,减少对缓存页的查找操作,间接减少锁竞争的概率。

5.3 生产环境调优

5.3.1 不同场景的 Cache 调优参数配置(临时+永久)

不同业务场景,Cache的优化方向不一样,整理了3个常见场景的调优参数,临时生效和永久生效的方法都给大家。

场景1:高吞吐日志场景(频繁写小文件,对写延迟要求不高)

核心目标:减少同步回写对写操作的阻塞,提升写吞吐,允许脏页短期堆积。

# 临时生效(重启失效)echo 15 > /proc/sys/vm/dirty_background_ratio  # 后台回写阈值15%echo 30 > /proc/sys/vm/dirty_ratio            # 同步回写阈值30%echo 2000 > /proc/sys/vm/dirty_expire_centisecs  # 脏页过期20echo 300 > /proc/sys/vm/dirty_writeback_centisecs  # 回写线程每3秒检查一次blockdev --setra 16384 /dev/sda  # 预读大小设为16KB(根据磁盘调整)# 永久生效(重启仍有效),编辑/etc/sysctl.conf,添加以下内容,然后执行sysctl -p生效vm.dirty_background_ratio = 15vm.dirty_ratio = 30vm.dirty_expire_centisecs = 2000vm.dirty_writeback_centisecs = 300

场景2:低延迟数据库场景(MySQL/PostgreSQL,对读写延迟要求高)

核心目标:减少脏页堆积,确保数据及时刷盘,避免延迟抖动,同时提升Cache命中率。

# 临时生效echo 5 > /proc/sys/vm/dirty_background_ratio   # 后台回写阈值5%,尽早回写echo 10 > /proc/sys/vm/dirty_ratio             # 同步回写阈值10%,避免写阻塞echo 1000 > /proc/sys/vm/dirty_expire_centisecs  # 脏页过期10秒,尽快刷盘echo 200 > /proc/sys/vm/dirty_writeback_centisecs  # 回写线程每2秒检查一次blockdev --setra 32768 /dev/sda  # 预读大小设为32KB,提升顺序读命中率# 永久生效,添加到/etc/sysctl.confvm.dirty_background_ratio = 5vm.dirty_ratio = 10vm.dirty_expire_centisecs = 1000vm.dirty_writeback_centisecs = 200

场景3:大文件顺序读场景(视频服务、大数据分析,对读速度要求高)

核心目标:最大化预读优势,提升Cache命中率,减少磁盘IO,加快读速度。

# 临时生效blockdev --setra 65536 /dev/sda  # 预读大小设为64KB(大文件建议设为64-128KB)echo 20 > /proc/sys/vm/dirty_background_ratio  # 后台回写阈值20%echo 40 > /proc/sys/vm/dirty_ratio            # 同步回写阈值40%# 永久生效,添加到/etc/sysctl.confvm.dirty_background_ratio = 20vm.dirty_ratio = 40

5.3.2 应用层配合优化

内核参数调优只是一方面,应用层配合优化,才能让Page Cache的作用最大化,避免“双重缓存”“缓存失效”等问题。

技巧1:避免双重缓存。如果应用程序有自己的缓存(比如Redis、MySQL的缓冲池),尽量绕开Page Cache,用Direct IO,避免内存被重复占用。比如MySQL的InnoDB引擎,可在my.cnf中配置“innodb_flush_method = O_DIRECT”,让数据库直接读写磁盘,不经过Page Cache,节省内存。

技巧2:优化文件访问模式。尽量避免大量小文件随机读写,比如把多个小文件合并成一个大文件,减少Cache查找次数;对于频繁访问的文件,尽量保持顺序读写,让内核的预读机制发挥作用,提升命中率。

技巧3:合理使用Tmpfs。对于临时文件、频繁读写的小文件(比如会话文件、临时日志),可以存在Tmpfs里,利用内存的高速读写特性,同时Tmpfs的内存占用会算在buff/cache里,内核会自动管理,不用手动清理。

技巧4:控制文件缓存时间。对于有效期短的文件(比如缓存文件、临时数据),应用程序可以在使用完后,手动释放对应的Cache,比如用posix_fadvise函数,告知内核该文件后续不再使用,让内核及时回收缓存,避免占用内存。

5.3.3 生产环境避坑

很多Cache相关的故障,都是因为操作不当导致的,常见的6个生产环境必避的坑:

1.  不盲目执行drop_cache:除非确认是无用缓存占用大量内存,且系统出现卡顿,否则不要手动清缓存,尤其是业务高峰时段,清缓存会导致IO瞬间飙升,业务超时。

2.  不随意调整脏页回写参数:脏页参数调整要结合业务场景,比如低延迟场景不能把dirty_ratio设太高,否则会导致脏页堆积,触发同步回写,增加延迟。

3.  不滥用Direct IO:Direct IO适合数据库等有自己缓存的场景,普通业务用Buffered IO即可,滥用Direct IO会失去Cache优势,导致IO延迟升高。

4.  不忽视大页的使用风险:大页适合内存密集型场景(比如数据库),但要提前规划内存,避免大页占用过多内存,导致其他进程申请不到内存。

5.  不忽视Cache锁竞争:多线程读写同一文件时,要做好文件拆分或写串行化,避免锁竞争导致并发性能下降。

6.  不只看free内存判断系统状态:判断内存是否充足,优先看available内存,而不是free内存,避免误判系统状态,做出不必要的优化操作。

六、总结:

看到这里,相信大家已经彻底搞懂Linux内存Cache的核心逻辑了——其实Cache没有那么复杂,本质上就是内核为了弥补内存和磁盘的速度差距,设计的一个“智能中转站”,核心就是Page Cache,再加上缓冲、预读、回写、回收等机制,共同提升系统性能。

Linux内存Cache的优化,核心就是“顺势而为”——顺应内核的设计逻辑,结合业务的访问模式,让Cache能充分缓存常用数据,减少磁盘IO,同时避免缓存堆积、锁竞争等问题。不用追求“极致优化”,只要能避开误区、合理配置,就能让系统的内存和IO性能稳定发挥,减少故障发生。

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-07-04 08:12:41 HTTP/2.0 GET : https://f.mffb.com.cn/a/491737.html
  2. 运行时间 : 0.112946s [ 吞吐率:8.85req/s ] 内存消耗:4,564.13kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=25b3e164a68b26bb5a27a2479d8b8868
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000744s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000773s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000350s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000277s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000477s ]
  6. SELECT * FROM `set` [ RunTime:0.000219s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000525s ]
  8. SELECT * FROM `article` WHERE `id` = 491737 LIMIT 1 [ RunTime:0.000511s ]
  9. UPDATE `article` SET `lasttime` = 1783123961 WHERE `id` = 491737 [ RunTime:0.020598s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 67 LIMIT 1 [ RunTime:0.000472s ]
  11. SELECT * FROM `article` WHERE `id` < 491737 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000655s ]
  12. SELECT * FROM `article` WHERE `id` > 491737 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.000701s ]
  13. SELECT * FROM `article` WHERE `id` < 491737 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.003564s ]
  14. SELECT * FROM `article` WHERE `id` < 491737 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.001091s ]
  15. SELECT * FROM `article` WHERE `id` < 491737 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.003267s ]
0.115452s