Fatcache学习笔记

简介: Fatcache学习笔记

前言

秋招差不多结束了,基本确定了之后去的公司,没了笔试面试和实习,最近也就闲了下来。


打算和朋友一起做点开源项目,暂定方向就是数据存储这块。于是这两周也是在学数据存储相关的知识,比如SSD、底层文件系统、lsm-tree等等。


我一直以来都有将我学过的东西记录下来的习惯,不过最近都是以知识脑图的形式记录在wps里。考虑到我已经很久没有发布过技术博文了,我还是打算将我最近学的整理一下,以笔记的形式记录在博客当中,方便日后回顾,同时也是分享出来一起学习。


NOTE:该文很多内容都摘自git-hulk。我也是通过他整理的资料去学习fatcache,侵删。


简介

fatcache是来自Twitter, 基于SSD上面实现的cache, 使用mc的协议,数据存储在SSD (Ps:memcached是将数据放在内存中)。 fatcache的数据放在SSD(其实机械盘也可以,只是性能不佳), 所以相对于内存cache, 如memcached、redis,能容纳更多数据。


主要特性:


通过slab管理数据,跟memcached类似,但稍有不同

通过索引查找数据

单线程

针对写SSD做了优化

一、slab分配机制

1.背景知识

SSD特性

SSD由于硬件的特性,改写前需要擦除(erase),擦除的单位一般是128个page(512KB),每个擦除单元称为块(block)。也就是说即时改写的数据只有几个比特,也必须重写整个block,造成了写的浪费,而这种现象也就是我们平时所说的写放大。


内存和硬盘读写速度

内存读写速度快,容量小,价格贵。

硬盘读写速度慢,容量大,价格较内存便宜。


其中要注意的是,两者读写速度是数量级的存在。所以很多时候在设计程序时,往往就是将一些热数据放在内存中来加快读写。


2.slab

因为之前讲SSD读写特性问题,同时也是为了方便内存和磁盘空间的管理,fatcache将内存和磁盘分为一块块的slab。每当需要空间来存储键值对时,fatcache都会找到一个能满足其空间需求的slab,将其中的一个item分配给它。


3.slab class

由于并不是所有的kv的长度都是一样的,这就需要不同item长度的slab, 而slabclass就是这些 不同长度item的slab的集合。在一般设计slabclass的时候,可以把slabclass看作一个item长度递增 的数组,并把slab放到对应的队列中。当在添加数据的时候,找到能容纳kv长度的slabclass,然后从未使用完或者空闲的slab, 分配一个item空间。


如slabclass有n级, 假设每个slab是1M, 第一级item长度是100byte, 每级增长因子是1.2。


第一级item的长是100bytes, item count = 1M/100


第二级item的长度是100byte*1.2 = 120byte, item count = 1M/120


第三级item的长度是120*1.2 = 144bytes, item count = 1M/144


以此类推.


当需要申请80byte的内存时候, 从第一级开始找,直到遇到第一个比它大的slab, 这里就是第一级的slab。 如果要申请130bytes的内存, 就是找到第三级。


PS:可以理解为slab根据大小分成了不同类型,而这个类型就是slab class


4.memory and disk slab

fatcache的slab来自两个地方,一个是内存,另一个是磁盘。在启动的时候,可以指定内存slab大小, 默认是64M, 我们也会指定磁盘分区。然后再把内存和磁盘的空间,切分为一块块slab, 再添加到free slab队列。 fatcache的内存slab, 充当的角色是写缓存,每次写的时候,一定是写在内存slab, 当写入时,发现没有内存slab, 就将最老的slab刷入磁盘。


5.slab在fatcache的使用

看过上面的内容之后,应该能知道slab大概的样子,以及Slabclass是干嘛的。 slabcloass每一级slab可以分配 的item长度和数量是固定的, 所以slab可能会有三种状态:


free slab(完全没有使用)

partial slab(部分使用),

full slab(全部使用).

