自己动手写一个key value store

2023-10-27

一涉及到persistent, 哪怕只是最基本的需求,很多人都会依赖数据库,或是其他现成的库或工具。确实,对于文件,大部分人很少直接打交道,或者只是诸如整体反序列化/序列化、按行读取、append new line等有限的操作。


一个persistent store最基本的问题是如何组织数据,也就是access method, 大致有:

1)队列(定长记录 or 不定长记录): kafka等消息队列,受限的访问模式,读写都很快, 

2)B+ 树 and variants:ntfs, ext2, DBMS等。读写性能综合,且支持key的比较、locality of reference,cache 友好

3)  Hash:BDB,DBMS等。缺点是不支持key的比较、locality of reference。

4) LSM:levelDB,HBase, bigTable等。 快速写,支持比较、 locality of reference


下面是一个基于Hash的(拉链法处理冲突)、任意长度key、任意长度value的 key value store,key 和 value都是 byte[]。

import java.io.*;
import java.util.Arrays;

public class HashStore {
	private RandomAccessFile rf;
	private long tableSize = 1024L * 1024 * 1024;
	public HashStore(String file) throws IOException {
		File f = new File(file);
		if (!f.exists()) {
			f.createNewFile();
		}
		rf = new RandomAccessFile(file, "rw");
		if (rf.length() == 0) {
			rf.setLength(tableSize * 8);
		}
	}
	public void put(byte[] k, byte[] v) throws IOException {
		long i = (Math.abs(Arrays.hashCode(k)) % tableSize) * 8;
		rf.seek(i);		
		byte[] key = new byte[k.length];
		boolean removeFlag = false;
		for (long p = rf.readLong(); p != 0; ) {
			rf.seek(p);
			int keyLen = rf.readInt();
			if (keyLen == k.length) {
				rf.read(key);
				int valueLen = rf.readInt();
				if (Arrays.equals(key, k)) {					
					if (valueLen == v.length) { // new value is of the same size, reuse the space
						rf.write(v);
						return;
					}
					else { // allocate new space, remove the old item (not physically)
						removeFlag = true;
						break;				
					}
				}
				else {
					rf.seek(rf.getFilePointer() + valueLen);
					p = rf.readLong();
				}
			}
			else {
				rf.seek(rf.getFilePointer() + keyLen);
				int valueLen = rf.readInt();
				rf.seek(rf.getFilePointer() + valueLen);
				p = rf.readLong();
			}
		}
		
		if (removeFlag) {
			remove(k);
		}
		
		rf.seek(i);
		long head = rf.readLong();		
		long pos = rf.length();	
		// insert the new item
		rf.seek(pos);
		rf.writeInt(k.length);
		rf.write(k);
		rf.writeInt(v.length);
		rf.write(v);
		rf.writeLong(head);
		rf.seek(i);
		rf.writeLong(pos);			
	}
	public byte[] get(byte[] k) throws IOException {
		long i = Math.abs(Arrays.hashCode(k)) % tableSize * 8;
		rf.seek(i);
		byte[] key = new byte[k.length];
		for (long p = rf.readLong(); p != 0; ) {
			rf.seek(p);
			int keyLen = rf.readInt();
			if (keyLen == k.length) {
				rf.read(key);
				int valueLen = rf.readInt();
				if (Arrays.equals(key, k)) {					
					byte[] v = new byte[valueLen];
					rf.read(v);
					return v;
				}
				else {
					rf.seek(rf.getFilePointer() + valueLen);
					p = rf.readLong();
				}
			}
			else {
				rf.seek(rf.getFilePointer() + keyLen);
				int valueLen = rf.readInt();
				rf.seek(rf.getFilePointer() + valueLen);
				p = rf.readLong();
			}
		}
		return null;
	}
	
	public void remove(byte[] k) throws IOException{
		long i = Math.abs(Arrays.hashCode(k)) % tableSize * 8;
		rf.seek(i);
		byte[] key = new byte[k.length];
		for (long p = rf.readLong(), pre = i; p != 0; ) {
			rf.seek(p);
			int keyLen = rf.readInt();
			if (keyLen == k.length) {
				rf.read(key);
				int valueLen = rf.readInt();
				if (Arrays.equals(key, k)) {					
					rf.seek(rf.getFilePointer() + valueLen);
					long next = rf.readLong();
					rf.seek(pre);
					rf.writeLong(next);
					return;
				}
				else {
					rf.seek(rf.getFilePointer() + valueLen);
					p = rf.readLong();
					pre = rf.getFilePointer() - 8;
				}
			}
			else {
				rf.seek(rf.getFilePointer() + keyLen);
				int valueLen = rf.readInt();
				rf.seek(rf.getFilePointer() + valueLen);
				p = rf.readLong();
				pre = rf.getFilePointer() - 8;
			}
		}
	}
	
