zoukankan      html  css  js  c++  java
  • 存储系统的实现-探析存储的机制和原理

            这一篇主要想写写一些自己对于存储的思考和领悟,由于有些东西自己实践过,所以感触过更加深一些,技术上我还是觉得自己实现和看别人的代码在感触上是不同的。

           这里如果一个图书馆,假如说书就是要我们要放的数据,会怎么放。最土的办法就是随便往里面丢,然后毫无章法,这样每次找书我们就累死了,由于必须每一本书都要一本书一本书翻过去(有点像DB的全表扫描),如果运气好可能会在比較前面找到,最差情况下就是翻遍整个图书馆最后找到了这本书。所以现实中图书馆的书也不是随便丢了,都是各个书架相应各种类型的书籍,这样才方便查找。详细到我们的数据存储也是这种,不可能有一条数据就往文件中面放,这样当数据量一大整个查找就很困难了。

            我对于整个存储的实现是很easy的一种,没有DB中依照各种字段进行查找,仅仅有依照ID进行查找,依照ID进行删除。我实现的初衷也并不是要反复制造轮子,而是在实现的过程中更好的理解这些存储的基本原理。


    系统架构图

           先看一下整个系统的架构图,了解一下整个分层思想。

            第一层为整个入口,包含插入、更新、查询、删除等入口。核心是对于文件的管理,包含文件的分配、读写文件等等。这里全部对文件的操作都是用RandomAccessFile这个类去操作。用这个类的优点是能够知道文件详细的偏移。另外详细的存储文件分为索引文件数据文件。也就是相应存储那一层的Index和Data。这里和数据库的文件分配有些类似。索引单列成一个文件主要是为了加快查询速度。一个优点是能够利用索引的有序性然后利用各种查找算法,我这里利用的是“跳跃表”算法。另外因为索引文件通常比真实的数据文件小非常多,查找过程中能够降低多次IO,能够得到性能上的提升。

    数据格式

           在上面的系统架构图中能够看出第三层的存储分为三个文件,一个是管理文件(Manager)索引文件(Index)数据文件(Data)

    名词解释

           管理文件(Manager):理解为一个文件的统一管理者,以下讲文件格式的时候会具体讲。
           索引文件(Index):索引文件就比較好理解,用过数据库的大概都知道索引文件这个概念。
           数据文件(Data):数据文件就是真实数据的存储块。

    管理文件(Manager)

           上面在名词解释中大概讲到了Manager这一个文件的职责。假设要讲清楚这个文件职责之前必须要对整个存储空间配置的机制有一些了解。这里先用文字描写叙述一下:假设我要存储一段数据,最简单的方式就是用RandomAccessFile直接往文件中面按行写,每一行就是一条记录,而且每行都须要一个标识,来标识这一行的唯一性,有点像DB里面主键的概念,这样删除的时候就不须要移动整个文件的位移(物理删除就须要移动整个位移),仅仅须要把这一行置为可用。那么回到一个问题:怎么知道当前行是可用还是已经被占用,这里就须要对空间有一个管理。就像买票,假设我买了A座位的票,那么A座位的票就是被使用状态,假设退票(相应我们的删除)那么这个座位还能够出售给其它人。总结一下,Manager事实上就是对整个空间的一个管理文件,存储着空间的使用状态。
           说了这么多,上一张图来看看这个Manager详细格式:

            整个文件由5部分组成:文件唯一标识块使用状态文件类型文件超始偏移文件结束偏移。总共是22个字节。这几个字段还是比較好理解,文件类型以下会讲,文件起始偏移和结束偏移事实上说白了是一个坐标,告诉你这个位置在哪里,你直接去那儿找就好了。
            这里先说一下整个文件的配置机制,是採用定长分配。简单的说就是一个房间開始就会分配成多种大小的小格,就是上图右上角所示256bytes512bytes4K1024K,这样分配的优点是便于管理,避免碎片的产生(为了解决碎片问题会有定期的文件合并)。定额分配就会规避碎片的问题,那么带来的弊端可能导致数据的不连续性。

    索引文件(Index,严格来讲是一个主键文件)

           索引文件假设用过DB的就应该比較好理解,不理解的能够參考字典的文件夹,重点能够看一下整个索引文件文件格式:

           从上图能够看出索引文件的大小是固定的,由索引文件标识数据文件起始偏移索引使用状态三部分组成,总共是13bytes。这里略微解释一下数据文件起始偏移,事实上就是一个定位数据详细位置的标志位,一般查找假设走索引的话会先找到详细的索引项,然后依据数据文件起始偏移找到详细的数据位置。这里为什么会有一个索引使用状态,由于索引的分配是不走Manager管理的(理论上也是要走Manager管理),可是这里为了简单。另外我这里的索引严格意义来讲是主键,由于索引文件标识ID仅仅能表示一个int类型。和真实的索引格式有一定的差别。


    数据文件

           接下来就是最复杂的数据文件存储格式了,先上一张图再进行解释

             一个数据空间由固定的29bytes+数据长度组成,也就是说申请256bytes容量的空间有29bytes是得不到利用的。就如我上图中所出的,因为每一个空间都是定长的,可是不一定每一个数据都仅仅申请一个空间,这里举一个简单的样例:300bytes字节的数据会申请一个256bytes空间和一个128bytes的空间,我在这里定为A和B,而在物理上这两个空间极有可能是不连续的。那么我在A找到了一部分数据,然后再去B找到余下的数据,再做一个拼接,一个完整的数据就出来了。那么这里就能够用链式存储的方式进行,也能够用另外的方式进行,比方说在某个地方存储一个完整数据的全部空间地址。

    详细操作数据

            上面讲到了一些数据的存储协议,那么接下来就会讲怎样存储数据删除数据更新数据

    Insert数据

            这里先以一张图来看看整个Insert的过程:

             从上面的的流程中能够看出insert大概有四步:
             格式化数据:由于一般数据都会有一定的格式,这里就须要定义详细的格式,比方说数据中Table的概念。这样才干够依照约定进行格式化。只是就最后转化的结果而言是一个byte[],可是详细格式化的过程会有一些讲究,比方说一个String,你必须知道这个String的长度,就是相应到byte[]数组的详细哪一段。其它定长类型好一些,比方说int,就是存储4个字节,long就存储8个字节。
             申请存储空间:这一步在上面讲到了一些,经过格式化数据后能够知道当前存储数据的大小,那么能够依照这个大小进行分配一些合适的空间。
             写数据文件:拿到存储空间,就把byte[]往里面填就好了。
             写索引文件:严格意义来讲我这里并不是索引而是主键,并且我这里的索引都是固定的int类型,相对来说狭隘了一些。

    Delete数据

              我这里仅仅能依据ID(索引文件中面的ID)进行删除,採用其它列进行删除的方式眼下还是不支持的。

            
             查找索引:事实上我这里的查找都简化成了简单的依据ID进行查找,假设依据其它字段进行查找就不是这样了。详细的查找算法会在后面Select数据给出。
             查找数据:找到了索引(主键)后就能够依据这个索引找到详细的数据地址,然后把这些空间地址置为可用(详细看一下Manger文件格式)
             回收索引:这个时候索引文件的使用状态就派上用场了。

    Update数据

            这里我的update策略是会先删除一条记录,然后再插入一条记录进行处理,这种优点是简单方便,由于本身的更新也涉及到空间的回收一重用。且我在这里格式化数据没有细化到数据库字段的概念,全部无法按字段进行管理。

    Select数据

           在删除数据里面就会用到依据ID进行查找数据。从索引文件的特性能够知道索引文件是有序的,那么就能够依据这个特性进行一些查找算法,我这里採用的查找算法就是"跳跃表"算法,这个算法的原理事实上就是改变步长 。详细能够參考《跳跃表实现索引检索》,这里就不再做详述。
      

    总结

      上面我写的存储应该是最简单的,对于学习存储的原理还是非常有帮助的。比方说索引,空间的预分配,为什么要这么做会有一个非常好的认识。由于非常多存储,比方说memcached都是採用这样的方式进行分配空间。尝试着自己实现也是一种帮助自己理解的手段。


  • 相关阅读:
    elasticsearch 数据迁移
    elasticsearch使用简介 -安装篇
    docker 使用笔记
    PHP 全局变量
    做人做事需牢记20条原则
    MYSQL 存储引擎概述
    postgresql常用命令
    ORACLE 清理SYSAUX表空间
    sqlserver数据库的启动
    postgressql启动与关闭
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/4296085.html
Copyright © 2011-2022 走看看