如何序列化与反序列化Lucene ByteBuffersDirectory(无需磁盘)
内存中Lucene索引的序列化与反序列化方案
问题背景
使用堆上的ByteBuffersDirectory存储短期内存Lucene索引,需要将整个目录序列化为byte[],再反序列化为ByteBuffersDirectory,全程无需操作磁盘。已实现压缩流生成方法,但无法完成反序列化,同时对ByteBuffersDirectory的OUTPUT_AS_ONE_BUFFER和OUTPUT_AS_BYTE_ARRAY常量用法不明确。
现有序列化(压缩)代码:
private static ByteArrayOutputStream toZip(ByteBuffersDirectory d) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ZipOutputStream zos = new ZipOutputStream(baos)) { for (String s : d.listAll()) { ZipEntry entry = new ZipEntry(s); IndexInput indexInput = d.openInput(s, IOContext.READONCE); entry.setSize(indexInput.length()); zos.putNextEntry(entry); for (long i = 0; i < indexInput.length(); i++) { zos.write(indexInput.readByte()); } zos.closeEntry(); } return baos; } }
解决方案
1. 利用内置常量实现极简序列化/反序列化
OUTPUT_AS_ONE_BUFFER和OUTPUT_AS_BYTE_ARRAY是ByteBuffersDirectory的内置AllocationStrategy,用于控制索引文件在内存中的存储形式,可直接实现无压缩的序列化:
序列化(直接转为单个byte数组)
创建ByteBuffersDirectory时指定OUTPUT_AS_BYTE_ARRAY策略,后续可通过getDirectoryBytes()直接获取序列化后的字节数组(该API适用于Lucene 8.0+):
// 初始化目录时指定内存分配策略 ByteBuffersDirectory dir = new ByteBuffersDirectory(ByteBuffersDirectory.OUTPUT_AS_BYTE_ARRAY); // 执行索引写入操作... // 直接序列化 byte[] serializedBytes = dir.getDirectoryBytes();
反序列化
用序列化得到的字节数组直接构建新的ByteBuffersDirectory:
// 从字节数组恢复内存索引目录 ByteBuffersDirectory restoredDir = new ByteBuffersDirectory(serializedBytes, IOContext.READONCE);
2. 修复压缩/反序列化流程
若需保留压缩逻辑,补充反序列化方法并优化原序列化代码:
反序列化实现
private static ByteBuffersDirectory fromZip(byte[] zipBytes) throws IOException { ByteBuffersDirectory dir = new ByteBuffersDirectory(); try (ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(zipBytes))) { ZipEntry entry; while ((entry = zis.getNextEntry()) != null) { // 创建索引输出流写入内存目录 try (IndexOutput output = dir.createOutput(entry.getName(), IOContext.DEFAULT)) { byte[] buffer = new byte[4096]; int len; // 批量读写提升效率 while ((len = zis.read(buffer)) != -1) { output.writeBytes(buffer, len); } } zis.closeEntry(); } // 提交目录变更 dir.commit(); return dir; } }
优化序列化代码(替换逐字节读写)
原代码逐字节写入效率极低,改为批量读写:
private static byte[] toZip(ByteBuffersDirectory d) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ZipOutputStream zos = new ZipOutputStream(baos)) { byte[] buffer = new byte[4096]; for (String s : d.listAll()) { ZipEntry entry = new ZipEntry(s); try (IndexInput indexInput = d.openInput(s, IOContext.READONCE)) { entry.setSize(indexInput.length()); zos.putNextEntry(entry); int len; while ((len = indexInput.readBytes(buffer, 0, buffer.length)) != -1) { zos.write(buffer, 0, len); } } zos.closeEntry(); } return baos.toByteArray(); } }
注意事项
- 确认Lucene版本兼容性:
getDirectoryBytes()是Lucene 8.0及以上版本的API; - 所有IO操作需确保资源正确关闭,避免内存泄漏;
- 压缩方案可减少序列化后的字节体积,但会增加CPU开销,需根据索引大小权衡选择。
内容的提问来源于stack exchange,提问作者Stephen Flavin
相关产品推荐
相关产品推荐

