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

3 篇文章 1 订阅

【背景】:

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

改善方法:做磁盘碎片整理,合并碎片文件,但随后还会再产生碎片造成磁盘读写性能下降,而且也解决不了小文件的随机存取的问题,这只是治标。更好的解决办法:更换电子硬盘(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));

    }
  • 12
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

EmineWang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值