基于可持久化内存-AEP的KV存储引擎

一份简单的AEP上手指南

QuicklyStart

  • 切换到测试程序目录
cd ../judge
  • 通过judge程序对KV存储进行一些测试
**
-s :set size per Thread.
-g :get size per Thread.
-t :num threads.
**./judge -s 10000 -g 10000 -t 5

上面的命令就是5个线程并发,每个线程在纯写阶段写入10000条数据;在纯读阶段,每个线程执行10000次Get请求。单线程请求数量不超过1000万。

示例:

./judge -s 10000000 -g 10000000 -t 16
Init config block size:64 block per segments:65536
---------------Performance Test-------------
pure write time:31558.22 mspure read time:4527.57 ms
---------------Correctness Test  -------------
sum sample: 160000
right: 160000
wrong: 0

其中17.49为纯写入耗时,3.77为纯读的时间

设计思想

AEP简述

什么是AEP?

持久化内存(Persistent Memory,简称 PMEM),也叫非易失性内存(Non-Volatile Memory,简称 NVM),是指一类支持字节寻址(byte-addressable)、可以通过 CPU 指令直接进行操作、断电后数据不丢失的存储硬件。

AEP的两种使用模式

Optane DIMM 有两种工作模式:Memory Mode 和 App Direct Mode。

  • Memory Mode 简单说就是把 Optane DIMMs 当成易失性内存使用,把 DRAM 当作 CPU 和 Optane DIMMs 之间的 cache,并且 DRAM 对外不可见(就像 CPU 的多级 cache 对外也不可见)。基于 Memory Mode 的工作模式,可以通过应用无感知的方式,解决一些内存数据库(比如 Redis、Memcached)单机 DRAM 容量不足或成本过高的问题。
  • App Direct Mode 将 Optane DIMMs 当成一个持久化设备来使用,直接通过 CPU 指令读写 Optane DIMMs,不需要经过 DRAM。应用可以使用能够感知持久化内存的文件系统(比如 EXT4-DAX、XFS-DAX、NOVA)或其他组件(比如 PMDK)来管理、操作持久化内存设备。

显而易见本项目中使用的是App Direct Mode,即将aep挂载到文件系统中使用。

PMDK工具库

正如开题所说,在App Direct Mode模式下,我们可以简单的把AEP作为非常快的硬盘使用,使得应用可以不用做任何变动直接迁移上去,但是这样的不足之处就是无法充分发挥AEP的性能。为了能够对AEP的使用进行细力度的优化,Interl官方提供了一套工具库-PMDK。PMDK 的全称是 Persistent Memory Development Kit,它包含了 Intel 开发的一系列旨在 方便非易失性内存的应用开发的函数库和工具。

数据结构

整体的KV设计采用的是哈希的结构,主要分为内存部分和AEP部分,这里注意没有涉及到硬盘。整体的思想是采用了数组加链表的形式来实现哈希结构。此外为了降低实现难度,我们将key设定为定长,val为80-1024byte的变长字符串

内存中的数据结构

内存中的数据结构主要分为两个部分:1. entry 2. key的索引信息;两者都是以数组的形式。

Entry数组
其主要是为了保存链表的头部,示例代码如下:

class Entry {public:Entry() : head_(UINT32_MAX){};~Entry() = default;KEY_INDEX_TYPE getHead() const {return this->head_.load(std::memory_order_relaxed);}// set head and return the old oneKEY_INDEX_TYPE setHead(const uint32_t _key_index) {return this->head_.exchange(_key_index, std::memory_order_relaxed);}public:std::atomic<KEY_INDEX_TYPE> head_;
};

每个enrty中只保存了链表头的位置,head其指向的是头节点的KEY在key的索引信息中的位置并通过atomic来保证更新的一致性。每个enrty占用4个字节.

key的索引信息
上面已经提及key的索引信息,同样是以数组的形式保存的,其中每个节点主要保存了以下几点信息,这里我们以伪代码的形式说明以下:

typedf struct KeyInfo {uint_16 version_;uint_16 val_length_;uint_32 block_index_;uint_32 next_index_;char[16] key_;
}

成员变量说明:

  • version_:版本号,主要用于在recovery的时候,去除过时的数据。
  • val_length_:value的长度,主要用于val的读取,以及aep内存的回收。
  • block_index_:这个kv在内存中的block索引,关于block的定义在后面。
  • next_index_:链表中下一个key的位置。
  • key_:key值。

AEP中的数据结构

这里采用的是key、val在内存中组装成record,一次写入AEP并执行持久化操作的思想。

为什么要这么做呢?

假设我们将所有的key/value分成两个部分存,首先显而易见的是会增加对于aep的io,而显而易见的是DRAM本身还是要快于AEP的;此外,为了保证单条写入的原子性需要执行两次,而aep的持久化函数pmem_drain,本身相当于一个内存屏障,降低了并发。

目前一条Record在AEP中的表示如下