最开始, 由于没开始使用,只会有free slab, 当使用一段时间后,就转换为partial slab, 如果这个slab已经被分配完, 则会变为full slab.


在fatcache中, 会维护这三种状态的slab, 其中free slab, full slab队列是slabclass所有级公用,partial slab是每一级都有一个 队列。


当某一级需要分配空间, 会经历下面的步骤:


如果还有内存slab未写满,直接分配地址,返回。

如果所有内存slab已经写满, 从内存full slab队列头部,剔除一个slab, 交换到磁盘slab,空出内存slab, 重新分配

6.slab空洞问题

fatcache分配时每次都是从Slab末端开始分配。也就是说,如果item删除, 我们并不会重新利用,相当于这个item只有在内存slab耗尽时,刷到磁盘slab才会重新利用,这样这个带有空洞的slab 被刷到磁盘,也就是磁盘的数据也会有空洞, 不仅仅是内存刷到磁盘的slab造成空洞,直接删除磁盘slab里的item,也会有空洞,造成空洞的过程类似下面:

q11.png

对于内存空洞,解决方案可以,对每个slab增加一个队列,记录空洞地址,使用时,优先从空洞队列分配,无空洞时, 从slab后面分配。同时放回slab时,如果是最后一个分配,直接放回slab, 否则放到空洞队列。


NOTE: 上面说的是针对内存空洞,不包含磁盘slab,因为fatcache主要是使用磁盘slab, 如果对每个磁盘slab, 增加空洞队列,可能会耗掉不少内存,需要提前评估。


注:这个应该并不是个“问题”,而是个trade-off的design。如果跟原作者所说的去额外维护一个free-slot-lits来track这些位置,即放弃了原设计的append的策略,带来的问题就是新和旧的数据被写入到同一个slab中,这样的混合的slab如果在生命周期特征比较明显的负载上,原始的FIFO的效果肯定就差很多了。尤其是在用大容量的SSD做backend的背景下,space cost应该是个弱考虑因素。 (grayxu on 2021/11/3)


二、索引机制

1.为什么要有索引机制?

fatcache的数据大部分在磁盘,只有小部分在内存中。查找一个key, 如果没有索引,每次都要到磁盘做查找,效率很低, 而且会有多次磁盘读。有了索引之后,在索引记录key, 以及数据所在的位置。查找时,只需判断索引是否存在,如果存在, 直接根据记录的磁盘位置,读取数据,最多只有一次读IO, 如果内存中找不到索引,说明key不存在,直接返回空。


2.索引数据结构

我们可以先来看一下索引(itemx)的定义,在fc_itemx.h里面


struct itemx {
    // 队列指针
    STAILQ_ENTRY(itemx) tqe;    /* link in index / free q */
    // key的sha1加密值,如果sha1一样,认为是同一个key.
    uint8_t             md[20]; /* sha1 message digest */
    // 这个key对应value的slab id.
    uint32_t            sid;    /* owner slab id */
    // 这个ke对应value在slab里面的偏移位置
    uint32_t            offset; /* item offset from owner slab base */
    // 我们协议看到的cas unique值。
    uint64_t            cas;    /* cas */
} __attribute__ ((__packed__));


存放索引的数据结构类似于Java中的HashMap,原理是hash+链表。

q1.png


fatcache在启动的时候,会根据设定索引的最大空间,然后把索引全部初始化放到索引的空闲列表。


当set一个key-value的时候,fatcache会从索引的空闲队列中,拿出一个索引,然后记录key的sha1加密值以及数据存放的位置。


当get一个key的时候, 先把key转为sha1值,然后从索引表里面找到索引项,然后读到数据。


当删除一个数据,只需要把索引直接干掉。不需要真正的删除数据,下次过来就不知道索引,也会找不到数据,空出的空间会重新利用


3.数据的查找过程

由key找到索引itemx且没有过期

根据索引中sid找到slab info,根据slab info中mem字段判断item是在内存中还是磁盘中

然后根据sid所在slab class id,计算item长度, 从内存或者disk读取到数据,返回

