基于LSM的Key-Value数据库实现初篇

前篇文章对LSM的基本原理,算法流程做了简单的介绍,这篇文章将实现一个简单的 基于LSM算法迷你Key-Value数据库,结合上篇文章的理论与本篇文章的实践使之对LSM算法有更好的理解,当然此版本还有很大问题只是 Demo模型,后面也会指出;
此LSMDB有支持常见的数据库四大功能: CURD(增删查改),从前篇文章可知要实现基于LSM的数据库此程序中需存在这么几种数据结构: memTable、immutable、SSTable、WAL,分别为内存表、只读内存表、排序字符串表、预写式日志,将这几种数据结构组合起来即可实现一个简单的Key-Value数据库;

结构介绍

MemTable: 内存表,此结构为一个有序的内存结构此处是一个红黑树,当数据达到指定的阈值时会发出刷盘操作,将MemTable拷贝到Immutable生成SSTable,MemTable开始新的周期;
Immutable: 只读内存表,为避免MemTable在刷盘操作时继续有新的入库操作导致出现数据异常情况,引入了此结构使之简单化,在触发刷盘操作时将MemTable数据拷贝到此只读结构,清空MemTable,将Immutable数据生成SSTable,清空Immutable;
SSTable: 为immutable持久化到磁盘后的排序字符串表;
WAL: 为避免MemTable数据还未持久化SSTable到磁盘程序崩溃导致数据丢失的情况引入的,WAL为顺序写入日志,在接收到数据时添加到MemTable的同时将数据写入到WAL文件中,数据刷盘持久化的同时将WAL文件删除,新创建WAL文件,本篇文章暂未实现;

触发SSTable持久化到磁盘时会生成 两个文件,一个为 SSTable文件由两部分组成数据区与元数据,数据区为所存储的值,元数据为数据与索引的开始Offset、长度组成;另一个为 索引文件:索引文件记录了每个Key的起始Offset与长度;

SSTable文件结构:

基于LSM的Key-Value数据库实现初篇

SSTable索引文件结构:

基于LSM的Key-Value数据库实现初篇

具体实现

加载SSTable文件

在程序启动时会初始化上面提到到LSM相关结构,同时检查目录是否存在SSTable文件,存在则从新到旧扫描加载每个SSTable文件的元数据,根据其元数据加载SSTable索引文件到内存中。

/**
还原索引
*/
func (s *SSTable) restoreIndex() {
   s.meta.readFormFile(s.dataFile)
   len := s.meta.indexLen
   offset := s.meta.indexStart
   s.indexFile.Seek(int64(offset), 0)
   b := make([]byte, len)
   s.indexFile.Read(b)
   s.indexTree.FromJSON(b)
}

写入操作

LSMDB在执行写操作时会先写入到内存表memoryTable,当memoryTable大小超过某个阈值时会执行切换内存表,将memoryTable数据拷贝到immuTable,清空memoryTable,执行持久化写入SSTable,清空immuTable;

/**
设置键值
*/
func (l *LSMStore) Set(key string, value string) {
  var cmd = &SetCommand{Command{1}, key, value}
  //todo 写入wal
  //写入内存表
  l.memoryTable.Put(key, cmd)
   if l.memoryTable.Size() > storeThreshold {
     l.switchTable()
     l.toSSTable()
   }
}

删除数据

LSMDB数据库中的删除并不是真正的删除,只是追加一条相同Key标志位为删除的数据,在读取时再做相应的处理,其他流程与添加类似;

/**
删除数据
*/
func (l *LSMStore) Del(key string) {
   var cmd = &RMCommand{Command{2}, key}
   //todo 写入wal
   //写入内存表
   l.memoryTable.Put(key, cmd)
    if l.memoryTable.Size() > storeThreshold {
    l.switchTable()
    l.toSSTable()
  }
}

读取数据

读取数据时会先检查memoryTable是否有数据,没有则检查immuTable是否存在,最后会依次检查所加载的SSTable索引是否存在,如存在则根据索引执行的Offset、len从SSTable文件读取相对应的内容数据;

/**
获取数据
*/
func (l *LSMStore) Get(key string) interface{} {
   cmd, found := l.memoryTable.Get(key)
   log.Println("memory:", found, cmd)
   if found {
       if v, ok := cmd.(*SetCommand); ok {
      return v.Value
        }
    }
 if l.immuTable != nil && l.immuTable.Size() > 0 {
    cmd, found := l.immuTable.Get(key)
    if found {
        if v, ok := cmd.(*SetCommand); ok {
            return v.Value
        }
    }
  } else {
    for _, t := range l.sstableList.Values() {
        table := t.(*SSTable)
        v := table.query(key)
        return v
    }
   }
  return nil
}

WebApi

开放用于查询、添加、删除的RESTFUL格式api:

查询: http://localhost:8080/lsmdb/{key}

基于LSM的Key-Value数据库实现初篇

添加: http://localhost:8080/lsmdb/{key}
{“key”:”211213″,”value”:”aaaaaaaa”}

基于LSM的Key-Value数据库实现初篇

