파일을 구성하여 제자리에서 사용할 수 있도록합니다. 즉 이러한 방식으로 로딩하지 않아도된다. 가변 길이 레코드가 있으므로 각 레코드의 위치 배열을 구성한 다음 키를 순서대로 배치하여 데이터에 대한 2 진 검색을 수행 할 수 있습니다. (또는 사용자 정의 해시 테이블을 사용할 수 있습니다.) 그런 다음 데이터가 실제로 데이터 객체로 변환되는 대신 파일에 저장된다는 사실을 숨기는 메소드로이를 래핑 할 수 있습니다.
이 모든 작업을 수행하면 "로드"단계가 중복되고 너무 많은 개체를 만들 필요가 없습니다.
이것은 긴 예제이지만 가능하면 무엇인지 보여줍니다.
import vanilla.java.chronicle.Chronicle;
import vanilla.java.chronicle.Excerpt;
import vanilla.java.chronicle.impl.IndexedChronicle;
import vanilla.java.chronicle.tools.ChronicleTest;
import java.io.IOException;
import java.util.*;
public class Main {
static final String TMP = System.getProperty("java.io.tmpdir");
public static void main(String... args) throws IOException {
String baseName = TMP + "/test";
String[] keys = generateAndSave(baseName, 100 * 1000 * 1000);
long start = System.nanoTime();
SavedSortedMap map = new SavedSortedMap(baseName);
for (int i = 0; i < keys.length/100; i++) {
long l = map.lookup(keys[i]);
// System.out.println(keys[i] + ": " + l);
}
map.close();
long time = System.nanoTime() - start;
System.out.printf("Load of %,d records and lookup of %,d keys took %.3f seconds%n",
keys.length, keys.length/100, time/1e9);
}
static SortedMap<String, Long> generateMap(int keys) {
SortedMap<String, Long> ret = new TreeMap<>();
while (ret.size() < keys) {
long n = ret.size();
String key = Long.toString(n);
while (key.length() < 9)
key = '0' + key;
ret.put(key, n);
}
return ret;
}
static void saveData(SortedMap<String, Long> map, String baseName) throws IOException {
Chronicle chronicle = new IndexedChronicle(baseName);
Excerpt excerpt = chronicle.createExcerpt();
for (Map.Entry<String, Long> entry : map.entrySet()) {
excerpt.startExcerpt(2 + entry.getKey().length() + 8);
excerpt.writeUTF(entry.getKey());
excerpt.writeLong(entry.getValue());
excerpt.finish();
}
chronicle.close();
}
static class SavedSortedMap {
final Chronicle chronicle;
final Excerpt excerpt;
final String midKey;
final long size;
SavedSortedMap(String baseName) throws IOException {
chronicle = new IndexedChronicle(baseName);
excerpt = chronicle.createExcerpt();
size = chronicle.size();
excerpt.index(size/2);
midKey = excerpt.readUTF();
}
// find exact match or take the value after.
public long lookup(CharSequence key) {
if (compareTo(key, midKey) < 0)
return lookup0(0, size/2, key);
return lookup0(size/2, size, key);
}
private final StringBuilder tmp = new StringBuilder();
private long lookup0(long from, long to, CharSequence key) {
long mid = (from + to) >>> 1;
excerpt.index(mid);
tmp.setLength(0);
excerpt.readUTF(tmp);
if (to - from <= 1)
return excerpt.readLong();
int cmp = compareTo(key, tmp);
if (cmp < 0)
return lookup0(from, mid, key);
if (cmp > 0)
return lookup0(mid, to, key);
return excerpt.readLong();
}
public static int compareTo(CharSequence a, CharSequence b) {
int lim = Math.min(a.length(), b.length());
for (int k = 0; k < lim; k++) {
char c1 = a.charAt(k);
char c2 = b.charAt(k);
if (c1 != c2)
return c1 - c2;
}
return a.length() - b.length();
}
public void close() {
chronicle.close();
}
}
private static String[] generateAndSave(String baseName, int keyCount) throws IOException {
SortedMap<String, Long> map = generateMap(keyCount);
saveData(map, baseName);
ChronicleTest.deleteOnExit(baseName);
String[] keys = map.keySet().toArray(new String[map.size()]);
Collections.shuffle(Arrays.asList(keys));
return keys;
}
}
은 2GB의 원시 데이터를 생성하고 백만개의 조회를 수행합니다. 로딩과 룩업이 힙을 거의 사용하지 않는 방식으로 작성되었습니다. 이 (1) 대신에 (N LN) O로하지만 더 복잡한 구현 O 그대로 룩업 당 빠를 것이다 해시 테이블 룩업을 이용하여 (1 < < MB)
ls -l /tmp/test*
-rw-rw---- 1 peter peter 2013265920 Dec 11 13:23 /tmp/test.data
-rw-rw---- 1 peter peter 805306368 Dec 11 13:23 /tmp/test.index
/tmp/test created.
/tmp/test, size=100000000
Load of 100,000,000 records and lookup of 1,000,000 keys took 10.945 seconds
.
프로파일 링 결과는 무엇을 말합니까? 어디에서 병목 현상이 발생합니까? –
1.7G 키 값 쌍, 파일 대신 데이터베이스를 사용하지 않는 이유는 무엇입니까? – jlordo
그 양의 데이터로 무엇을하고 싶습니까? 나는 당신이 이것에 비효율적 인 접근법을 사용하고 있을지도 모른다는 강한 느낌을 가지고 있습니다. –