PS:索引数量是用户可配置的,如果索引耗完,就会通过slab_evict来把老的slab剔除,回收索引


总结

fatcache是一款很不错的针对SSD开发的存储软件,其特点如下:


单线程, 实现更加简单,但性能没多线程的好

无随机写,通过slab管理,转化为顺序写,减少小块IO写,无写放大

随机读, 读性能没有写性能好

索引管理, 快速判断数据是否存在,同时可以快速定位数据的位置,最多只有一次IO

slab分为内存和磁盘两种, 读写磁盘是direct io,不会使用pagecache, 内存slab更多是写缓冲的角色

fatcache很适合用在那些对于数据响应时间并不是要求太高,最好是介于全内存和DB之间,同时数据量比较大的场景。


对我而言,fatcache的slab分配机制和索引机制是非常有借鉴意义的。

相关文章
|
Go 开发者
Go语言并发模型概览:CSP模型解析
【2月更文挑战第17天】Go语言以其强大的并发处理能力在编程领域崭露头角。其中,CSP(Communicating Sequential Processes)模型作为Go语言并发模型的核心之一,在并发编程中发挥着至关重要的作用。本文将深入解析CSP模型的基本原理及其在Go语言中的应用,帮助读者更好地理解Go语言的并发编程特性。
|
JSON 前端开发 JavaScript
前端常用的加密方式
前端常用的加密方式
435 0
|
Linux
Linux使用Aria2命令下载BT种子/磁力/直链文件
Linux使用Aria2命令下载BT种子/磁力/直链文件
2177 0
|
存储 固态存储 索引
搜索和推荐统一存储层的新进展和思考
我们在2017年统一了搜索和推荐场景下的HA3、iGraph、RTP和DII四大引擎的存储层(参见统一之战),帮助它们取得了的更迅速的迁移能力、更快速的数据恢复能力和更丰富的数据召回能力。 最近一年来,我们在统一的存储框架上又做了进一步的演进,下面将分别从架构、Build服务以及存储模型角度介绍我们的新进展和思考。   1.架构   在我们的传统架构(参见统一之战)中,
3197 0
|
SQL 运维 Oracle
Oracle 超时设置2:设置实例级参数
Oracle超时设置系列的第二篇文章,设置实例级参数
952 0
|
11月前
|
人工智能 自然语言处理 数据可视化
OneCode 接入 DeepSeek:开启代码开发新纪元
OneCode 接入 DeepSeek,带来自然语言聊天式基础建模、本地代码工程无缝结合、图生代码功能全线升级及 AI 模型代码直接导入等新特性,极大提升开发效率与体验。预计第二季度推出开源版本,进一步推动开发者社区的开放与创新。这些改进使开发更加高效、便捷和智能,助力代码开发进入新纪元。
|
安全 数据中心
数据中心服务器机架是什么
数据中心服务器机架是用于容纳服务器、存储器等IT设备的结构,旨在提升数据中心的管理与运营效率。常见的类型包括开放式机架、封闭式机柜和壁挂式机架,每种类型各有特点,适用于不同的场景需求。选择时需考虑尺寸、承重、冷却效率及安全性等因素,以确保最佳的使用效果。
1135 4
|
11月前
|
JavaScript 前端开发 数据安全/隐私保护
Vue Router 简介
Vue Router 是 Vue.js 官方的路由管理库,用于构建单页面应用(SPA)。它将不同页面映射到对应组件,支持嵌套路由、路由参数和导航守卫等功能,简化复杂前端应用的开发。主要特性包括路由映射、嵌套路由、路由参数、导航守卫和路由懒加载,提升性能和开发效率。安装命令:`npm install vue-router`。
|
存储 缓存 监控
Memcached介绍和详解
Memcached介绍和详解
742 3
|
关系型数据库 MySQL 数据库
MySQL数据库—查询:关联查询(一篇教会你在多表关联下查询数据)
MySQL数据库—查询:关联查询(一篇教会你在多表关联下查询数据)
1219 0