图一. Record结构

  • VAL_LEN:value的长度
  • KEY:key值
  • VERSION:版本号
  • VALUE:value值
  • CHECK_SUM:主要用于recovery

断电恢复

考虑到value为定长字段为不定长的字段,为了实现一次写入请求的原子性,我采取了对写入数据计算checksum的方法,将checksum与其它相关数据一并写入,做到单次kv写入只需要执行一次持久化操作。具体的恢复流程如下:

图二. 计算CHECK_SUM

  • 首先在内存中组装Record,然后计算如图二所示中字符串的hash值作为CHECK_SUM一并写入。
  • 目前,AEP被划分为以Block为最小单位的内存块,而内存的申请与回收都是以Block为基本单位。因此在恢复的时候,我们遍历所有Block对取出的一条数据计算CHECK_SUM,与Record中记录的进行对比,如果相同则通过校验。

AEP的GC设计

如果所示,我们将AEP内存主要分为了三个层次:File、Segment、Block。

图三. AEP内存结构

FILE

如上文所讲,我们采用的是AD模式,即将AEP以硬盘的模式挂载到文件系统。因此,我们首先建立一个文件,同时以PMDK提供的pmem_map映射到内存,这样我们便可以直接对文件进行操作。而GC的目的正是为了更好的管理File内部的aep内存。

Segment

线程级内存管理器-AepMemoryControllerGlobalMemoryController申请aep内存的基本单位。后面会介绍这两者。

Block

GC逻辑中管理的最小单位,为一个固定的size的空间,即对于一条Record的申请aep内存的请求,AepMemoryController会根据大小分配出n个block大小的空间。在官方的文档中,我们可以了解到如下两点:

  • cache line size between memory controller and the Optane DIMM is 64 bytes,
  • the actual physical access granuality is 256 bytes.
    因此,我们默认每个block的size为64byte,这样保证数据都是按照64对齐。

为了提高并发效率,对于AEP的GC策略,如图五所示,我们采用的是threal local + global的方式。这里存在两个管理器:AepMemoryControllerGlobalMemoryController。其中GlobalMemoryController主要是负责管理整个File的内存,而AepMemoryController则是被线程所有,负责线程级别的内存管理。

图五. **AepMemoryController**和**GlobalMemoryController**

在介绍两者之前我们先介绍以下FreeList。

FreeList

FreeList负责维护的是碎片化内存的管理,提供Push和Pop接口。目前其内存实现比较简单,主要是维护了一个hashmap的结构存储了不同大小的Block。代码如下:

class FreeList {public:FreeList() = default;virtual ~FreeList() = default;virtual void Push(BLOCK_INDEX_TYPE _block_index, size_t _size) = 0;virtual bool Pop(BLOCK_INDEX_TYPE* _block_index, size_t _size) = 0;bool ThreadSafePop(BLOCK_INDEX_TYPE* _block_index, size_t _size) {bool flag;mt.lock();flag = Pop(_block_index, _size);mt.unlock();return flag;}virtual void MergeTo(FreeList* src_free_list, FreeList* dst_free_list) = 0;
};

GlobalMemoryController

GlobalMemoryController主要是负责管理整个File的内存,其主要职责如下:

* 负责对于AepMemoryController的allocate请求分配一个Segment

接口如下:

bool Allocate(BLOCK_INDEX_TYPE* _block_index)

* 负责维护一个全局的freelist

在执行recovery的时候,会将碎片化的内存存储到全局的FreeList中。

* 在内存紧张时可以提供一个扮演内存的角色,负责将内存中的大对象存储到AEP中

首先来说,AEP本身存在两种模式,memory模式与AD模式。对于AD模式,如果我们没有执行持久化操作,即pmem_persist,其效率其实与memory存在差别但是不大。因此考虑如下情景:

  • 内存使用非常紧张
  • kv存储此时以读请求为主

这时我们可以将内存中的大对象,放到AEP中申请保存。此外,在recovery的时候,因为校验CHECK_SUM存在,这段用于起临时作用的aep内存会被GlobalMemoryController回收。

AepMemoryController

AepMemoryController主要是线程级别的资源,主要包括两个方面:一个segment的连续内存和一个FreeList管理的碎片化内存。

// 申请AEP内存
bool New(int _size, BLOCK_INDEX_TYPE* _index)// 释放AEP内存
bool Delete(int _size, BLOCK_INDEX_TYPE _index)
  • _size: 申请Block的数量
  • _index: 返回的block index

AEP内存分配的优先级

根据优先级排列如下:

  1. AepMemoryController-连续内存
  2. AepMemoryController-碎片内存
  3. GlobalMemoryController-连续内存
  4. GlobalMemoryController-碎片内存

Reference

  • Aep的结构介绍:https://software.intel.com/content/www/us/en/develop/videos/overview-of-the-new-intel-optane-dc-memory.html
  • PMDK的介绍:https://pmem.io/pmdk/
  • Yang J , Kim J , Hoseinzadeh M , et al. An Empirical Guide to the Behavior and Use of Scalable Persistent Memory[J]. arXiv, 2019.
  • 官方的编程指南《Programming Persistent Memory》

