Java项目实战-200行代码写简易KV数据库[附完整源码]
本文从0实现一个简易的KV数据库,代码行数不多,核心代码不超过200行。
设计思路
- 查询语法:支持String类型的Key和Value,暂不支持其他复杂类型,不支持SQL语法解析和执行计划优化。
- 存储引擎:基于顺序日志进行写入,每条执行命令的数据信息按行存在文本日志文件里。暂不支持类似hbase中的合并等复杂的逻辑。
代码模块
下面给大家介绍核心代码解析。
核心API定义:
/**
* KV数据库操作Client
*
* @author summer
* @version : SimpleKvClient.java, v 0.1 2022年05月05日 3:49 PM summer Exp $
*/
public interface SimpleKvClient {
/**
* 增加元素
*
* @param key k
* @param value v
*/
void put(String key, String value);
/**
* 获取指定key对于的值
*
* @param key k
* @return
*/
String get(String key);
/**
* 删除指定key
*
* @param key
*/
void del(String key);
} 基于顺序日志的实现:
import com.alibaba.fastjson.JSON;
import com.summer.simplekv.api.SimpleKvClient;
import com.summer.simplekv.enums.CommandTypeEnum;
import com.summer.simplekv.model.CommandRequestModel;
import lombok.extern.java.Log;
import org.apache.commons.lang3.StringUtils;
import java.io.*;
/**
* 基于日志顺序写入的KV数据库实现
*
* @author summer
* @version : LogBasedKV.java, v 0.1 2022年05月05日 4:06 PM summer Exp $
*/
@Log
public class LogBasedKV implements SimpleKvClient {
/**
* 日志文件
*/
private File logFile;
/**
* 构造函数
*
* @param fileName 文件名
*/
public LogBasedKV(String fileName) {
logFile = new File(fileName);
}
@Override
public void put(String key, String value) {
BufferedWriter bufferedWriter = null;
try {
FileWriter fileWriter = new FileWriter(logFile, true);
bufferedWriter = new BufferedWriter(fileWriter);
//日志写入内容构造
CommandRequestModel commandRequestModel = new CommandRequestModel();
commandRequestModel.setCommandTypeEnum(CommandTypeEnum.SET);
commandRequestModel.setKey(key);
commandRequestModel.setValue(value);
//往日志文件中写入内容
bufferedWriter.write(JSON.toJSONString(commandRequestModel));
bufferedWriter.newLine();
} catch (Exception e) {
log.warning("put exception,[" + key + "," + value + "]");
} finally {
try {
bufferedWriter.close();
} catch (Exception e) {
log.warning("bufferedWriter close exception");
}
}
}
@Override
public String get(String key) {
try {
FileReader fileReader = new FileReader(logFile);
BufferedReader bufferedReader = new BufferedReader(fileReader);
//按行读取日志文件的内容,查找到最后一条修改类操作命令
CommandRequestModel lastUpdateCommand = null;
String line = bufferedReader.readLine();
while (line != null) {
CommandRequestModel commandRequestModel = JSON.parseObject(line, CommandRequestModel.class);
if ((CommandTypeEnum.SET == commandRequestModel.getCommandTypeEnum()
|| CommandTypeEnum.DEL == commandRequestModel.getCommandTypeEnum())
&& StringUtils.equals(key, commandRequestModel.getKey())) {
lastUpdateCommand = commandRequestModel;
}
line = bufferedReader.readLine();
}
if (lastUpdateCommand == null || CommandTypeEnum.DEL == lastUpdateCommand.getCommandTypeEnum()) {
return null;
}
return lastUpdateCommand.getValue();
} catch (Exception e) {
log.warning("get exception,[" + key + "]");
}
return null;
}
@Override
public void del(String key) {
BufferedWriter bufferedWriter = null;
try {
FileWriter fileWriter = new FileWriter(logFile, true);
bufferedWriter = new BufferedWriter(fileWriter);
//日志写入内容构造
CommandRequestModel commandRequestModel = new CommandRequestModel();
commandRequestModel.setCommandTypeEnum(CommandTypeEnum.DEL);
commandRequestModel.setKey(key);
//往日志文件中写入内容
bufferedWriter.write(JSON.toJSONString(commandRequestModel));
bufferedWriter.newLine();
} catch (Exception e) {
log.warning("del exception,[" + key + "]");
} finally {
try {
bufferedWriter.close();
} catch (Exception e) {
log.warning("bufferedWriter close exception");
}
}
}
} 性能分析
因为是追加写入,所以写入的性能非常快,比如Hbase就是采用的顺序写入的方式。关于读取,因为需要读取扫描整个文件来得到key对应的记录,因此性能较差,是O(N)。像实际生产环境使用,是会做一些优化工作的,比如把日志内容进行刷盘处理,同样的key的多条记录会进行合并处理,然后建立索引,查询性能会快很多。此处只是给大家做个demo展示,还有相当多的优化工作需要做。
工程测试
测试比较简单,直接构建的实例,调用其查询、写入、删除方法即可。
运行日志:
完整代码和讲解:
