大家有没有想过一个问题,RPC的实现分两部分,序列化与反序列化协议和传输协议,其中传输协议可以用TCP/UDP/HTTP等,序列化与反序列化协议可以用JSON/XML/ProtoBuf/hessian等等,那么能不能消除序列化与反序列化的损耗呢?有没有一个大一统的协议,跨语言跨平台,一个数据结构,大家拿来就处理,不用转成对象,这样能省不少资源,这就是apache arrow的目标之一,还有没有其他的好处?当然有了,如果数据都在一起,那么cpu的缓存利用率就很高了,性能提升不是一点半点。
- 添加依赖
<!-- https://mvnrepository.com/artifact/org.apache.arrow/arrow-vector -->
<dependency>
<groupId>org.apache.arrow</groupId>
<artifactId>arrow-vector</artifactId>
<version>1.0.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.arrow/arrow-memory-unsafe -->
<dependency>
<groupId>org.apache.arrow</groupId>
<artifactId>arrow-memory-unsafe</artifactId>
<version>1.0.1</version>
</dependency>
- 简单测试
@Test
public void testIntVector() {
RootAllocator allocator = new RootAllocator(Long.MAX_VALUE);
IntVector vector = new IntVector("int vector", allocator);
vector.allocateNew(10);
vector.set(/*index*/5, /*value*/25);
vector.set(/*index*/0, /*value*/125);
vector.setValueCount(10);
System.out.println(vector.get(5));
System.out.println(vector.get(0));
vector.close();
}
- 复杂场景,网络传输,可以看到写入流并转为byte数组,同样从byte数组又转回vector
需要注意的是,这个不同于序列化反序列化,这个底层是byte数组,类似于列式存储,可以很大程度利用cpu缓存特性,如果是java对象,那么可能是散落在堆内存当中,处理数据就会慢下来。
@Test
public void testVectorSchemaRoot() throws IOException {
RootAllocator allocator = new RootAllocator(Long.MAX_VALUE);
BitVector bitVector = new BitVector("boolean", allocator);
VarCharVector varCharVector = new VarCharVector("varchar", allocator);
bitVector.allocateNew();
varCharVector.allocateNew();
for (int i = 0; i < 10; i++) {
bitVector.setSafe(i, i % 2 == 0 ? 0 : 1);
varCharVector.setSafe(i, ("test" + i).getBytes(StandardCharsets.UTF_8));
}
bitVector.setValueCount(10);
varCharVector.setValueCount(10);
List<Field> fields = Arrays.asList(bitVector.getField(), varCharVector.getField());
List<FieldVector> vectors = Arrays.asList(bitVector, varCharVector);
VectorSchemaRoot root = new VectorSchemaRoot(fields, vectors);
ByteArrayOutputStream out = new ByteArrayOutputStream();
ArrowStreamWriter writer = new ArrowStreamWriter(root, /*DictionaryProvider=*/null, Channels.newChannel(out));
writer.start();
writer.writeBatch();
/*for (int i = 0; i < 4; i++) {
// populate VectorSchemaRoot data and write the second batch
BitVector childVector1 = (BitVector)root.getVector(0);
VarCharVector childVector2 = (VarCharVector)root.getVector(1);
//为什么要reset why
//childVector1.reset();
//childVector2.reset();
writer.writeBatch();
}*/
writer.end();
try (ArrowStreamReader reader = new ArrowStreamReader(new ByteArrayInputStream(out.toByteArray()), allocator)) {
Schema schema = reader.getVectorSchemaRoot().getSchema();
System.out.println(schema);
while(reader.loadNextBatch()){
System.out.println("-------------------------batch-------------");
VectorSchemaRoot readBatch = reader.getVectorSchemaRoot();
//拿到VectorSchemaRoot以后,就能得到值
BitVector childVector1 = (BitVector) readBatch.getVector("boolean");
VarCharVector childVector2 = (VarCharVector) readBatch.getVector("varchar");
//处理
}
}
}
-
特性解析
- 省去大部分序列化与反序列化的损耗
- cpu缓存友好
- 压缩,列式数据存储可以实现很高的压缩
- 对程序员不友好,不过我们可以写工具来实现转换过程
-
可应用场景
- 数据分析领域
- 某些rpc热点代码,场景单一切并发很高,可以用此方式优化,用来改善GC和处理速度。