删除: DELETE http://localhost:8080/lsmdb/{key}

目前存在的问题

目前此版本的实现还有多处都有问题,也是后续版本需改进的地方:
1、此处的索引文件为全量索引,为每个key都记录相应的数据,此处的索引文件大小是非常大的,对性能影响很大;
2、此版本没有实现WAL功能程序崩溃时数据丢失;
3、此版本并没有后台执行SSTable合并功能,没有对修改、删除操作做任何处理,只是在查询时做了相应的忽略操作,影响性能;
4、单机版本不是分布式程序

文章首发地址:https://mp.weixin.qq.com/s/HoRjSMYumG4A40kHn5UKvQ

Original: https://www.cnblogs.com/softlin/p/15705367.html
Author: AiFly
Title: 基于LSM的Key-Value数据库实现初篇

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/622162/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • (转) MySQL中的意向锁

    详解 MySql InnoDB 中意向锁的作用 posted on2022-09-29 21:54 茶倌 阅读(9 ) 评论() 编辑 Original: https://www….

    Java 2023年6月8日
    080
  • SpringBoot 如何进行对象复制

    首先我们看看为什么需要对象复制? 为什么需要对象复制 如上,是我们平时开发中最常见的三层MVC架构模型,编辑操作时Controller层接收到前端传来的DTO对象,在Service…

    Java 2023年5月29日
    085
  • 简单易懂讲文件

    注意事项 如果运行代码的时候找不到文件,但是文件的的确确又存在,检查下 idea 的工作路径 路径 Path Path 对象是将一个路径封装成一个对象,然后通过这个对象来执行路径的…

    Java 2023年6月8日
    0105
  • SpringBoot整合Filter过滤器

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    Java 2023年6月7日
    076
  • ipchat 点对点聊天工具 1.00.05 已发布

    ipchat 点对点聊天工具 1.00.05 已发布。 zg-ipchat 是一款聊天工具。可实现简单的文本信息传输,无加密。点对点直接通讯,无需中间服务器,支持 Pv6/IPv4…

    Java 2023年6月9日
    070
  • java运行时创建对象

    使用JDK自带的反射(java.lang.reflect)或者自省(java.beans.Introspector)运行时创建对象。 有很多场景需要运行时创建对象,比如Copy对象…

    Java 2023年6月5日
    062
  • C# 线程手册 第五章 扩展多线程应用程序 系列

    到目前为止我们使用多线程应用程序的目的是尽可能多地使用计算机处理器资源。所以,看起来我们仅需要为每个独立的任务分配一个不同的线程,并让处理器确定在任何时间它总会处理其中的某一个任务…

    Java 2023年5月29日
    077
  • dubbo源码分析3(dubbo中的spi机制)

    上一篇我们看过了jdk中的spi机制,也分析了它的缺点就是会一次性将META-INF/services下的配置文件中,对应接口的全部实现类都给加载; 而dubbo中的spi肯定是提…

    Java 2023年6月6日
    084
  • tomcat加载启动过程

    流程图 posted @2022-08-19 17:43 默念x 阅读(8 ) 评论() 编辑 Original: https://www.cnblogs.com/monianxd…

    Java 2023年6月9日
    089
  • Java的值传递

    特别注意:java只有值传递没有引用传递。 一、值传递和引用传递的定义 值传递(pass by value)是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进…

    Java 2023年5月29日
    0102
  • 定时调度的线程池

    定时调度线程池 当我们需要定时进行线程的调度 @Slf4j public class Test5 { public static void main(String[] args) …

    Java 2023年6月16日
    0119
  • 你们不要再吵了! Java只有值传递..(11-6补充)

    写在前边 上次聊到Java8新特性lambda时,有小伙伴在评论区提及到了lambda对于局部变量的引用,补充着博客的时候,知识点一发散就有了这篇对于 值传递还是引用传递的思考。关…

    Java 2023年6月5日
    0101
  • 【数据结构】稀疏数组 — 应用场景,转换的思路分析,代码实现

    楔子: 数据结构包括线性结构和非线性结构。 1、线性结构: 1) 线性结构作为最常用的数据结构,其特点是数据元素之间存在一对一的线性关系 2) 线性结构有两种不同的存储结构,即顺序…

    Java 2023年6月8日
    056
  • springBoot项目不重新上传jar包,增量升级步骤

    1.把源jar包cp到一个空文件夹里,然后把该jar包解压 4.重新打包 5.把打好的jar包cp到启动目录,启动就ok Original: https://www.cnblogs…

    Java 2023年5月30日
    072
  • 踩坑系列-Java Calendar

    Calendar是Java util包下的日期Api,其中获取月份是当前月份-1 java;gutter:true; public class Demo {</p> &…

    Java 2023年6月7日
    074
  • Vue学习之——–插槽【默认插槽、具名插槽、作用域插槽】(2022/8/30)

    插槽Vue.js官网介绍:https://vuejs.org/guide/components/slots.html会牵涉到template的用法、占位、实际不渲染到页面中 1、默…

    Java 2023年6月14日
    091
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球