Kahadb设计思想

 

简介

  • hakadb是activemq的持久化数据库,作为消息队列的存储,每个消息有一个消息ID,提供了对消息的快速的查找,更新,以及消息的事物支持,以及意外磬机之后的恢复。丰富的功能决定了他在存储结构上与redis中简单的双端队列不同。
 

kahadb三大部分

  • 主要由索引(B-Tree Index),数据(PageFile),日志(Journal)组成。为了实现消息的快速定位更新,通过b-tree来索引数据,主要对应目录中的;为了数据的写入效率,数据都是顺序存储在文件中,这部分工作被抽象到了PageFile类;Journal负责了操作日志,用于写入和恢复最终都是决定与Journal
 

kahadb之Journal

 

核心类

 

Journal 读写日志的facade

  • write方法,向日志系统写入日志。返回Location。
  • read方法,根据Location从之日系统中读取日志
 

Location 日志系统中定位日志Record

  • 由日志的DataFileId,offset,size,type组成,用来定位日志
 

DataFile 日志文件的存储文件

日志都存储在db-{1,2,3,...n}.log文件中,每个这个文件都由DataFile类来封装,其中1,2,3..就是DataFileId,还有一些长度和文件指针的信息

 

DataFileAppender 多线程下优化了批量写入

  • 两个线程,一个队列,把并发写变成批量单线程写入,具体实现略
 

DataFileAccessorPool和DataFileAccessor

  • 封装了对日志的读操作,暂时略,对理解整体结构没有大的影响。
 

读写实例

 
  1. File dir = new File("target/tests/Test");
  2. dir.mkdirs();
  3. Journal dataManager = new Journal();
  4. dataManager.setDirectory(dir);
  5. dataManager.start();
  6. //写入日志
  7. ByteSequence write = new ByteSequence("Hello world".getBytes());
  8. Location location = dataManager.write(write, true);
  9. //读取日志
  10. ByteSequence read = dataManager.read(location);
  11. System.out.println(new String(read.data));
 

存储结构

  • 由头部28个字节,和数据组成
 

头部

  • 四个字节Journal.BATCH_CONTROL_RECORD_SIZE, 固定的28
  • 一个字节Journal.BATCH_CONTROL_RECORD_TYPE, 2
  • 十一个字节,字符串 Journal.BATCH_CONTROL_RECORD_MAGIC, WRITE BATCH 这几个字节
  • 四个字节记录批量写入了多少批的BATCH_SIZE ,不固定的int
  • 八个字节校验和,后半部分写入日志的校验和
 

数据

  • 四个字节的size,这个size是数据头部的长度+数据的长度
  • 一个字节数据类型
  • 若干字节的数据
 

日志文件的RecoveryCheck

  • 在第一次加载日志文件的时候会做一次Recovery,主要是恢复一些上次写入的Offset,知道了存储结构以后其实按照这个结构读写就可以找到最后一次写的Offset,但是假如日志写入一半失败到了,导致部分存储结构不整齐了,但后续是好的,理想情况应该跳过这些坏的部分,让后面的可用。Recovery的时候对这种情况会重新在文件里搜索日志的头部的前16字节,不知为何是要搜这头部,搜到以后就把之前搜索过的区域标记为corruptedBlocks
 

kahadb之PageFile

 

核心类

 

PageFile

PageFile负责了消息的持久化存储。PageFile对外的主要接口有删除数据库,载入数据库,生成一个用于读写的Transaction,数据被存储在了

 

Page

PageFile如果是用于存储数据的文件,Page就是文件中的每一小段,为了减少IO,每个文件被分成4K大小的段,是一个Page。至于为什么4K大小可以减少磁盘IO的原因是,当用户程序请求要读1K的数据时候,操作系统会预读更多的数据,放到内核缓冲中,因为内核中的缓冲一页的大小是4K,所以应用程序一般都会和操作系统的PageCache对齐,这样充分利用操作系统的PageCache。Page最重要的一个属性是PageId,这个Id是在一个PageFile中顺序排列的,所以根据这个PageId就可以定位到PageFile文件中的Offset。

 

