0x00前言在之前的笔记中,我们了解到内存本质上是作为外部存储的缓存,以及数据持久化和数据丢失访问等。最后,外部需要磁盘这样的存储,自然需要频繁的I/O操作。本篇笔记主要介绍磁盘的工作原理和I/O的优化方法。0x10的原理是老做法。在学习I/O的性能分析和优化方法之前,我们首先要学习外部存储的原理。从上一篇关于内存的笔记我们知道,内存主要有buff/cache,分别用来缓存磁盘I/O和文件系统I/O。磁盘读写是指直接访问磁盘块设备,也称为原始I/O,文件系统是建立在磁盘上的一种文件管理和组织方式,它位于块设备上。我们的一些应用程序是通过文件系统I/O访问的,有些是通过直接原始I/O访问的磁盘。针对不同的访问方式,我们有不同的优化方式。另外,一般情况下,I/O栈从上到下主要分为三层:文件系统层:包括虚拟文件系统层,为应用程序提供文件访问接口,管理磁盘数据通用块层:包括设备I/OqueuesandI/Oscheduler,管理和调度I/O,然后将其发送到下一级设备设备层:包括存储设备和存储驱动,负责最终的I/O0x11文件系统文件系统是一个文件组织和管理机制的种类,不同的管理机制是不同的文件系统,比如ext4、xfs、ntfs等。在文件系统中,文件不仅仅是存储在磁盘上的数据,数据还附加了元数据。就好像一个“人”除了自己的肉体之外,还有很多信息,比如姓名、出生日期、身份证号码、家庭住址等,才能称得上是人,可以被识别,可以被称呼。在Linux上,文件系统会为每个文件分配两个数据结构来记录文件的元信息和目录结构,以便管理:索引节点:inode(索引节点)记录文件元数据,inode编号,文件大小,权限,修改日志和其他inode一一对应文件,就像每个人对应一张身份证存储在磁盘上,需要占用磁盘空间目录项:dentry(目录项)记录文件名,inode记录文件名文件与其他文件的关系。就像家谱一样,由内核维护,保存在内存中。它也被称为目录条目缓存和索引节点。存在多对一的关系。可以这样理解,一个文件有多个别名,就像一个人可以有多个身份一样。可以是一个人的父亲,也可以是另一个人的儿子。索引节点和目录项的关系如下图所示:从这张图中我们可以了解到以下几点:每个dentry是一个数据结构,包含inode,文件名和其他关联的dentry地址等。inode是存储在磁盘上,而dentry是一个内存缓存。从上篇的内存笔记可以推测,inode也会缓存在内存中。dentry和文件数据之间类似的树形结构存储在根据第六点不同的磁盘块,我们可以推导出另一个知识点,即在为磁盘格式化文件系统时,会将文件系统分为三块:superblock:存放整个文件系统的状态inodeblock:存放inode节点datablock:存放文件数据机械硬盘最小扇区为512B,每次读取这么小的size效率很低,所以文件系统也会将扇区组织成4KB大小的逻辑块,如上图左侧所示,数据块被划分为逻辑块0x1100。虚拟文件系统中所有文件系统的四个基本要素:dentryinode逻辑块超级块,不同的文件系统有不同的组织方式。中间引入了抽象层VFS(VirtualFileSystem),就像Java中抽象方法的实现一样,VFS是一个抽象类,具体的文件系统实现了抽象类定义的接口。如下图所示:我们可以看到Linux支持的文件系统分为:磁盘文件系统、网络文件系统、内存文件系统,文件系统首先要在VFS中找到一个挂载点,用户进程才能通过VFS文件接口访问其中的文件。0x1101文件系统I/O可以通过文件接口读写文件。读写方式的不同导致了I/O的种类很多。主要有四种:标准库是否有缓冲(缓冲由标准库内部实现):BufferedI/O:使用标准库缓存加速文件访问UnbufferedI/O:直接访问direct/indirectI/O/O通过系统调用:DirectI/O绕过操作系统的页面缓存(cache),直接访问文件系统。非直接I/O使用O_DIRECT标志使用缓存阻塞/非阻塞I/O(调用者的角度,注意是否等待调用结果):调用者进行I/O操作,如果被调用者不响应,调用者线程阻塞,不能做任何其他事情。调用者执行I/O操作。如果被调用者没有响应,调用者线程不会被阻塞,可以做其他事情。后续调用者可以通过轮询(调用者过一会检查是否就绪)或事件通知(被调用者准备好后主动通知调用者)获得响应。同步/异步I/O(站在被调用者的角度,关注消息通知机制):同步是指在I/O操作完成之前,被调用者不会将结果返回给调用者。如果调用者被阻塞,那么调用者只能等待被调用者执行完毕;如果调用者是非阻塞的,调用者会先运行,过一会他可能会检查是否可以(roundInquiry)或者调用他(事件通知)异步是指被调用者立即返回I/O操作交给调用者,让调用者先做其他事情,做完了再通知他。0x1102文件系统性能观察能力:df命令缓存:查看/proc/memeinfoSlab、/proc/slabinfo文件查看dentry和inode缓存slabtop工具0x12磁盘I/O磁盘主要分为机械盘和固态盘,磁盘的连续读写速度比随机读写快。在Linux中,磁盘用作块设备。为了减少不同磁盘设备带来的差异,Linux通过一个通用的块层来管理不同的块设备。通用块层的主要功能如下:连接过去和未来,为文件系统提供统一的标准接口,提供统一的框架来管理块设备的驱动程序,对I/O请求进行排队和调度上层,提高磁盘读写效率。Linux支持4种调度算法NONE:不使用任何调度,常用于vmNOOP:先进先出队列,常用于ssdCFQ(CompletelyFairScheduler):完全公平调度器,基于时间片来分配请求eachprocess,支持调度优先级,适用于进程数较多的系统,如桌面,mediaDeadLine:读写不同的I/O队列,多用于I/O压力大的场景,如database0x1200磁盘性能指标usage:处理I/O的时间百分比(只考虑I/O数量,不考虑I/O大小)Saturation:处理I/O的繁忙程度,无法接受I/O请求100%。IOPS(Input/OutputPerSecond):每秒接受的I/O请求吞吐量Quantity:每秒I/O请求大小Responsetime:从I/O请求到响应的时间0X1201整体性能观察:iostat单进程:pidstat、iostopDisk性能测量:fio0x20性能调优是建立在理解I/O栈原理之后,我们分析性能瓶颈的方式大概是iostat观察磁盘整体情况,分析各种指标pidstat观察具体进程的情况,分析各种指标strace结合应用程序分析进程的I/O行为,分析I/O从哪里来,是否正常?其中,文件系统和I/O指标和磁盘I/O指标需要分别分析。另外,在分析这些指标的时候,需要根据具体的I/O场景来分析。除了分析I/OO指标和内存缓存也需要分析。如果要调优I/O,每一层都有不同的手段:应用层使用连续读写代替随机读写,使用系统缓存和外部缓存。比如当MySQL放在Redis前面读写同一个磁盘空间时,当使用mmap(内存文件映射)同步写入时,合并请求以减少访问I/O的次数。如果有多个进程,可以使用cgroup来限制I/O。在CFQ中使用ionice来调整进程的I/O优先级。文件系统Usetheappropriatefilesystemtooptimizefilesystem配置选项来优化文件系统。缓存不需要是持久的。您可以使用tmpfs磁盘和使用SSD组RAID来选择合适的I/O调度算法。不同的进程使用不同的磁盘来增加顺序读场景的预读数据。优化内核,比如调整I/O队列的长度