	public void close() throws IOException {
		rf.close();
	}
	
	public void manageFragment() {
		// garbage collection, compact the data file
	}
}

put 一百万个item,再每个get一次,用时40秒,相同的操作在Sql server 上(两个字段的表(key, value), key 为主键) 写一百万个key-value, 再每个用主键取一次,竟然结束不了,主要是一个一个select比较慢。


import static org.junit.Assert.*;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;

import org.junit.Test;

public class HashStoreTest {

	@Test
	public void test() throws IOException {
		HashStore hs = new HashStore("c:\\hashStore.txt");
		
		int n = 1000000;
		Integer[] a = new Integer[n];
		for (int i = 0; i < n; ++i) {
			a[i] = i + 1;
		}
		shuffle(a);
		
		long start = System.currentTimeMillis();
		for (int j = 0; j < n; ++j) {
			Integer i = a[j];
			String k = i.toString();
			String v = new Integer(-i).toString();
			hs.put(k.getBytes(), v.getBytes());
		}
		
		for (int j = 0; j < n; ++j) {
			Integer i = a[j];
			assertEquals(new String(hs.get(i.toString().getBytes())), new Integer(-i).toString());
		}
		System.out.println(System.currentTimeMillis() - start);
	}

	private void shuffle(Integer[] a) {
		if (a == null) return;
		Random r = new Random();
		for (int i = 0; i < a.length; ++i) {
			int j = r.nextInt(i + 1);
			Integer tmp = a[i];
			a[i] = a[j];
			a[j] = a[i];
		}
	}
}



本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

自己动手写一个key value store 的相关文章

  • SQL中日期格式的处理to_char函数

    日期和字符转换函数用法 to date to char select to char sysdate yyyy mm dd hh24 mi ss as nowTime from dual 日期转化为字符串 select to char sy
  • FISCO BCOS Go-sdk 配置文件

    0 参考文档 GitHub FISCO BCOS go sdk golang SDK of FISCO BCOS 1 环境配置 操作系统 CentOS7 Golang版本 1 17 2 WeBASE版本 1 5 2 已开启 可参见 WeBA