Transaction

Transaction负责了创建Page,在Page上读写,提交以及回滚操作

 

读写实例

 
  1. File dir = new File("target/tests/Test");
  2. dir.mkdirs();
  3. PageFile pf = new PageFile(dir, "myfile");
  4. pf.load();
  5. //写入
  6. Transaction tx = pf.tx();
  7. Page<String> page = tx.allocate();
  8. page.set("Hello world");
  9. tx.store(page, StringMarshaller.INSTANCE, true);
  10. tx.commit();
  11. pf.unload();
  12. pf.load();
  13. //读取
  14. tx = pf.tx();
  15. tx.load(page.getPageId(), StringMarshaller.INSTANCE);
  16. String hello = page.get();
 

存储结构

  • 存储的时候包括{dbname}.data和{dbname}.redo {dbname}.free文件。data文件是保存真正消息的文件。当data文件META信息中的cleanShutdown为false的时候会通过redo文件来做recovery。
 

.data文件

 

头部

  • 4K的第一页存放META信息
  • META初始信息如下
 
  1. fileType=org.apache.activemq.store.kahadb.disk.page.PageFile
  2. pageSize=4096
  3. freePages=-1
  4. cleanShutdown=true
  5. metaDataTxId=0
  6. fileTypeVersion=1
  7. lastTxId=0
  • 需要说明一点的是这个信息在第一页中存储了两遍
 

BODY部分

  • 一个字节的type 固定的是2
  • 八个字节的transactionId
  • 八个字节的下一个记录的位置
  • 四个字节的校验和,不过是个保留字节,没什么用
  • N个字节真正的数据
 

.redo文件

 

头部

  • 八个字节的下一个TX的id 存储在第一页
  • 八个字节的校验和 存储在第一页
  • 四个字节的当前这个写入的记录数量 存储在第一页
 

BODY部分

  • 8个字节PageId
  • page数据与data文件中的body部分相同
  • pageId和page数据是一页的大小4K
 

PageFile的多级缓存

  • 可以看到当写入数据的时候分配新的Page,Page写入磁盘是比较消耗IO的,所以在一个Transaction上有一个freeList来保存空的Page,在PageFile也有freeList来加速新分配Page。同时为了加快读写Page的速度,PageFile中有一个PageCache
 

事物的回滚

  • 事物只有在commit的时候才会调用PageFile的write方法,Transaction中的store只是把他逻辑保存在Transaction中,但是回滚的时候要把PageFile中申请的Page归还到freeList

