提交 d00707ae authored 作者: Thomas Mueller's avatar Thomas Mueller

Cache: support for a second level soft-references cache.

上级 18655e85
......@@ -145,6 +145,14 @@ public class SysProperties {
*/
public static final int CACHE_SIZE_INDEX_DEFAULT = CACHE_SIZE_DEFAULT >> CACHE_SIZE_INDEX_SHIFT;
/**
* System property <code>h2.cacheTypeDefault</code> (default: LRU).<br />
* How many time the cache size value is divided by two to get the index
* cache size. The index cache size is calculated like this: cacheSize >>
* cacheSizeIndexShift.
*/
public static final String CACHE_TYPE_DEFAULT = getStringSetting("h2.cacheTypeDefault", "LRU");
/**
* System property <code>h2.check</code> (default: true).<br />
* Assertions in the database engine.
......
......@@ -58,7 +58,6 @@ import org.h2.tools.DeleteDbFiles;
import org.h2.tools.Server;
import org.h2.util.BitField;
import org.h2.util.ByteUtils;
import org.h2.util.CacheLRU;
import org.h2.util.ClassUtils;
import org.h2.util.FileUtils;
import org.h2.util.IntHashMap;
......@@ -212,7 +211,7 @@ public class Database implements DataHandler {
int traceLevelFile = ci.getIntProperty(SetTypes.TRACE_LEVEL_FILE, TraceSystem.DEFAULT_TRACE_LEVEL_FILE);
int traceLevelSystemOut = ci.getIntProperty(SetTypes.TRACE_LEVEL_SYSTEM_OUT,
TraceSystem.DEFAULT_TRACE_LEVEL_SYSTEM_OUT);
this.cacheType = StringUtils.toUpperEnglish(ci.removeProperty("CACHE_TYPE", CacheLRU.TYPE_NAME));
this.cacheType = StringUtils.toUpperEnglish(ci.removeProperty("CACHE_TYPE", SysProperties.CACHE_TYPE_DEFAULT));
openDatabase(traceLevelFile, traceLevelSystemOut, closeAtVmShutdown);
}
......
......@@ -29,7 +29,6 @@ import org.h2.message.Trace;
import org.h2.util.BitField;
import org.h2.util.ByteUtils;
import org.h2.util.Cache;
import org.h2.util.Cache2Q;
import org.h2.util.CacheLRU;
import org.h2.util.CacheObject;
import org.h2.util.CacheWriter;
......@@ -123,11 +122,8 @@ public class DiskFile implements CacheWriter {
this.dataFile = dataFile;
this.logChanges = logChanges;
String cacheType = database.getCacheType();
if (Cache2Q.TYPE_NAME.equals(cacheType)) {
this.cache = new Cache2Q(this, cacheSize);
} else {
this.cache = new CacheLRU(this, cacheSize);
}
this.cache = CacheLRU.getCache(this, cacheType, cacheSize);
rowBuff = DataPage.create(database, BLOCK_SIZE);
// TODO: the overhead is larger in the log file, so this value is too high :-(
recordOverhead = 4 * DataPage.LENGTH_INT + 1 + DataPage.LENGTH_FILLER;
......
......@@ -11,6 +11,7 @@ import java.io.OutputStream;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.WeakHashMap;
import org.h2.constant.ErrorCode;
import org.h2.engine.Database;
import org.h2.engine.Session;
......@@ -30,13 +31,13 @@ import org.h2.table.IndexColumn;
import org.h2.table.Table;
import org.h2.table.TableData;
import org.h2.util.Cache;
import org.h2.util.Cache2Q;
import org.h2.util.CacheLRU;
import org.h2.util.CacheObject;
import org.h2.util.CacheWriter;
import org.h2.util.FileUtils;
import org.h2.util.ObjectArray;
import org.h2.util.ObjectUtils;
import org.h2.util.SoftHashMap;
import org.h2.util.StringUtils;
import org.h2.value.Value;
import org.h2.value.ValueInt;
......@@ -167,7 +168,7 @@ public class PageStore implements CacheWriter {
* @param accessMode the access mode
* @param cacheSizeDefault the default cache size
*/
public PageStore(Database database, String fileName, String accessMode, int cacheSizeDefault) {
public PageStore(Database database, String fileName, String accessMode, int cacheSizeDefault) throws SQLException {
this.fileName = fileName;
this.accessMode = accessMode;
this.database = database;
......@@ -176,11 +177,7 @@ public class PageStore implements CacheWriter {
trace.setLevel(TraceSystem.DEBUG);
this.cacheSize = cacheSizeDefault;
String cacheType = database.getCacheType();
if (Cache2Q.TYPE_NAME.equals(cacheType)) {
this.cache = new Cache2Q(this, cacheSize);
} else {
this.cache = new CacheLRU(this, cacheSize);
}
this.cache = CacheLRU.getCache(this, cacheType, cacheSize);
}
/**
......
......@@ -7,6 +7,8 @@
package org.h2.util;
import java.sql.SQLException;
import java.util.Map;
import java.util.WeakHashMap;
import org.h2.constant.SysProperties;
import org.h2.engine.Constants;
......@@ -17,18 +19,18 @@ import org.h2.message.Message;
*/
public class CacheLRU implements Cache {
public static final String TYPE_NAME = "LRU";
static final String TYPE_NAME = "LRU";
private final CacheWriter writer;
private int len;
private final CacheObject head = new CacheHead();
private final int len;
private final int mask;
private int maxSize;
private CacheObject[] values;
private int mask;
private int recordCount;
private int sizeMemory;
private CacheObject head = new CacheHead();
public CacheLRU(CacheWriter writer, int maxKb) {
private CacheLRU(CacheWriter writer, int maxKb) {
this.maxSize = maxKb * 1024 / 4;
this.writer = writer;
this.len = MathUtils.nextPowerOf2(maxSize / 64);
......@@ -37,6 +39,40 @@ public class CacheLRU implements Cache {
clear();
}
/**
* Create a cache of the given type and size.
*
* @param writer the cache writer
* @param cacheType the cache type
* @param cacheSize the size
* @return the cache object
*/
public static Cache getCache(CacheWriter writer, String cacheType, int cacheSize) throws SQLException {
Map secondLevel = null;
String prefix = null;
if (cacheType.startsWith("SOFT_")) {
secondLevel = new SoftHashMap();
cacheType = cacheType.substring("SOFT_".length());
prefix = "SOFT_";
} else if (cacheType.startsWith("WEAK_")) {
secondLevel = new WeakHashMap();
cacheType = cacheType.substring("WEAK_".length());
prefix = "WEAK_";
}
Cache cache;
if (CacheTQ.TYPE_NAME.equals(cacheType)) {
cache = new CacheTQ(writer, cacheSize);
} else if (CacheLRU.TYPE_NAME.equals(cacheType)) {
cache = new CacheLRU(writer, cacheSize);
} else {
throw Message.getInvalidValueException(cacheType, "CACHE_TYPE");
}
if (secondLevel != null) {
cache = new CacheSecondLevel(cache, prefix, secondLevel);
}
return cache;
}
public void clear() {
head.next = head.previous = head;
// first set to null - avoiding out of memory
......
/*
* Copyright 2004-2009 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: Jan Kotek
*/
package org.h2.util;
import java.sql.SQLException;
import java.util.Map;
/**
* Cache which wraps another cache (proxy pattern) and adds caching using map.
* This is useful for WeakReference, SoftReference or hard reference cache.
*/
class CacheSecondLevel implements Cache {
private final Cache baseCache;
private final String prefix;
private final Map map;
CacheSecondLevel(Cache cache, String prefix, Map map) {
this.baseCache = cache;
this.prefix = prefix;
this.map = map;
}
public void clear() {
map.clear();
baseCache.clear();
}
public CacheObject find(int pos) {
CacheObject ret = baseCache.find(pos);
if (ret == null) {
ret = (CacheObject) map.get(ObjectUtils.getInteger(pos));
}
return ret;
}
public CacheObject get(int pos) {
CacheObject ret = baseCache.get(pos);
if (ret == null) {
ret = (CacheObject) map.get(ObjectUtils.getInteger(pos));
}
return ret;
}
public ObjectArray getAllChanged() {
return baseCache.getAllChanged();
}
public int getMaxSize() {
return baseCache.getMaxSize();
}
public int getSize() {
return baseCache.getSize();
}
public String getTypeName() {
return prefix + baseCache.getTypeName();
}
public void put(CacheObject r) throws SQLException {
baseCache.put(r);
Integer pos = ObjectUtils.getInteger(r.getPos());
map.put(pos, r);
}
public void remove(int pos) {
baseCache.remove(pos);
map.remove(ObjectUtils.getInteger(pos));
}
public void setMaxSize(int size) throws SQLException {
baseCache.setMaxSize(size);
}
public CacheObject update(int pos, CacheObject record) throws SQLException {
CacheObject oldRec = baseCache.update(pos, record);
map.put(ObjectUtils.getInteger(pos), record);
return oldRec;
}
}
......@@ -19,25 +19,26 @@ import org.h2.message.Message;
* In this implementation, items are moved from 'in'
* queue and move to the 'main' queue if the are referenced again.
*/
public class Cache2Q implements Cache {
class CacheTQ implements Cache {
static final String TYPE_NAME = "TQ";
public static final String TYPE_NAME = "TQ";
private static final int MAIN = 1, IN = 2, OUT = 3;
private static final int PERCENT_IN = 20, PERCENT_OUT = 50;
private final CacheWriter writer;
private final CacheObject headMain = new CacheHead();
private final CacheObject headIn = new CacheHead();
private final CacheObject headOut = new CacheHead();
private final int len;
private final int mask;
private int maxSize;
private int maxMain, maxIn, maxOut;
private CacheObject headMain = new CacheHead();
private CacheObject headIn = new CacheHead();
private CacheObject headOut = new CacheHead();
private int sizeMain, sizeIn, sizeOut;
private int recordCount;
private int len;
private CacheObject[] values;
private int mask;
public Cache2Q(CacheWriter writer, int maxKb) {
CacheTQ(CacheWriter writer, int maxKb) {
int maxSize = maxKb * 1024 / 4;
this.writer = writer;
this.maxSize = maxSize;
......
/*
* Copyright 2004-2009 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* Map which stores items using SoftReference. Items can be garbage collected
* and removed. It is not a general purpose cache, as it doesn't implement some
* methods, and others not according to the map definition, to improve speed.
*/
public class SoftHashMap extends AbstractMap implements Map {
private Map map;
private ReferenceQueue queue = new ReferenceQueue();
public SoftHashMap() {
map = new HashMap();
}
private void processQueue() {
while (true) {
Object o = queue.poll();
if (o == null) {
return;
}
Object key = ((SoftValue) o).key;
map.remove(key);
}
}
public Object get(Object key) {
processQueue();
Object o = map.get(key);
if (o == null) {
return null;
}
return ((SoftReference) o).get();
}
/**
* Store the object. The return value of this method is null or a SoftReference.
*
* @param key the key
* @param value the value
* @return null or a SoftReference that points to the old object.
*/
public Object put(Object key, Object value) {
processQueue();
return map.put(key, new SoftValue(value, queue, key));
}
/**
* Remove an object. The return value of this method is null or a SoftReference.
*
* @param key the key
* @return null or a SoftReference that points to the old object.
*/
public Object remove(Object key) {
processQueue();
return map.remove(key);
}
public void clear() {
processQueue();
map.clear();
}
public Set entrySet() {
throw new UnsupportedOperationException();
}
/**
* A soft reference that has a hard reference to the key.
*/
private static class SoftValue extends SoftReference {
private final Object key;
public SoftValue(Object ref, ReferenceQueue q, Object key) {
super(ref, q);
this.key = key;
}
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论