提交 1f33e90e authored 作者: Thomas Mueller's avatar Thomas Mueller

New LOB storage.

上级 63a2efdb
...@@ -7,7 +7,6 @@ ...@@ -7,7 +7,6 @@
package org.h2.test.utils; package org.h2.test.utils;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.sql.Connection; import java.sql.Connection;
...@@ -17,13 +16,14 @@ import java.sql.ResultSet; ...@@ -17,13 +16,14 @@ import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random; import java.util.Random;
import org.h2.constant.ErrorCode; import org.h2.constant.ErrorCode;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.tools.DeleteDbFiles; import org.h2.tools.DeleteDbFiles;
import org.h2.util.New;
import org.h2.util.Utils;
import org.h2.util.IOUtils; import org.h2.util.IOUtils;
import org.h2.util.New;
import org.h2.util.Profiler;
/** /**
* Implementation and tests of a LOB storage mechanism that splits LOBs in * Implementation and tests of a LOB storage mechanism that splits LOBs in
...@@ -32,9 +32,12 @@ import org.h2.util.IOUtils; ...@@ -32,9 +32,12 @@ import org.h2.util.IOUtils;
public class TestLob { public class TestLob {
private static final int BLOCK_LENGTH = 20000; private static final int BLOCK_LENGTH = 20000;
private static final boolean HASH = true;
private static final long UNIQUE = 0xffff;
private Connection conn; private Connection conn;
private PreparedStatement prepInsertBlock, prepSelectBlock, prepDeleteBlock; private PreparedStatement prepInsertLob, prepInsertMap, prepInsertBlock;
private PreparedStatement prepInsertLob, prepSelectLob, prepDeleteLob; private PreparedStatement prepSelectMapBlock, prepSelectBlock;
private PreparedStatement prepDeleteBlockUnused, prepDeleteMap, prepDeleteLob;
private long nextLob; private long nextLob;
private long nextBlock; private long nextBlock;
...@@ -51,18 +54,6 @@ public class TestLob { ...@@ -51,18 +54,6 @@ public class TestLob {
this.length = length; this.length = length;
} }
LobId(byte[] key) {
id = Utils.readLong(key, 0);
length = Utils.readLong(key, 8);
}
byte[] getKey() {
byte[] key = new byte[16];
Utils.writeLong(key, 0, id);
Utils.writeLong(key, 0, length);
return key;
}
long getId() { long getId() {
return id; return id;
} }
...@@ -80,14 +71,15 @@ public class TestLob { ...@@ -80,14 +71,15 @@ public class TestLob {
private byte[] buffer; private byte[] buffer;
private int pos; private int pos;
private PreparedStatement prepSelectBlock; private PreparedStatement prepSelectMapBlock;
private long remaining; private long remaining;
private long next; private long lob;
private int seq;
LobInputStream(PreparedStatement prepSelectBlock, long first, long length) { LobInputStream(PreparedStatement prepSelectMapBlock, long lob, long length) {
this.next = first; this.lob = lob;
this.remaining = length; this.remaining = length;
this.prepSelectBlock = prepSelectBlock; this.prepSelectMapBlock = prepSelectMapBlock;
} }
public int read() throws IOException { public int read() throws IOException {
...@@ -136,40 +128,20 @@ public class TestLob { ...@@ -136,40 +128,20 @@ public class TestLob {
return; return;
} }
try { try {
// select id, next, data from block where id = ? // select data from map m inner join block b
prepSelectBlock.setLong(1, next); // on m.block = b.id where m.lob = ? and m.seq = ?
ResultSet rs = prepSelectBlock.executeQuery(); prepSelectMapBlock.setLong(1, lob);
prepSelectMapBlock.setInt(2, seq);
ResultSet rs = prepSelectMapBlock.executeQuery();
if (!rs.next()) { if (!rs.next()) {
SQLException e = DbException.get(ErrorCode.IO_EXCEPTION_1, "block: "+ next).getSQLException(); throw DbException.get(ErrorCode.IO_EXCEPTION_1, "lob: "+ lob + " seq: " + seq).getSQLException();
IOException io = new EOFException("Unexpected end of stream");
io.initCause(e);
throw e;
} }
next = rs.getLong(2); seq++;
buffer = rs.getBytes(3); buffer = rs.getBytes(1);
pos = 0; pos = 0;
} catch (SQLException e) { } catch (SQLException e) {
throw DbException.convertToIOException(e); throw DbException.convertToIOException(e);
} }
// compress / decompress
// int len = readInt();
// if (decompress == null) {
// // EOF
// this.bufferLength = 0;
// } else if (len < 0) {
// len = -len;
// buffer = ensureSize(buffer, len);
// readFully(buffer, len);
// this.bufferLength = len;
// } else {
// inBuffer = ensureSize(inBuffer, len);
// int size = readInt();
// readFully(inBuffer, len);
// buffer = ensureSize(buffer, size);
// decompress.expand(inBuffer, 0, len, buffer, 0, size);
// this.bufferLength = size;
// }
// pos = 0;
} }
...@@ -199,16 +171,16 @@ public class TestLob { ...@@ -199,16 +171,16 @@ public class TestLob {
// block: 1817 // block: 1817
// regular: 4552 // regular: 4552
int len = 32 * 1024; // int len = 32 * 1024;
// block: 1770 // block: 1712 / 636
// regular: 2255 // regular: 2255
// int len = 64 * 1024; int len = 64 * 1024;
// block: 1776 // block: 1540 / 590
// regular: 1385 // regular: 1385
// 1024 * 1024 // int len = 1024 * 1024;
// block: 1682 // block: 1682 / 560
// regular: 455 // regular: 455
// int len = 128 * 1024; // int len = 128 * 1024;
...@@ -237,15 +209,16 @@ public class TestLob { ...@@ -237,15 +209,16 @@ public class TestLob {
Statement stat = c.createStatement(); Statement stat = c.createStatement();
stat.execute("create table test(id int primary key, data blob)"); stat.execute("create table test(id int primary key, data blob)");
PreparedStatement prep = conn.prepareStatement("insert into test(id, data) values(?, ?)"); PreparedStatement prep = conn.prepareStatement("insert into test(id, data) values(?, ?)");
// Profiler prof = new Profiler(); Profiler prof = new Profiler();
// prof.setInterval(1); prof.interval = 1;
// prof.startCollecting(); prof.startCollecting();
int x = 0, y = 0; int x = 0, y = 0;
ArrayList<LobId> list = New.arrayList(); ArrayList<LobId> list = New.arrayList();
for (int j = 0; j < repeat; j++) { for (int j = 0; j < repeat; j++) {
boolean regular = (j & 1) == 1; boolean regular = (j & 1) == 1;
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
// random.nextBytes(buff);
if (regular) { if (regular) {
prep.setInt(1, x++); prep.setInt(1, x++);
prep.setBinaryStream(2, new ByteArrayInputStream(buff), len); prep.setBinaryStream(2, new ByteArrayInputStream(buff), len);
...@@ -267,27 +240,29 @@ public class TestLob { ...@@ -267,27 +240,29 @@ public class TestLob {
boolean regular = (j & 1) == 1; boolean regular = (j & 1) == 1;
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
for (int i = 0; i < count * 10; i++) { for (int i = 0; i < count * 10; i++) {
InputStream in2; InputStream in2 = null;
if (regular) { if (regular) {
prep.setInt(1, x++ % repeat); // prep.setInt(1, x++ % repeat);
ResultSet rs = prep.executeQuery(); // ResultSet rs = prep.executeQuery();
rs.next(); // rs.next();
in2 = rs.getBinaryStream(1); // in2 = rs.getBinaryStream(1);
} else { } else {
in2 = getInputStream(list.get(y++ % repeat)); in2 = getInputStream(list.get(y++ % repeat));
} }
while (true) { if (in2 != null) {
int len2 = in2.read(buff2); while (true) {
if (len2 < 0) { int len2 = in2.read(buff2);
break; if (len2 < 0) {
break;
}
} }
} }
} }
System.out.println((regular ? "regular: " : "block: ") + (System.currentTimeMillis() - time)); System.out.println((regular ? "regular: " : "block: ") + (System.currentTimeMillis() - time));
} }
// prof.stopCollecting(); prof.stopCollecting();
// System.out.println(prof.getTop(5)); System.out.println(prof.getTop(5));
c.close(); c.close();
} }
...@@ -296,66 +271,110 @@ public class TestLob { ...@@ -296,66 +271,110 @@ public class TestLob {
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
// stat.execute("set undo_log 0"); // stat.execute("set undo_log 0");
// stat.execute("set redo_log_binary 0"); // stat.execute("set redo_log_binary 0");
// TODO support incremental garbage collection stat.execute("create table if not exists lob(id bigint primary key, length bigint, table int)");
stat.execute("create table if not exists block(id bigint primary key, next bigint, data binary)"); stat.execute("create table if not exists map(lob bigint, seq int, block bigint, primary key(lob, seq))");
stat.execute("create table if not exists lob(id bigint primary key, length bigint, block bigint, table int)"); stat.execute("create index idx_map_block on map(block, lob)");
stat.execute("create table if not exists block(id bigint primary key, data binary)");
ResultSet rs; ResultSet rs;
rs = stat.executeQuery("select max(id) from block"); rs = stat.executeQuery("select max(id) from block");
rs.next(); rs.next();
nextBlock = rs.getLong(1) + 1; nextBlock = rs.getLong(1) + 1;
if (HASH) {
nextBlock = Math.max(UNIQUE + 1, nextLob);
}
rs = stat.executeQuery("select max(id) from lob"); rs = stat.executeQuery("select max(id) from lob");
rs.next(); rs.next();
nextLob = rs.getLong(1) + 1; nextLob = rs.getLong(1) + 1;
prepInsertBlock = conn.prepareStatement("insert into block(id, next, data) values(?, ?, ?)"); prepInsertLob = conn.prepareStatement("insert into lob(id, length, table) values(?, ?, ?)");
prepDeleteBlock = conn.prepareStatement("delete from block where id = ?"); prepInsertMap = conn.prepareStatement("insert into map(lob, seq, block) values(?, ?, ?)");
prepInsertBlock = conn.prepareStatement("insert into block(id, data) values(?, ?)");
prepSelectMapBlock = conn.prepareStatement("select data from map m inner join block b on m.block = b.id where m.lob = ? and m.seq = ?");
prepSelectBlock = conn.prepareStatement("select data from block where id = ?");
prepDeleteLob = conn.prepareStatement("delete from lob where id = ?"); prepDeleteLob = conn.prepareStatement("delete from lob where id = ?");
prepInsertLob = conn.prepareStatement("insert into lob(id, length, block, table) values(?, ?, ?, ?)"); prepDeleteMap = conn.prepareStatement("delete from map where lob = ?");
prepSelectBlock = conn.prepareStatement("select id, next, data from block where id = ?"); prepDeleteBlockUnused = conn.prepareStatement("delete from block where id in(select block from map where lob = ?) and not exists(select 1 from map where block = id and lob <> ?)");
} }
private void deleteLob(int lobId) { private void deleteLob(long lob) throws SQLException {
// // delete from map where lob = ?
prepDeleteMap.setLong(1, lob);
prepDeleteMap.execute();
// delete from block where id in(select block from map where lob = ?)
// and not exists(select 1 from map where block = id and lob <> ?)
prepDeleteBlockUnused.setLong(1, lob);
prepDeleteBlockUnused.setLong(2, lob);
prepDeleteBlockUnused.execute();
// delete from lob where id = ?
prepDeleteLob.setLong(1, lob);
prepDeleteLob.execute();
} }
private LobId addLob(InputStream in, long maxLength, int table) throws SQLException { private LobId addLob(InputStream in, long maxLength, int table) throws SQLException {
byte[][] buff = new byte[2][BLOCK_LENGTH]; byte[] buff = new byte[BLOCK_LENGTH];
if (maxLength < 0) { if (maxLength < 0) {
maxLength = Long.MAX_VALUE; maxLength = Long.MAX_VALUE;
} }
long length = 0; long length = 0;
int blockId = 0;
long firstBlock = nextBlock;
long lob = nextLob++; long lob = nextLob++;
try { try {
for (; maxLength > 0; blockId++) { for (int seq = 0; maxLength > 0; seq++) {
int len = IOUtils.readFully(in, buff[blockId & 1], 0, BLOCK_LENGTH); int len = IOUtils.readFully(in, buff, 0, BLOCK_LENGTH);
if (len <= 0) { if (len <= 0) {
break; break;
} }
length += len; length += len;
maxLength -= len; maxLength -= len;
if (blockId > 0) {
// insert into block(id, next, data) values(?, ?, ?) byte[] b;
prepInsertBlock.setLong(1, nextBlock++); if (len != buff.length) {
prepInsertBlock.setLong(2, nextBlock); b = new byte[len];
prepInsertBlock.setBytes(3, buff[(blockId - 1) & 1]); System.arraycopy(buff, 0, b, 0, len);
prepInsertBlock.execute(); } else {
b = buff;
} }
// insert into block(id, data) values(?, ?)
long block;
boolean blockExists = false;
if (HASH) {
block = Arrays.hashCode(b) & UNIQUE;
prepSelectBlock.setLong(1, block);
ResultSet rs = prepSelectBlock.executeQuery();
if (rs.next()) {
byte[] compare = rs.getBytes(1);
if (Arrays.equals(b, compare)) {
blockExists = true;
} else {
block = nextBlock++;
}
}
} else {
block = nextBlock++;
}
if (!blockExists) {
// insert into block(id, data) values(?, ?)
prepInsertBlock.setLong(1, block);
prepInsertBlock.setBytes(2, b);
try {
prepInsertBlock.execute();
} catch (SQLException e) {
int test;
e.printStackTrace();
}
}
// insert into map(lob, seq, block) values(?, ?, ?)
prepInsertMap.setLong(1, lob);
prepInsertMap.setInt(2, seq);
prepInsertMap.setLong(3, block);
prepInsertMap.execute();
} }
// insert into block(id, next, data) values(?, ?, ?) // insert into lob(id, length, table) values(?, ?, ?)
prepInsertBlock.setLong(1, nextBlock++);
prepInsertBlock.setLong(2, firstBlock);
prepInsertBlock.setBytes(3, buff[(blockId - 1) & 1]);
prepInsertBlock.execute();
// insert into lob(id, length, block, table) values(?, ?, ?, ?)
prepInsertLob.setLong(1, lob); prepInsertLob.setLong(1, lob);
prepInsertLob.setLong(2, length); prepInsertLob.setLong(2, length);
prepInsertLob.setLong(3, firstBlock); prepInsertLob.setInt(3, table);
prepInsertLob.setInt(4, table);
prepInsertLob.execute(); prepInsertLob.execute();
return new LobId(lob, length); return new LobId(lob, length);
} catch (IOException e) { } catch (IOException e) {
deleteBlocks(firstBlock, nextBlock - 1); deleteLob(lob);
throw DbException.convertIOException(e, "adding blob"); throw DbException.convertIOException(e, "adding blob");
} }
} }
...@@ -363,15 +382,7 @@ public class TestLob { ...@@ -363,15 +382,7 @@ public class TestLob {
private InputStream getInputStream(LobId lobId) { private InputStream getInputStream(LobId lobId) {
long id = lobId.getId(); long id = lobId.getId();
long length = lobId.getLength(); long length = lobId.getLength();
return new LobInputStream(prepSelectBlock, id, length); return new LobInputStream(prepSelectMapBlock, id, length);
}
private void deleteBlocks(long first, long last) throws SQLException {
for (long id = first; id <= last; id++) {
// delete from block where id = ?
prepDeleteBlock.setLong(1, id);
prepDeleteBlock.execute();
}
} }
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论