kahadb设计的更多相关文章

  1. kafka系列教程2(设计构造及原理1)

    kafka采用了一些非主流(unconventional)并经过实践的设计使其高效和可扩展.在实际使用中kafka显示出了相对于常见流行的消息系统的优越性.并且每天能够处理上百GB的新的数据.   类 ...

  2. KahaDB简介

    ActiveMQ 5.3以后,出现了KahaDB.她是一个基于文件支持事务的消息存储器,是一个可靠,高性能,可扩展的消息存储器.     她的设计初衷就是使用简单并尽可能的快.KahaDB的索引使用一 ...

  3. 如何一步一步用DDD设计一个电商网站(九)—— 小心陷入值对象持久化的坑

    阅读目录 前言 场景1的思考 场景2的思考 避坑方式 实践 结语 一.前言 在上一篇中(如何一步一步用DDD设计一个电商网站(八)—— 会员价的集成),有一行注释的代码: public interfa ...

  4. 如何一步一步用DDD设计一个电商网站(八)—— 会员价的集成

    阅读目录 前言 建模 实现 结语 一.前言 前面几篇已经实现了一个基本的购买+售价计算的过程,这次再让售价丰满一些,增加一个会员价的概念.会员价在现在的主流电商中,是一个不大常见的模式,其带来的问题是 ...

  5. 设计爬虫Hawk背后的故事

    本文写于圣诞节北京下午慵懒的午后.本文偏技术向,不过应该大部分人能看懂. 五年之痒 2016年,能记入个人年终总结的事情没几件,其中一个便是开源了Hawk.我花不少时间优化和推广它,得到的评价还算比较 ...

  6. 如何一步一步用DDD设计一个电商网站(十)—— 一个完整的购物车

     阅读目录 前言 回顾 梳理 实现 结语 一.前言 之前的文章中已经涉及到了购买商品加入购物车,购物车内购物项的金额计算等功能.本篇准备把剩下的购物车的基本概念一次处理完. 二.回顾 在动手之前我对之 ...

  7. 如何一步一步用DDD设计一个电商网站(一)—— 先理解核心概念

    一.前言     DDD(领域驱动设计)的一些介绍网上资料很多,这里就不继续描述了.自己使用领域驱动设计摸滚打爬也有2年多的时间,出于对知识的总结和分享,也是对自我理解的一个公开检验,介于博客园这个平 ...

  8. 如何一步一步用DDD设计一个电商网站(七)—— 实现售价上下文

    阅读目录 前言 明确业务细节 建模 实现 结语 一.前言 上一篇我们已经确立的购买上下文和销售上下文的交互方式,传送门在此:http://www.cnblogs.com/Zachary-Fan/p/D ...

  9. 如何一步一步用DDD设计一个电商网站(六)—— 给购物车加点料,集成售价上下文

    阅读目录 前言 如何在一个项目中实现多个上下文的业务 售价上下文与购买上下文的集成 结语 一.前言 前几篇已经实现了一个最简单的购买过程,这次开始往这个过程中增加一些东西.比如促销.会员价等,在我们的 ...

随机推荐

  1. Redis主从、哨兵、集群的简单区别

    2018-10-26 主从:读写分离,备份哨兵:监控,自动转移,选主集群:数据 hash 分片,同时包含主从及哨兵特性

  2. 【Tensorflow】 Object_detection之liunx服务器安装部署步骤记录

    环境:centos7+anaconda python3.6 步骤: 1.下载Models cd 到预存放目录下,执行: git clone https://github.com/tensorflow/ ...

  3. MyISAM的前缀压缩索引在索引块中的组织方式

    纯粹自己的理解,哪位大佬看到了还请指正. 首先贴一张<高性能MySQL>中的一段话: 这句话的意思是说,MyISAM使用b+树组织索引.也就是说无论索引压缩与否,组织方式一定是B+树. 下 ...

  4. setContentView和inflate区别

    一般用LayoutInflater做一件事:inflate inflate这个方法总共有四种形式(见下面),目的都是把xml表述的layout转化为View对象.其中有一个比较常用,View infl ...

  5. TOJ 3248 Flip Game

    Description Flip game is played on a rectangular 4x4 field with two-sided pieces placed on each of i ...

  6. MySQL搭建Amoeba_读写分离

    一.背景知识 Amoeba(变形虫)项目,专注 分布式数据库 proxy 开发.座落与Client.DB Server(s)之间.对客户端透明.具有负载均衡.高可用性.sql过滤.读写分离.可路由相关 ...

  7. nginx反向代理使用网址速度变慢

    最近公司网址加载静态文件的速度总是跟不上于是试着用带端口的ip来访问, 发现速度快不少于是将nginx的代理修改为ip的如: location / { proxy_pass http://localh ...

  8. 关于FileFOutputStream应用中的FileNotFoundException问题

    在使用fileoutputstream时经常出现FileNotFoundException问题,即便是同一个程序(可行)改了一下包名再重新编译,就会无缘无故的抛出FileNotFoundExcepti ...

  9. c# 字体库跨域解决

    网上大部分的资料说的都是在apache和ng服务器的情况下解决方案,但基本的思路都是添加响应头 场景: 页面引用css文件: <link href="http://www.tuohua ...

  10. 二维数组针对某字段排序 - array_multisort()

    /** * 针对二维数组下的某字段排序 * @param array $myarr 被排序数组 * @param string $sort_key 排序根据字段 * @param flag $sort ...