在没接触操作系统原理之前一直不太明白这个问题,看一些io相关的资料上面写着的缓冲区概念也很模糊,现在才发现这些设计都是有它的原因的。写上层应用的我们是不关心读写函数操作系统的实现,比如要写一个数据库软件,用户通过你提供的操作界面或者api产生数据存储硬盘,有些环节是会导致数据丢失的,为什么会丢失,以及怎么合理使用缓冲区来提升应用整体的性能。
一般我们口中的软件是构建在操作系统之上的应用,这种应用所使用的接口归根结底都是对操作系统进行调用,存储数据时调用系统的write,读数据时调用read,系统实现的好坏直接影响到你的应用程序性能,比如在windows和linux下写10G的文件,前者要消耗10秒钟的时间,后者只需要9秒钟,这时我们可以判定linux下io实现相对的好一点,这只是打个比方这两个操作系统都是十分优秀的系统。你的读写命令发送给系统,系统收到命令要去调用磁盘驱动,操作系统本身就是一个很庞大的软件,就像一个管家一样。当你发送指令时它就会遵守命令去执行。假设一个例子,你是城堡的主人肚子饿了要安排管家从厨房拿来食物,厨房理解为磁盘存储着食物,吃饱需要10个面包,但是你命令管家一次拿来一个这个效率就很低下,吃完面包只能等待,映射到程序上就是读文件一次只读一点数据然后命令操作系统很多次。
解决这种低效的操作最简单的方式就是加入缓冲区,一次命令管家拿来两个面包,这样管家就会少跑5次。这种方式是应用层级的缓冲,其实操作系统很聪明,在你让它一次拿一个的时候它有可能会拿两个在你需要的时候直接递给你,这种是系统级的缓冲。系统之所以缓冲是因为来回的磁盘操作要耗费大量的时间,所以加了这么一个机制,当应用和系统都有缓冲应用的性能会得到比较大的提升。
写为什么会丢失? 读有缓冲区写也有,同样假设一个例子,你在农地收割农作物,然后把收割的作物递给管家让其存放到仓库,这个过程是产生数据,然后写入磁盘。但是你一次只递给管家一点点作物就让它跑一趟显然很浪费时间。管家的操作逻辑就像上面读操作那样。正常情况下这套逻辑没有问题,但是计算器是人造的始终达不到100%完美,总会有一些意外,比如停电,cpu停止工作,磁盘瞬间损坏,等等,当你的数据还在缓冲区中没有写入到磁盘,这份数据就丢失了,一般小网站停电什么的不会有什么影响,因为缓冲区刷新很快,慢是相对的。在一直处于高压的消息队列数据库场景会出现停电时数据丢失。
缓冲区设置多大合适,系统内核的缓冲区大小一般不需要动,除非你有充分的理由,应用层面的缓冲区也不是越大越好,jdk中BufferedInputStream
定义的是8k,应该是个通用的参数,以这个为基准再根据业务进行微调,应该能得到想要的参数。
总结
缓冲区的出现是对性能的妥协,因为cpu交互外部设备很耗时,而交互内存很快,从耗时多的地方一次性多取点,整体性能就会提升。整体有点乱想到哪写到哪q.q。不足以及错误的地方可在评论区指出,欢迎讨论交流。