源码

https://github.com/AndyShen105/aep-kv

基于可持久化内存-AEP的KV存储引擎相关推荐

  1. 基于淘宝开源Tair分布式KV存储引擎的整合部署

    一.前言 Tair支撑了淘宝几乎所有系统的缓存信息(Tair = Taobao Pair,Pair即Key-Value键值对),内置了三个存储引擎:mdb(默认,类似于Memcache).rdb(类似 ...

  2. 性能提升2.58倍!阿里最快KV存储引擎揭秘

    简介: 阿里云智能数据库Tair团队主要负责自研分布式键值存储(KVS)系统,几乎涵盖了淘宝.天猫.阿里妈妈.菜鸟.钉钉.优酷.高德等阿里巴巴所有核心业务.十多年来,始终如一为阿里业务提供着高可靠.高 ...

  3. 从零实现一个 k-v 存储引擎

    写这篇文章的目的,是为了帮助更多的人理解 rosedb,我会从零开始实现一个简单的包含 PUT.GET.DELETE 操作的 k-v 存储引擎. 你可以将其看做是一个简易版本的 rosedb,就叫它 ...

  4. 超级轻量级: KV存储引擎实现

    Hi (๑╹◡╹)ノ",各位Gopher本人最近又用Go造了一个轮子,一个超级轻量级KV存储引擎,欢迎

  5. golang kv存储引擎

    GitHub - k-si/CaskDB: K-V database based on Bitcask, but has some magic changes 跪求star OTZ Bitcask模型 ...

  6. 基于持久内存的 单机上亿(128B)QPS -- 持久化 k/v 存储引擎

    文章目录 性能数据 设计背景 设计架构 Hash 索引结构 及 PMEM空间管理形态 基本API 及 实现 API 初始化流程 写流程 读流程 删除流程 PMEM Allocator设计 主要组件 空 ...

  7. 美团点评:基于Druid的Kylin存储引擎实践

    作者:康凯森 作者简介:美团大数据工程师,Apache Kylin Committer,目前主要负责美团 OLAP 系统(Kylin & Druid & Palo)的平台化建设. 8月 ...

  8. mysql 内存引擎_MySQL常见的三种存储引擎(InnoDB、MyISAM、MEMORY)

    MySQL是我们经常使用的数据库处理系统(DBMS),不知小伙伴们有没有注意过其中的"存储引擎"(storage_engine)呢?有时候面试题中也会问道MySQL几种常用的存储引 ...

  9. 存储引擎 boltdb 的设计奥秘?

    作者 | 奇伢 来源 | 奇伢云存储 etcd 的存储 etcd v3 是使用的持久化存储来存储它的 kv 数据,etcd  存储的是非常核心的元数据信息,所以最重要的是稳定.使用的是 boltdb ...

最新文章

  1. 2019年中国科创板全面解读报告
  2. java中使用grpc和protobuf
  3. mysql在mac上的坑
  4. 【kafka】kafka_2.11-1.1.0 配置 SASL_PLAINTEXT 认证方式
  5. 《精解 Windows 10》——2.10 触摸手势
  6. iTunes Connect(一) —— iOS应用上架到AppStore
  7. 73本免费的、语言无关的优秀的编程书籍
  8. uinty 为什么一旋转鼠标镜头就倒了_Unity3D使用鼠标旋转缩放平移视角
  9. SQL Server2017 安装错误:Verify that you have sufficient access to that key
  10. 考UCPM产品经理证书是否值得?
  11. python opencv 凸包 convexHull
  12. 怎样用计算机放音乐,QQ音乐怎么用手机控制电脑放歌曲,qq音乐怎么连接电脑|qq音乐怎么用手机控制电脑...
  13. DDR存储原理,AC/DC判别标准深度解析(官方手册整理)
  14. Linux性能分析方法
  15. 通达信地量指标公式 启涨地量买点选股指标天眼地量指标
  16. 2015异常问题解决方案经验总结(一)
  17. Linux grep -v 命令排除输出
  18. 2023微软校招开始啦,快来找我内推吧
  19. vim 清空文本内容指令
  20. 使用RedisTemplate批量存入数据,100万测试 需要1分钟

热门文章

  1. 第3课用计算机处理信息,第3课 用计算机处理信息 课件.
  2. Liquibase逆向生成Changelog.xml
  3. IATF16949认证咨询——IATF16949质量管理体系文件编制检查辅导
  4. html5 3d全景代码,HTML5教程 三维全景详解
  5. 短信验证码原理java_[java发送短信验证码原理]java发送短信验证码
  6. python打包程序在win10不能运行、点击无反应_Windows10平台用PyInstaller打成exe程序后不能运行请高手指点...
  7. 城市大脑标准体系层次结构
  8. 论文复现:模拟风电不确定性——拉丁超立方抽样生成及缩减场景(Matlab全代码)
  9. java面向对象三大特性理解
  10. 计算机有60进制吗,60进制介绍