随机推荐

  • CentOS下部署Postgresql-离线模式

    标签 离线部署Postgresql centos下部署Postgresql 一 环境要求 测试环境部署信息 操作系统 CentOS 7 8 cat etc redhat release postgresql版本 13 3 二 准备工作 2
  • mybatis string 参数 MyBatis 参数类型为String时常见问题及解决办法

    笔者在开发中遇到一个类型转换的问题 特此记录下来分析给大家 对字符串参数进行是否相等 比较时的问题 错误
  • 如何在C语言中调用shell命令

    如何在C语言中调用shell命令 在linux操作系统中 很多shell命令使用起来非常简单 这些shell命令的程序实现已经被底层实现好 有时候需要在程序中调用shell命令 这样可以就不用在控制台上手动输入shell命令了 下面就以三个
  • Pyhon引用Socket和Scapy实现稳定性测试

    前言 在日常测试中 稳定性测试是必不可少的环节 但是难以模拟真实的客户场景是短板 而且稳定性测试开始后 执行数据的收集也是少之又少 所以我们结合ptyhon中的Socket库和Scapy库 来实现稳定性测试 可以做到随机EPS 区间EPS出
  • 2023学习软件测试,如何月薪过万?这几条必须具备

    软件测试 如何月薪过万 这个问题换做前几年的功能测试或许还有点小难 但如今以点点点为主的功能测试 即将被淘汰 适者生存的法则下 自动化测试如雨后春笋登上舞台 同一时间 随着各大互联网公司迅速扩大测试人员的招聘规模 也使得软件测试这个行业 但
  • Jenkins完全配置(基于jenkins镜像进行jar包自动化打镜像自动化推入)(renchar与阿里云进行自动化推送)

    写在前面 使用谷歌浏览器可以进行全文翻译 点击配置文件中的蓝色问号可以解决部分问题 点击新建任务 注意maven和jdk需要配置才能使用 需要构建镜像则需提前下载好插件 选择项目类型 输入自定义的任务名称 请根据以下配置文件进行配置 只需修
  • SpringBoot整合Keycloak实现单点登录

    Keycloak是一个开源的身份和权限访问管理工具 轻松为应用程序和安全服务添加身份验证 无需处理储存用户或者验证用户 其提供用户联合 强健的身份验证 用户管理和细粒度授权等功能 1 搭建Keycloak服务器 本文使用docker com
  • 初级Java程序员如何快速提升自己的能力?

    对于刚刚进入工作岗位的初级程序员来说 不论是进入外包公司 还是互联网公司 都需要一个适应的过程 不少刚走上工作岗位的程序员 就是因为迟迟不能进入工作状态而选择离开 这也是比较常见的事情 导致不能进入工作状态的原因主要有三方面 其一是自身的知
  • python小游戏毕设 走迷宫小游戏设计与实现 (源码)

    文章目录 0 项目简介 1 课题背景 2 实现效果 3 Pygame介绍 4 具体实现 4 1 创建迷宫 4 2 定义角色类 4 3 界面切换 5 最后 0 项目简介 Hi 各位同学好呀 这里是L学长 今天向大家分享一个今年 2022 最新
  • 背景的设置、渐变和雪碧图

    一 背景的设置和和可选值 1 background color 设置背景颜色 2 background image来设置背景图片 语法 background image url 相对路径 可以同时为一个元素指定背景颜色和背景图片 这样背景颜
  • springboot学习笔记(一)

    基础知识点 什么是Spring Spring是一个开源框架 2003年兴起的一个轻量级的java开发框架 Spring是为了解决企业级应用开发的复杂性而创建的 简化开发 Spring是如何简化java开发的 什么是springboot sp
  • Keras模型测试准确率震荡大

    今天在Keras训练了一个模型 发现模型的训练accuracy和测试accuracy的准确率偏差比较大 如下 在问了些大佬后 感谢大佬 我的这个原因很可能是因为过拟合导致的差距比较大 之后在每个层之间都加入了dropout 再重新训练模型得
  • Unity中欧拉角

    什么是欧拉角 没有方向 大小概念 1 使用单个角度来保存方位 2 X与Z沿自身坐标系旋转 Y沿世界坐标旋转 3 API Vector3 eulerAngle this tranform rulerAngles 优点 1 仅使用三个数字表达方
  • 如何备份服务器系统还原,服务器操作系统备份和还原

    服务器操作系统备份和还原 内容精选 换一换 实例支持自动化发放裸金属服务器 远程Console登录 支持租户自主管理裸金属服务器生命周期 查询 启动 关机 重启 删除 导出服务器列表 将租户名下的所有裸金属服务器信息 以CSV文件的形式导出
  • signature=8f638f82cfb5ef3c26e5bb05751ee69d,iSpy/VideoSourceAdvanced.resx at 4eee092db75fe362bcfb7752...

    text microsoft resx 2 0 System Resources ResXResourceReader System Windows Forms Version 4 0 0 0 Culture neutral PublicK
  • STM32微控制器综合实训11 伺服电机控制器设计实验

    实验11 伺服电机控制器设计实验 了解伺服电机的应用领域 掌握伺服电机的速度控制模式 伺服电机的位置控制模式 文章目录 程序设计 伺服电机的速度控制模式代码讲解 main c timer c 伺服电机的位置控制模式代码讲解 main c t
  • 8 Buildroot 根文件系统构建

    一 根文件系统简介 根文件系统一般也叫做 rootfs 这个是属于 Linux 内核的一部分 根文件系统首先是一种文件系统 该文件系统不仅具有普通文件系统的存储数据文件的功能 但是相对于普通的文件系统 它的特殊之处在于 它是内核启动时所挂载
  • oracle函数忽略大小写,Oracle中不区分大小写的主键

    我们的数据的语义不区分大小写 因此我们将oracle会话配置为不区分大小写 alter session set NLS COMP LINGUISTIC alter session set NLS SORT BINARY AI 然后 为了利用
  • vue3中的reactive和ref

    一 关于reactive reactive 接受一个对象类型的值 返回一个对象的代理 reactive的特点 1 仅对对象类型有效 对象 数组和 Map Set 这样的集合类型 而对 string number 和 boolean 这样的
  • 自己动手写一个key value store

    一涉及到persistent 哪怕只是最基本的需求 很多人都会依赖数据库 或是其他现成的库或工具 确实 对于文件 大部分人很少直接打交道 或者只是诸如整体反序列化 序列化 按行读取 append new line等有限的操作 一个persi