顺序读写和随机读写区别和实现

【背景】:

随机和顺序读写,是存储器的两种输入输出方式。存储的数据在磁盘中占据空间,对于一个新磁盘,操作系统会将数据文件依次写入磁盘,当有些数据被删除时,就会空出该数据原来占有的存储空间,时间长了,不断的写入、删除数据,就会产生很多零零散散的存储空间,就会造成一个较大的数据文件放在许多不连续的存贮空间上,读写些这部分数据时,就是随机读写,磁头要不断的调整磁道的位置,以在不同位置上的读写数据,相对于连续空间上的顺序读写,要耗时很多。在开机时、启动大型程序时,电脑要读取大量小文件,而这些文件也不是连续存放的,也属于随机读取的范围。

改善方法:做磁盘碎片整理,合并碎片文件,但随后还会再产生碎片造成磁盘读写性能下降,而且也解决不了小文件的随机存取的问题,这只是治标。更好的解决办法:更换电子硬盘(SSD),电子盘由于免除了机械硬盘的磁头运动,对于随机数据的读写极大的提高。

举个例子,SSD的随机读取延迟只有零点几毫秒,而7200RPM的随机读取延迟有7毫秒左右,5400RPM硬盘更是高达9毫秒之多,体现在性能上就是开关机速度。
我们常用中间件kafka就是采用的顺序读写,避免了磁盘寻址的过程。

顺序IO和随机IO

  • 连续 / 随机 I/O

连续 I/O :指的是本次 I/O 给出的初始扇区地址和上一次 I/O 的结束扇区地址是完全连续或者相隔不多的。反之,如果相差很大,则算作一次随机 I/O。

而发生随机I/O可能是因为磁盘碎片导致磁盘空间不连续,或者当前block空间小于文件大小导致的。

连续 I/O 比随机 I/O 效率高的原因是:在做连续 I/O 的时候,磁头几乎不用换道,或者换道的时间很短;而对于随机 I/O,如果这个 I/O 很多的话,会导致磁头不停地换道,造成效率的极大降低。
对于磁盘的读写分为两种模式,顺序IO和随机IO。 随机IO存在一个寻址的过程,所以效率比较低。而顺序IO,相当于有一个物理索引,在读取的时候不需要寻找地址,效率很高。

《顺序读写和随机读写区别和实现》

  • Java中的随机读写
    在Java中读写文件的方式有很多种,先总结以下3种方法:

比如:

public static void fileWrite(String filePath, String content) { 
     FileOutputStream outputStream = null;
     try { 
         File file = new File(filePath);
         boolean isCreate = file.createNewFile();//创建文件
         if (isCreate) { 
             outputStream = new FileOutputStream(file);//形参里面可追加true参数,表示在原有文件末尾追加信息
             outputStream.write(content.getBytes());
         }else { 
             outputStream = new FileOutputStream(file,true);//表示在原有文件末尾追加信息
             outputStream.write(content.getBytes());
         }
     } catch (Exception e) { 
         e.printStackTrace();
     } finally { 
         try { 
             outputStream.close();
         } catch (IOException e) { 
             e.printStackTrace();
         }
     }
 }
	
 public static void fileRead(String filePath) { 
     File file = new File(filePath);
     if (file.exists()) { 
         try { 
             //创建FileInputStream对象,读取文件内容
             FileInputStream fis = new FileInputStream(file);
             byte[] bys = new byte[1024];
             while (fis.read(bys, 0, bys.length) != -1) { 
                 //将字节数组转换为字符串
                 System.out.print(new String(bys, StandardCharsets.UTF_8));
             }
         } catch (IOException ex) { 
             ex.printStackTrace();
         }
			
     }
 } 

Java中的顺序读写

上面的对文件的读写都是随机读写,如果用来写比较小的日志文件还能满足要求,如果用来操作一个文件的读写,那可能带来很大的性能消耗。

顺序IO的读写在中间件使用的很频繁,尤其是在队列中。几乎所有的队列(kafka,qmq等使用文件存储消息)都采用了顺序IO读写。

与随机读写不同的是,顺序读写是优先分配一块文件空间,然后后续内容追加到对应空间内。
在使用顺序IO进行文件读写时候,需要知道上次写入的地方,所以需要维护一个索引或者轮询获得一个没有写入位置。


    /** * * @param filePath * @param content * @param index 从指定位置开始写入 * @return 返回当前文件末尾的位置,便于下次继续写入 * * MappedByteBuffer,可以让文件直接在内存(堆外内存)修改,操作系统不需要拷贝一次。而如何同步到文件由NIO来完成 * * * 参数1: FileChannel.MapMode.READ_WRITE 使用的读写模式 * * 参数2: 0 : 可以直接修改的起始位置 * * 参数3: 5: 是映射到内存的大小(不是索引位置) ,即将 1.txt 的多少个字节映射到内存 * * 可以直接修改的范围就是 0-5 * * 实际类型 DirectByteBuffer * * MappedByteBuffer mappedByteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, 5); */
    public static long fileWrite(String filePath, String content, int index) { 
        File file = new File(filePath);
        RandomAccessFile randomAccessTargetFile;
        MappedByteBuffer map;
        try { 
            randomAccessTargetFile = new RandomAccessFile(file, "rw");
            FileChannel targetFileChannel = randomAccessTargetFile.getChannel();
            map = targetFileChannel.map(FileChannel.MapMode.READ_WRITE, 0, (long)  1024 * 1024); // 预先分配空间
            map.position(index);
            map.put(content.getBytes());
            return map.position();
        } catch (IOException e) { 
            e.printStackTrace();
        } finally { 
        }
        return 0L;
    }


    public static String fileRead(String filePath, long index) { 
        File file = new File(filePath);
        RandomAccessFile randomAccessTargetFile;
        MappedByteBuffer map;
        try { 
            randomAccessTargetFile = new RandomAccessFile(file, "rw");
            FileChannel targetFileChannel = randomAccessTargetFile.getChannel();
            map = targetFileChannel.map(FileChannel.MapMode.READ_WRITE, 0, index);
            byte[] byteArr = new byte[10 * 1024];
            map.get(byteArr, 0, (int) index);
            return new String(byteArr);
        } catch (IOException e) { 
            e.printStackTrace();
        } finally { 
        }
        return "";
    }

    public static void main(String[] args) { 

        System.out.println(ShunXuFile.fileWrite("D:/FileShunxuTest2","1111111111111111111",0));

    }
    原文作者:EmineWang
    原文地址: https://blog.csdn.net/a718515028/article/details/121091699
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