java文件按行写入数据后并创建行索引及查询
背景
当有很多数据需要存储,这些数据只是想要简单的按行存储和查询,不需要进行其他条件搜索,此时就可以考虑不需把这些数据存储在数据库,而是直接写入文件,然后从文件中查询
但是正常情况下,如果仅仅只是按行写入文件,读取的时候如果不是从第一行开始读,java api是不支持直接定位到某行开始查询,比如如果想查出第1000行之后的数据,此时需要先读取前面999行的数据之后才能读取第1000行的数据,可想而知性能好不到哪去。
有些人可能会说用 RandomAccessFile 可以直接跳过一定偏移量定位到999行,可是正常情况下,我们并不能知道999行对应的偏移量,也就无法知道要跳过多少偏移量直接定位到999行。
本文主要是提供了解决上面问题的工具。
实现功能
1.可以按行写入数据,并对写入的每一行创建索引
2.基于索引快速定位到行所在位置,不需要一行行遍历
3.支持对写入数据进行分片。比如写入1W条数据,1000条数据一个文件4.支持分页查询
5.支持指定行号查询
6.目前仅支持一次性写入全部数据,不支持写一半后继续往后添加(后续考虑支持)
7.不支持从中间插入数据(后续不会支持,因为目前的实现机制从中间插入,插入行后面的所有行都得重新构造索引)
代码地址
g5zhu5896/file-storage · GitHub
介绍
核心类
FileDataWriter
用于往文件中写入数据并创建索引
使用:
String path= "/file/张三/"; Integer totalSize = 10000; Integer fileMaxSize=1000; String charset=CharsetUtil.UTF_8; Long indexFixedWidthFactor = 3l; //如果文件已存在写入会报错 if (!new File(path).exists()) { try (FileDataWriter fileDataWriter = FileDataWriter.builder().withFileMaxSize(fileMaxSize) .withIndexFixedWidthFactor(indexFixedWidthFactor ) .withCharset(charset).build(filePath)) { for (Long i = 1L; i <= totalSize; i++) { fileDataWriter.write(i + ""); } } catch (IOException e) { e.printStackTrace(); } }
相关配置
fileMaxSize:配置一个文件最大存储的行数 path:文件要存储的路径。可以自定义规则 indexFixedWidthFactor: 索引固定宽度因子,用于计算索引文件中索引的固定宽度。配的越大越浪费空间,配小了更容易触发调整固定宽度 charset:配置写文件的编码
FileDataPageReader
使用:
String path= "/file/张三/"; //如果文件不存在则无法读取 if (!new File(path).exists()) { FileDataPageReader fileDataPageReader = FileDataPageReader.builder().build(path); //分页查询 //从第二页开始查询 Integer page = 2; //一页查20条 Integer pageSize=20 List<String> strings = fileDataPageReader.readPage(page, pageSize); //将行内容转成Interger,此处也可以把json转成对象 List<Integer> strings = fileDataPageReader.readPage(page, pageSize,item->{ return new Integer(item); }); //指定行数查询 //从第5行开始查 Integer startIndex = 5; //共查100条 Integer size = 100; List<String> strings = fileDataPageReader.read(startIndex, size); //将行内容转成Interger,此处也可以把json转成对象 List<Integer> strings = fileDataPageReader.read(startIndex, size,item->{ return new Integer(item); }); //获取总行数 Integer totalSize=fileDataPageReader.getTotalSize(); }
文件介绍
(目前文件后缀只能写死,可以通过改 Constants 类调整生成后缀)
.dat文件
数据文件,按行写入。会有多个,文件如下图所示
.idx
行索引文件,会有多个,存储每一行对应的文件偏移量,通过该文件快速定位数据文件的行偏移量,如下图所示
cfg
配置文件,记录当前文件的一些配置信息,如下图所示
相关配置
fileMaxSize:配置每一个文件最大存储的行数 totalSize:总行数 charset:配置写文件的编码 columnIndexWidthMap:行索引动态固定宽度map,主要用于减少索引文件的大小。有序,{1:4,1852:8}表示1-1851行的间距是4,1852及之后的行的间距是8
相关依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.19</version>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.26</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
</dependency>
注意
本文代码仅基于 Test.main 中的 demo 进行测试验证过,所以依然可能存在 bug ,但个人感觉测试的 demo 已经挺多的了.
核心逻辑
定位数据行
主要通过 RandomAccessFile.seek 快速定位到行偏移量,然后通过索引文件获取具体的行偏移量。这没什么好说的,
从索引文件中获取数据行偏移量
索引文件需要存储数据行中的偏移量,但是索引文件中包含很多行,我们想知道数据行的偏移量在索引文件中的哪个位置,也无法直接定位。
所以为了快速从索引文件中定位到数据行的偏移量,采用了索引固定宽度的做法。具体如下
假设 indexFixedWidthFactor=3
step1: 写入第1行数据,假设占用行偏移量9,宽度为1,则当前索引固定宽度为curFixedWidth=1+indexFixedWidthFactor=4,写入索引文件第1行的偏移量为0000(记录的是写入前的偏移量,那才是行开始位置)(代码在 FileDataWriter.write)
step2: 写入第2行数据,假设占用10个行偏移量,加上第1行的偏移量后为19,写入第2行偏移量0009,以此类推,第3行为0019
step3: 直到写入103行,假设写入后的行偏移量的行偏移量为10009,由于10009宽度为5,大于 curFixedWidth ,于是重新计算当前索引固定宽度为 curFixedWidth=5+indexFixedWidthFactor=8 ,所以第104行的行偏移量是00010009。以此类推
step4:假设150行宽度依然小于8,然后写到151行,当前文件写满,需要写下一个文件,则会从头如 step1 一样计算索引固定宽度curFixedWidth=1+indexFixedWidthFactor=4。
step5:假设只写到179行,最后的 curFixedWidth=4 ,此时会将配置信息写入 cfg 文件,其中包括 columnIndexWidthMap={1:4, 104:8, 151:4} (代码在 FileDataWriter.close)
step6:,由于是每一行的行偏移量是固定宽度,读取的时候就可以基于 columnIndexWidthMap 和要读取的行号,计算出行数据文件的行偏移量在索引文件中的偏移量,直接读取出数据文件的行偏移量(代码在FileDataPageReader.computeSeek)(如假设要从120行开始读取,则行偏移量为4*(104-1)+8*(120-104),从此处往后读取8位及120行的数据文件行偏移量
原文地址:https://blog.csdn.net/g5zhu5896/article/details/145310317
免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!