计算机如何存数据?
计算机的数据存储主要分两种:按地址来读写数据,比如内存,norflash等,可以直接读写某一个地址byte的数据(比如0x12345678这个地址);按块来读写数据,比如常见的磁盘,SSD,nandflash等,只能读写一块连续的数据(比如去某一个磁盘,从0开始的16KB数据,512KB数据)。
为什么会存在这两种存储数据的方式?核心问题就是成本了,可以按地址读写数据的存储会比按块来读写的存储贵很多。计算机科学作为一种工程应用类科学,很多复杂的设计就是为了解决各种成本问题。
操作系统和文件系统
操作系统的核心能力就是管理和分配各种资源。存储作为一种重要的计算机资源,操作系统对存储的管理就非常重要了。一般我们在使用各种操作系统的时候,只需要知道目录,文件名就可以进行各种增删改查的操作。文件一般都存在于各种按块读写的设备中,那么文件和存储设备间复杂的关系就需要有“人”来负责管理了,这就是文件系统要干的事情了。
各种类型的文件系统有很多,归根到底做的事情就是管理文件和设备的映射。操作系统是整个系统的核心,负责管理计算机的硬件和软件资源,以及协调各个程序的运行。它提供了访问文件系统的接口,让其他程序可以通过这个接口来读写文件。文件系统是操作系统的一部分,用于管理计算机中的文件和目录。它将文件组织成层次结构,并为每个文件分配一个唯一的标识符,称为文件名。文件系统还负责维护文件的元数据(例如,文件大小、创建日期和访问权限)。磁盘是存储设备的一种,它使用磁性材料来存储数据。在计算机中,磁盘通常指硬盘或固态硬盘。磁盘可以被分成多个分区,每个分区可以被格式化为一个文件系统。当一个程序需要读写文件时,它将向操作系统发出请求。操作系统会根据文件名查找文件系统中的文件,并将文件读取到内存中或将数据写入磁盘中。当操作系统接收到读写请求时,它会将请求传递给文件系统,文件系统再将请求转发给磁盘驱动器,驱动器将数据读取到内存中或写入磁盘中。示意图:
为了提高效率,操作系统会缓存部分可能用到的相关文件到内核空间的内存页缓存中,这样在读取的时候如果这部分数据已经缓存,就避免了磁盘的IO。另外,在写入数据的时候,系统也可以将数据先写入在页缓存中,在某些时刻将内存中的数据刷入到磁盘中。
常见的文件操作
1.文本编辑器
使用文本编辑器的时候,可以实现各种各样的花式操作,比如改中间一个一部分,在头部或者中间某个指定位置新增一部分。但是上文在分析操作系统和文件系统的原理时可以得知,对文件的操作其实只有open/close,read/write,seek这几个操作,那么文本编辑器这些操作是怎么实现的呢?文本编辑器都是基于内存在操作,编辑完写入时就是写入了一个全新的文件。
2.使用程序对文件的中间部分进行修改
对一个1G的文件100M开始的地方进行修改,然后对前后两个文件在磁盘中的具体位置进行分析,结果如下,可以看出来文件在磁盘本身的位置并没有变化。
3.如何提高写文件效率
通过上面的介绍可以知道,用户态的应用程序在写文件时就两步操作,第一步seek到指定为止,第二步就是在这个位置写入数据。在编辑器中非常常见的在指定位置插入数据,删除一段数据这样的操作,在实际的应用程序实现是很低效的。比如在一个1G的文件中,第100M的位置删除10M的数据,用程序来实现时需要经过这几步。
1.open这个1G的文件;
2.seek到110M,并读取接下来的890M数据;
3.seek到100M,将上面读取的890M数据写入到文件;
4.close文件。
通过上面的过程可以看出对文件中间进行修改和删除是非常复杂和低效的。这种实现方式显然是无法支持数据库的增删改的操作。那常见的类数据库应用是如何实现高效的写文件操作呢?有两种比较典型的实现思路。
1.修改文件只有append操作,然后在读的时候通过比较复杂的策略来实现,在空闲时来merge之前不停append的多个文件。比如LSM。
2.应用程序对文件的格式做很好的设计,确保增删改都只对一个指定的块文件进行修改,不会影响其他文件其他位置的数据。比如mysql的bufferpool。
4.什么情况下会丢数据
应用程序在读数据时,系统会判断内存中是否会缓存,如果缓存直接从内存中读。应用在写数据,系统会现将数据写入到内存中,由系统来决定什么时候进行刷盘。在mysql中,innodb_flush_log_at_trx_commit为0、1、2时mysql有不同的数据刷盘机制。innodb_flush_log_at_trx_commit为0时,什么都不做,等着定时任务调度去每隔1秒write和flush,这种效率最高,但是丢失数据的风险最大,mysql进程挂掉数据就有可能丢到;为1时事务提交后立即write和flush,能保证强一致,但IO效率很差;为2时事务提交后立即write,但是每秒flush一次,IO效率有所提升,mysql进程挂掉,但是系统没有挂掉的话,数据不会丢,大部分互联网应用都是这个值。
同理,kafka在broker返回成功以后,同样也会在极端情况下因为系统没有刷盘导致的丢数据。