本文采用Java语言版本的levelDB,在springboot项目下或者maven项目中进行测试。
0,快捷键复习
根据类名查找类 ctrl+shift+n或者在面板右上角点击搜索图标
查看接口的实现 ctrl+alt+左键
1,引入依赖
<dependency>
<groupId>org.iq80.leveldb</groupId>
<artifactId>leveldb</artifactId>
<version>0.12</version>
</dependency>
<dependency>
<groupId>org.iq80.leveldb</groupId>
<artifactId>leveldb-api</artifactId>
<version>0.12</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
2,定义一些常量
//存储路径,默认是项目路径下
private static final String PATH = "leveldb";
private static final File FILE = new File(PATH);
//编码集
private static final Charset CHARSET = Charset.forName("UTF-8");
3,测试写入和读取操作
/**
* put操作测试
*/
@Test
public void putTest() {
DBFactory factory = new Iq80DBFactory();
// 默认如果没有则创建
Options options = new Options();
File file = new File(PATH);
//sync写数据后强制写入磁盘
WriteOptions writeOptions=new WriteOptions().sync(true);
DB db = null;
try {
db = factory.open(file, options);
byte[] keyByte1 = "key-01".getBytes(CHARSET);
byte[] keyByte2 = "key-02".getBytes(CHARSET);
// 会写入磁盘中
db.put(keyByte1, "value-01".getBytes(CHARSET),writeOptions);
db.put(keyByte2, "value-02".getBytes(CHARSET));
System.out.println(asString(db.get(keyByte1)));
System.out.println(asString(db.get(keyByte2)));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (db != null) {
try {
db.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
/**
* 写操作 WriteOptions可以对写操作进行一些定义,ReadOptions同样
*/
@Test
public void writeOptionsTest() {
DBFactory factory = new Iq80DBFactory();
Options options = new Options();
DB db = null;
try {
db = factory.open(FILE, options);
WriteOptions writeOptions = new WriteOptions().sync(true); // 线程安全
db.put("key-08".getBytes(CHARSET), "value-08".getBytes(CHARSET), writeOptions);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (db != null) {
try {
db.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
项目目录中多了一些文件,分别是日志文件记录操作信息,manifest文件记录存储状态信息,current记录当前生效的manifest文件名。
4,从快照中读取
在上一步之后,能够从磁盘中间中的快照中读取数据。如果在生成快照之后进行写操作,快照读不会受到影响。
/**
* 测试从快照中读取数据
*/
@Test
public void readFromSnapshotTest() {
DBFactory factory = new Iq80DBFactory();
File file = new File(PATH);
Options options = new Options();
DB db = null;
try {
db = factory.open(file, options);
// 读取当前快照,重启服务仍能读取,说明快照持久化至磁盘,
Snapshot snapshot = db.getSnapshot();
// 读取操作
ReadOptions readOptions = new ReadOptions();
//当读取一大块数据时,可以使用fillCache = false来禁止缓存被覆盖。
readOptions.fillCache(false);
// 默认snapshot为当前
readOptions.snapshot(snapshot);
DBIterator it = db.iterator(readOptions);
//迭代访问
while (it.hasNext()) {
Map.Entry<byte[], byte[]> entry = it.next();
String key = new String(entry.getKey(), CHARSET);
String value = new String(entry.getValue(), CHARSET);
System.out.println("key: " + key + " value: " + value);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (db != null) {
try {
db.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
/**
* 生成快照后写操作不影响快照读取
*/
@Test
public void snapshotTest() {
DBFactory factory = new Iq80DBFactory();
Options options = new Options();
DB db = null;
try {
db = factory.open(FILE, options);
db.put("key-04".getBytes(CHARSET), "value-04".getBytes(CHARSET));
// 只能之前到getSnapshot之前put的值,之后的无法获取,即读取期间数据的变更
Snapshot snapshot = db.getSnapshot();
db.put("key-05".getBytes(CHARSET), "value-05".getBytes(CHARSET));
ReadOptions readOptions = new ReadOptions();
readOptions.fillCache(false);
readOptions.snapshot(snapshot);
//可以从数据库中读取,但是快照遍历时没有
byte[] res=db.get("key-05".getBytes(CHARSET));
System.out.println("From the database "+new String(res,CHARSET));
DBIterator it = db.iterator(readOptions);
while (it.hasNext()) {
Map.Entry<byte[],byte[]> entry = it.next();
String key = new String(entry.getKey(), CHARSET);
String value = new String(entry.getValue(), CHARSET);
System.out.println("key: " + key + " value: " + value);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (db != null) {
try {
db.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
5,删除操作
删除操作最终转化put
操作,只是将key-value
中的value
置空。
如下图所示,在底层会对value进行判断,分别调用不同方法,但是到最后都是memtable.add()
,只是参数不同。
@Test
public void deleteTest() {
DBFactory factory = new Iq80DBFactory();
Options options = new Options();
DB db = null;
try {
db = factory.open(FILE, options);
// 存在会删除,之后查询不出
db.delete("key-02".getBytes(CHARSET));
// 不存在不会报错
db.delete("key02".getBytes(CHARSET));
Snapshot snapshot = db.getSnapshot();
ReadOptions readOptions = new ReadOptions();
readOptions.fillCache(false);
readOptions.snapshot(snapshot);
DBIterator it = db.iterator(readOptions);
while (it.hasNext()) {
Map.Entry<byte[], byte[]> entry = (Map.Entry<byte[], byte[]>) it
.next();
String key = new String(entry.getKey(), CHARSET);
String value = new String(entry.getValue(), CHARSET);
System.out.println("key: " + key + " value: " + value);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (db != null) {
try {
db.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
6,批量操作
利用writeBatch可以将更新操作和删除操作作为一个整体批量执行。
/**
* 批量写和删除
*/
@Test
public void writeBatchDeleteTest() {
DBFactory factory = Iq80DBFactory.factory;
Options options = new Options();
DB db = null;
try {
db = factory.open(FILE, options);
WriteBatch writeBatch = db.createWriteBatch();
writeBatch.put("key-10".getBytes(CHARSET), "value-10".getBytes(CHARSET));
writeBatch.put("key-11".getBytes(CHARSET), "value-11".getBytes(CHARSET));
// 会将key-01的value置为""
writeBatch.delete("key-01".getBytes(CHARSET));
db.write(writeBatch);
writeBatch.close();
DBIterator it = db.iterator();
while (it.hasNext()) {
Map.Entry<byte[], byte[]> entry = (Map.Entry<byte[], byte[]>) it
.next();
String key = new String(entry.getKey(), CHARSET);
String value = new String(entry.getValue(), CHARSET);
System.out.println("key: " + key + " value: " + value);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (db != null) {
try {
db.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
7,对象的写入和读取
对象的写入和读取,与普通数据的区别在于对象需要实现序列化接口,并在写入和读取后进行序列化和反序列化。
/**
* 对象的写入测试
*/
@Test
public void writeObject() {
Options options = new Options();
DBFactory factory = Iq80DBFactory.factory;
DB db = null;
try {
db = factory.open(FILE, options);
User user = new User();
user.setName("admin");
user.setSex("男");
WriteOptions writeOptions = new WriteOptions();
writeOptions.snapshot(true);
ByteArrayOutputStream baos = new ByteArrayOutputStream(); //byte[]输出流
ObjectOutputStream oos = new ObjectOutputStream(baos); //包装流,对象输出到输出流对象中
oos.writeObject(user); //写对象到输出流
db.put(user.getName().getBytes(CHARSET), baos.toByteArray()); //写入db时要转化为(byte[],byte[])形式
} catch (IOException e) {
e.printStackTrace();
} finally {
if (db != null) {
try {
db.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 对象读取测试
*/
@Test
public void readObject() {
Options options = new Options();
DBFactory factory = Iq80DBFactory.factory;
DB db = null;
try {
db = factory.open(FILE, options);
byte[] valueByte = db.get("admin".getBytes(CHARSET)); //从数据库中读出byte[]
ByteArrayInputStream bais = new ByteArrayInputStream(valueByte);//输入流对象读取结果
ObjectInputStream ois = new ObjectInputStream(bais);//写对象流包装
User user = (User) ois.readObject();//获得对象
System.out.println(user);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (db != null) {
try {
db.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}