1 基本介绍
1.1 设计思想
数据的存储可划分为active和inactive两大类,active数据是小部分,会频繁访问,使用更高性能的底层存储介质进行存储;inactive的数据是全集,使用廉价的存储介质存储。这种分冷热的思想与CPU的多级缓存、操作系统的cache、各类软件系统的软件缓存是相通的,但ceph通过软件层面的配合,充分利用不同的硬件介质和软件存储模式,为上层用户提供透明统一的访问接口。
目的:在兼顾存储成本前提下,通过配合使用不同的存储介质,并使用不同的软件存储模式,达到更好的数据存取性能。
说明:cache tier是ceph在其抽象的ObjectStore层面实现的冷热分层,上层的RGW/CEPHFS/RBD均可受益,其中RGW本身还可据此实现生命周期功能。
1.2 实现要点
- CRUSH Map:通过指定不同的root,并创建多个相应的crush_rule,用来从软件上定义如何使用底层存储介质
- pool的crush_rule:创建不同存储池,为各自设置对应的crush_rule
- pool的存储策略:通过crush_rule的类型来指定软件层面的存储模式,包括多副本(副本数可配)、EC(K、M可配)
- tier关联:动态配置两个存储池的关联关系和迁移策略,包括none、writeback、forward、readonly、readforward、proxy、readproxy
- Objecter:屏蔽底层实现,为客户端的读写请求提供统一接口
1.3 总体架构
ceph官网提供的tier存储技术的总体架构如下图:
对于设置了cache tier的存储池,客户的读写请求是无感的,通过单独的objecter模块负责实现cache tier和storage tier的联动,并根据管理员配置的各项cache tier的策略,自动实现数据的更新和同步。
1.4 特殊性
- 适用于热点访问场景
添加tier之后,能够提升总体的性能与具体的数据存取场景高度依赖,因为需要将热存储池的数据迁移到冷存储池,因此对访问小部分热点数据的场景比较适合,绝大多数请求只会访问一小部分数据。 - 通用场景性能较差且benchmark困难
通用的非热点访问的场景下,都不是cache友好的,都会有性能损失。另外通用的benckmark方法无法发挥tier的优势,不会只访问一小部分的数据,因此性能数据也表现不佳。 - 对象遍历操作不稳定
由于tier层的小部分热数据的存在,如果用户直接调用librados库的对象遍历API,可能会得到不一致的结果,但是直接使用RGW/RBD/CephFS没有影响。 - 复杂性提升
使用tier后,会为Rados层带来额外的复杂性,可能会增加出现bug的风险。
2 功能实现分析
cache tier是在ceph抽象的ObjectStore层面,处于底层的统一的KV存储层,其具体实现依附于ceph的存储池,cache tier作为存储池的属性,依据用户配置,存储池在完成具体的读写操作时提供相应的cache tier功能。ceph将存储池进行分片(PG),在具体实现功能时又是基于PG为最基本的单位来实现。
2.1 参数设计
cache tier的参数是存储池的属性,底层数据结构定义为pg_pool_t:
struct pg_pool_t { ... typedef enum { CACHEMODE_NONE = 0, /// no caching CACHEMODE_WRITEBACK = 1, /// write to cache, flush later CACHEMODE_FORWARD = 2, /// forward if not in cache CACHEMODE_READONLY = 3, /// handle reads, forward writes [not strongly consi CACHEMODE_READFORWARD = 4, /// forward reads, write to cache flush later CACHEMODE_READPROXY = 5, /// proxy reads, write to cache flush later CACHEMODE_PROXY = 6, /// proxy if not in cache } cache_mode_t; ... set uint64_t tiers; /// pools that are tiers of us int64_t tier_of; /// pool for which we are a tier(-1为没有tier) // Note that write wins for read+write ops int64_t read_tier; /// pool/tier for objecter to direct reads(-1为没有tier) int64_t write_tier; /// pool/tier for objecter to direct write(-1为没有tier) cache_mode_t cache_mode; /// cache pool mode uint64_t target_max_bytes; /// tiering: target max pool size uint64_t target_max_objects; /// tiering: target max pool size uint32_t cache_target_dirty_ratio_micro; /// cache: fraction of target to leave dirty uint32_t cache_target_dirty_high_ratio_micro; /// cache: fraction of target to flush with high speed uint32_t cache_target_full_ratio_micro; /// cache: fraction of target to fill before we evict in earnest uint32_t cache_min_flush_age; /// minimum age (seconds) before we can flush uint32_t cache_min_evict_age; /// minimum age (seconds) before we can evict HitSet::Params hit_set_params; /// The HitSet params to use on this pool uint32_t hit_set_period; /// periodicity of HitSet segments (seconds) uint32_t hit_set_count; /// number of periods to retain bool use_gmt_hitset; /// use gmt to name the hitset archive object uint3