构造MVstore
/**
* Create and open the store.
*
* @param config the configuration to use
* @throws IllegalStateException if the file is corrupt, or an exception
* occurred while opening
* @throws IllegalArgumentException if the directory does not exist
*/
MVStore(HashMap<String, Object> config) {
this.compress = config.containsKey("compress");
Object o = config.get("pageSplitSize");
//page分裂的阀值,默认时候6K
pageSplitSize = o == null ? 6 * 1024 : (Integer) o;
o = config.get("backgroundExceptionHandler");
this.backgroundExceptionHandler = (UncaughtExceptionHandler) o;
//meta数据map
meta = new MVMapConcurrent<String, String>(StringDataType.INSTANCE, StringDataType.INSTANCE);
HashMap<String, String> c = New.hashMap();
c.put("id", "0");
//新建的store currentVersion为0
c.put("createVersion", Long.toString(currentVersion));
//meta是个copyonwrite map,读操作无锁,写时同步,初始化metamap的属性store为this,id为0,createVersion为0,writeversion也为0
meta.init(this, c);
//如果没有设置filestore和filename都为null,直接返回,可能表示只是用内存,不持久化
fileStore = (FileStore) config.get("fileStore");
String fileName = (String) config.get("fileName");
if (fileName == null && fileStore == null) {
cache = null;
return;
}
if (fileStore == null) {
fileStore = new FileStore();
}
//保留时间
retentionTime = fileStore.getDefaultRetentionTime();
boolean readOnly = config.containsKey("readOnly");
o = config.get("cacheSize");
int mb = o == null ? 16 : (Integer) o;
//创建缓存
if (mb > 0) {
int maxMemoryBytes = mb * 1024 * 1024;
int averageMemory = Math.max(10, pageSplitSize / 2);
int segmentCount = 16;
int stackMoveDistance = maxMemoryBytes / averageMemory * 2 / 100;
cache = new CacheLongKeyLIRS<Page>(
maxMemoryBytes, averageMemory, segmentCount, stackMoveDistance);
}
//写缓冲默认4MB
o = config.get("writeBufferSize");
mb = o == null ? 4 : (Integer) o;
int writeBufferSize = mb * 1024 * 1024;
int div = pageSplitSize;
//就散没有最多能有多少未保存的page
unsavedPageCountMax = writeBufferSize / (div == 0 ? 1 : div);
char[] encryptionKey = (char[]) config.get("encryptionKey");
try {
//打开文件
fileStore.open(fileName, readOnly, encryptionKey);
if (fileStore.size() == 0) {
//如果新建,写入头
creationTime = 0;
creationTime = getTime();
lastStoreTime = creationTime;
storeHeader.put("H", "3");
storeHeader.put("blockSize", "" + BLOCK_SIZE);
storeHeader.put("format", "" + FORMAT_WRITE);
storeHeader.put("creationTime", "" + creationTime);
writeStoreHeader();
} else {
readStoreHeader();
long format = DataUtils.parseLong(storeHeader.get("format"), 0);
if (format > FORMAT_WRITE && !fileStore.isReadOnly()) {
throw DataUtils.newIllegalStateException(
DataUtils.ERROR_UNSUPPORTED_FORMAT,
"The write format {0} is larger than the supported format {1}, " +
"and the file was not opened in read-only mode",
format, FORMAT_WRITE);
}
format = DataUtils.parseLong(storeHeader.get("formatRead"), format);
if (format > FORMAT_READ) {
throw DataUtils.newIllegalStateException(
DataUtils.ERROR_UNSUPPORTED_FORMAT,
"The read format {0} is larger than the supported format {1}",
format, FORMAT_READ);
}
//新建rootChunkStart为0
if (rootChunkStart > 0) {
readMeta();
}
}
//新建meta没有这个值,所以默认-1
long rollback = DataUtils.parseLong(meta.get("rollbackOnOpen"), -1);
if (rollback != -1) {
rollbackTo(rollback);
}
} catch (IllegalStateException e) {
try {
closeStore(false);
} catch (Exception e2) {
// ignore
}
throw e;
} finally {
if (encryptionKey != null) {
Arrays.fill(encryptionKey, (char) 0);
}
}
//更新上次store时间
lastStoreTime = getTime();
//更新上次store版本
this.lastCommittedVersion = currentVersion;
// setWriteDelay starts the thread, but only if
// the parameter is different than the current value
o = config.get("writeDelay");
int writeDelay = o == null ? 1000 : (Integer) o;
//设置延迟写时间,如果大于0,会启动后台写线程
setWriteDelay(writeDelay);
}
private void stopBackgroundThread() {
//构造的时候调用直接return
if (backgroundThread == null) {
return;
}
Thread t = backgroundThread;
backgroundThread = null;
synchronized (this) {
notify();
}
try {
t.join();
} catch (Exception e) {
// ignore
}
}
/**
* Set the maximum delay in milliseconds to store committed changes (for
* file-based stores).
* <p>
* The default is 1000, meaning committed changes are stored after at
* most one second.
* <p>
* When the value is set to -1, committed changes are only written when
* calling the store method. When the value is set to 0, committed
* changes are immediately written on a commit, but please note this
* decreases performance and does still not guarantee the disk will
* actually write the data.
*
* @param millis the maximum delay
*/
public void setWriteDelay(int millis) {
if (writeDelay == millis) {
return;
}
writeDelay = millis;
if (fileStore == null) {
return;
}
//未做处理,直接return
stopBackgroundThread();
// start the background thread if needed
if (millis > 0) {
//大于0就起后台线程,线程会wait最多sleep时间就去调后台store,并不是1s,而是0.1s
int sleep = Math.max(1, millis / 10);
Writer w = new Writer(this, sleep);
Thread t = new Thread(w, "MVStore writer " + fileStore.toString());
t.setDaemon(true);
t.start();
backgroundThread = t;
}
}
public int getWriteDelay() {
return writeDelay;
}
/**
* A background writer to automatically store changes from time to time.
*/
private static class Writer implements Runnable {
private final MVStore store;
private final int sleep;
Writer(MVStore store, int sleep) {
this.store = store;
this.sleep = sleep;
}
@Override
public void run() {
while (store.backgroundThread != null) {
synchronized (store) {
try {
store.wait(sleep);
} catch (InterruptedException e) {
// ignore
}
}
store.storeInBackground();
}
}
}
/**
* Store all unsaved changes, if there are any that are committed.
*/
void storeInBackground() {
if (closed || unsavedPageCount == 0) {
return;
}
// could also store when there are many unsaved pages,
// but according to a test it doesn't really help
if (lastStoredVersion >= lastCommittedVersion) {
return;
}
long time = getTime();
//还是1s,前面没看到这个地方
if (time <= lastStoreTime + writeDelay) {
return;
}
if (!hasUnsavedChanges()) {
return;
}
//存储committed
try {
store(true);
} catch (Exception e) {
if (backgroundExceptionHandler != null) {
backgroundExceptionHandler.uncaughtException(null, e);
}
}
}