-
Notifications
You must be signed in to change notification settings - Fork 38
超大基数bitmap的存取
Chen Huajun edited this page Dec 18, 2022
·
3 revisions
如果我们需要将roaringbitmap列中存储的数据取回客户端,有几种方式。常规的方式包括
-
方法1:将bitmap中的每个数字作为一条记录返回
比如:
SELECT unnest(rb_to_array('{1,2,3}'::roaringbitmap));
或
SELECT rb_iterate('{1,2,3}'::roaringbitmap);
-
方法2:将bitmap转换成数组传回客户端
比如:
SELECT rb_to_array('{1,2,3}'::roaringbitmap);
-
方法3:将bitmap格式化成字符串传回客户端
比如:
SET roaringbitmap.output_format='array'; SELECT '{1,2,3}'::roaringbitmap::text;
但是,对于一个基数超大的bitmap,以上方法需要做类型转换。类型转换不仅耗费CPU,而且转换后的数据大小可能会远大于原始的roaringbitmap, 不仅增加了数据传输的开销,单个字段超过1GB还会产生内存分配错误。
ERROR: invalid memory alloc request size 1073741824
除了读取,写入一个基数超大的bitmap也存在类似问题。
读取原始的roaringbitmap二进制裸数据,然后在客户端进行解析。 Roaringbitmap有各种语言的类库实现,可以轻松解析roaringbitmap数据。写bitmap也类似,先在客户端生成好roaringbitmap数据再直接把二进制数据写到数据库。 以下是Java语言的实现:
采用这种方案的示例如下:
表定义
create table testtb(id int, bitmap roaringbitmap);
bitmap读取
String sql = "select bitmap::bytea from testtb where id = ?";
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setInt(1, 1);
ResultSet rs = stmt.executeQuery();
while(rs.next()){
RoaringBitmap rb = new RoaringBitmap();
DataInputStream is = new DataInputStream(rs.getBinaryStream(1));
rb.deserialize(is);
is.close();
}
rs.close();
stmt.close();
bitmap写入
String sql = "INSERT INTO testtb(id, bitmap) VALUES (?,?::bytea::roaringbitmap)";
PreparedStatement stmt = conn.prepareStatement(sql);
RoaringBitmap rb = RoaringBitmap.bitmapOf();
for(int i = 0; i < 10000000; i ++)
rb.add((int)(1+Math.random()*100000000));
rb.runOptimize(); // run存储格式优化
byte[] array = new byte[rb.serializedSizeInBytes()];
try {
rb.serialize(new java.io.DataOutputStream(new java.io.OutputStream() {
int c = 0;
public void flush() {
}
public void close() {
}
public void write(int b) {
array[c++] = (byte) b;
}
public void write(byte[] b) {
write(b, 0, b.length);
}
public void write(byte[] b, int off, int l) {
System.arraycopy(b, off, array, c, l);
c += l;
}
}));
} catch (IOException ioe) {
throw new RuntimeException("unexpected error while serializing to a byte array");
}
stmt.setInt(1, 1);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(array);
stmt.setBinaryStream(2, byteArrayInputStream, array.length);
stmt.executeUpdate();
stmt.close();
- 《苏宁大规模标签场景应用实践》(https://pan.baidu.com/s/1eRQsdAa)