ceph缓存分层

缓存分类

writeback(热数据)

WRITEBACK模式是最复杂也是最有实用价值的模式,其具体实现会按照请求类型、cache状态综合判断,并复用前三种模式下的一些处理细节进行综合处理。处于这种模式下的cache tier存储池,其处理流程如下:

判断cache tier存储池的状态是否已满,如果已满,则对于读请求直接调用do_proxy_read,对于写请求直接将OP加入到waiting_for_cache_not_full队列,并在下一次有新的请求达到时重新放入OP队列处理。

在cache tier未满的情况下,先判断是否必须进行promote,如果需要就调用promote_object,先阻塞当前请求,从后端存储池读取一份数据到cache tier存储池,并在完成之后再将当前请求加入OP队列

在cache tier未满且不会强制promote时,这也是最常见的情况下:对于写入请求,会先阻塞,调用promote_object从后端读取一份数据并保存,完成之后将当前请求重新加入OP队列,这样下一次执行这个读请求时,会判断已经存在于cache tier中,就直接写入在cache tier存储池中;对于读请求,则会首先调用 do_proxy_read从后端存储池读取数据但不保存在cache tier存储池中,之后再判断本地读请求是否需要跳过promote,这是在创建该OP时设置的一个flag,通过op->need_skip_promote来判断,在所有的OP请求中有两种场景会设置不需promote,否则都会执行promote从后端存储池读取一份数据保存在cache tier存储池

其中会设置skip promote标志的两种情况如下:
CEPH_OSD_OP_DELETE请求会设置skip promote
read、sync_read、sparse_read、checksum、writefull请求若设置了CEPH_OSD_OP_FLAG_FADVISE_NOCACHE或CEPH_OSD_OP_FLAG_FADVISE_DONTNEED
标志位就会设置skip promote

这种模式适合于大量修改的数据应用场景(例如图片视频编辑, 联机事务处理类应用),适合”热”数据。

forward

FORWARD模式表示所有到达cache tier存储池的请求都不会处理,直接将它的后端存储池的ID回复给请求方,并返回-ENOENT的错误号,具体实现比较简单。

该模式的用途是在删除WRITEBACK模式的cache tier时,需将其cache mode先设置为FORWARD,并主动调用cache tier的flush和evict操作,确保cache tier存储池的对象全部evict和flush到后端存储池,保证这个过程中不会有新的数据写入。

readonly(冷数据)

READONLY模式是指对于所有的写请求,都直接调用do_cache_redirect函数,与FORWARD模式同样处理;对于所有的读请求,会先判断是否存在于cache tier存储池中,如果存在就直接返回,否则会先调用Objecter从后端存储池读取一份数据,并创建一个ObjectContext对象保存,将读取数据返回给客户。

这种模式比较适合数据一次写入,多次读取的应用场景。例如图片,视频, 音频等。适合”冷”数据。

readforward

READFORWARD是FORWARD与WRITEBACK模式的综合。对于所有读请求执行与FORWARD一样的处理,调用do_cache_redirect,直接返回后端存储池给用户,并返回-ENOENT的错误号;对于写请求则与WRITEBACK模式相同处理,先调用promote_object从后端读取一份数据保存并加入到OP队列重新执行,把数据写入到cache tier存储池中。

proxy

PROXY模式下,针对读写请求都会执行proxy,也就是作为一个代理向后端存储池发起请求并返回给客户端,除非强制要求先进行promote操作。

对于写请求调用do_proxy_write,则会直接调用会调用OSDService的Objecter成员的mutate方法,将写请求直接写入到后端的存储池中,并记录到内部维护的proxywrite_ops、in_progress_proxy_ops两个map结构,另外设置了成功时的回调函数,在写入完成之后从维护的map结构中删除,并返回给客户端CEPH_OSD_FLAG_ACK | CEPH_OSD_FLAG_ONDISK的响应。对于读请求调用do_proxy_read,与写请求处理类似,直接作为代理端发送请求到后端存储池并等待结果完成,同样也会分别记录到两个map结构并在完成时删除。

这种模式下,读写请求的对象的数据都不会在cache tier存储池中保存,自身扮演为一个代理(proxy)的角色,这是与FORWARD模式的区别。

readproxy

READPROXY是PROXY与WRITEBACK模式的综合。对于所有读请求执行与PROXY模式一样的处理,调用do_proxy_read,仅从后端存储池读取数据并返回给用户,不存储读取到的对象;对于写请求则与WRITEBACK模式相同处理,先调用promote_object从后端读取一份数据并保存并加入到OP队列重新执行,把数据写入到cache tier存储池中。

部署搭建(主要为writeback、readonly)

参数说明

hit_set_type
描述: 启用缓存存储池的命中集跟踪
有效值:bloom, explicit_hash, explicit_object
默认值:bloom ,其他是用于测试

hit_set_count
描述:为缓存存储池保留的命中集数量。此值越高, ceph-osd 守护进程消耗的内存越多
有效值:整数
默认值:1. Agent doesn’t handle > 1 yet

hit_set_fpp
描述:bloom 命中集类型的假阳性概率
有效值:Double 0.0 - 1.0
默认值:0.05

hit_set_period
描述:为缓存存储池保留的命中集有效期。此值越高, ceph-osd 消耗的内存越多
有效值:整数
默认值:3600 1hr

target_max_bytes
描述:达到 max_bytes 阀值时 Ceph 就回写或赶出对象
有效值:整数
默认值:1000000000000 #1-TB

target_max_objects
描述:达到 max_objects 阀值时 Ceph 就回写或赶出对象
有效值:整数
默认值:1000000 #1M objects

cache_min_flush_age
描述:达到此时间(单位为秒)时,缓存代理就把某些对象从缓存存储池刷回到存储池
有效值:整数
默认值:600 10min

cache_min_evict_age
描述:达到此时间(单位为秒)时,缓存代理就把某些对象从缓存存储池赶出
有效值:整数
默认值:1800 30min

cache_target_dirty_ratio
描述:缓存存储池包含的脏对象达到多少比例时就把它们回写到后端的存储池
有效值:Double
默认值:.4

cache_target_full_ratio
描述:缓存存储池包含的干净对象达到多少比例时,缓存代理就把它们赶出缓存存储池
有效值:Double
默认值:.8

cache_target_dirty_high_ratio
描述:缓存存储池内包含的已修改(脏的)对象达到此比例时,缓存层代理就会更快地把脏对象刷回到后端存储池
有效值:Double
默认值:.6

添加配置cache tier

缓存池:
创建缓存池的方式和标准存储池一样,区别在于缓存层使用了高性能的磁盘并且有自己的规则集。

后端存储池可以为:
Standard Storage(标准存储): 这情景下,存储池中会复制一个对象的多个副本。
Erasure Coding(纠删编码): 这种情景下,存储池会使用纠删编码来保证数据的有效性,会牺牲一部分性能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 创建一个缓存层
ceph osd tier add {storagepool} {cachepool}

# 设置缓存模式
ceph osd tier cache-mode {cachepool} {cache-mode}

# 客户端的流量从存储池重定向到缓存池
ceph osd tier set-overlay {storagepool} {cachepool}

# 配置参数
ceph osd pool set {cachepool} hit_set_type bloom
ceph osd pool set {cachepool} hit_set_count 1
ceph osd pool set {cachepool} hit_set_period 3600

ceph osd pool set {cachepool} target_max_bytes 1000000000000
ceph osd pool set {cachepool} target_max_objects 1000000

ceph osd pool set {cachepool} cache_target_dirty_ratio 0.4
ceph osd pool set {cachepool} cache_target_dirty_high_ratio 0.6
ceph osd pool set {cachepool} cache_target_full_ratio 0.8

ceph osd pool set {cachepool} cache_min_flush_age 600
ceph osd pool set {cachepool} cache_min_evict_age 1800

删除缓存层

writeback

因为writeback缓存层可能会有脏数据,你必须按照以下的步骤来确保你不会丢失任意在缓存中的脏对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 修改缓存模式为forward,任何新的写入对象会直接写入后端存储
ceph osd tier cache-mode {cachepool} forward/proxy

# 确认缓存池中所有数据都被刷新,这通常会花费一段时间
rados -p {cachepool} ls

# 如果缓存池中还有对象,可以手动进行刷新
rados -p {cachepool} cache-flush-evict-all

# 移除重新向,客户端不会直接发送流量到缓存
ceph osd tier remove-overlay {storagetier}

# 最后,从后端存储移除缓存池
ceph osd tier remove {storagepool} {cachepool}

readonly

因为只读缓存层中不存储脏数据,可以直接禁用和移除

1
2
3
4
5
6
7
8
# 设置缓存模式为none进行禁用
ceph osd tier cache-mode {cachepool} none

# 移除重新向,客户端不会直接发送流量到缓存
ceph osd tier remove-overlay {storagetier}

# 从后端存储移除缓存层
ceph osd tier remove {storagepool} {cachepool}

测试用例

测试组网环境

服务器:dell X86
Cpu: Intel(R) Xeon(R) CPU E5-2630 v3 @ 2.40GHz 2物理核数 24逻辑核
内存:128G
硬盘:12盘位 4T硬盘 (1个槽位用于系统盘)
网口:万兆网口 * 1

Ceph环境:(node51 node52 node53 node54 node55)
Mon:node155 node156 node157
Mgr:node155 node156 node157
Mds:node155 node156 node157
Osd:11 SATA * 3 , SSD * 4
网口:66网段

池环境:(bluestore)
index :sata规则 2副本 64pg 64pgp
data : sata规则 2副本 1024g 1024pgp
cachepool : ssd规则 2副本 256pg 256pgp

rgw:
单节点出rgw (node155)

工具:
cosbench

no cache tier

| 用例 | cosbench 用例 | latency(ms) | iops(op/s) | bandwidth(MB/s) | 磁盘使用率 | CPU负载 |
| —————- | ———— |———— | ———————— | ———– |———– |
| 64K write | c:u(1-100),o:u(1-3000),div(o) 3000worker | 1302 | 2007 | 127.31 |98 |20 |
| 64K read | c:u(1-100),o:u(1-3000),div(o) 3000worker | 120 | 4013 | 260 |10 |10 |

writeback

由于只采用4个SSD,故提升的效果并不明显,但可以看出时延确实下降了,后台查看流量,也基本往cachepool写

| 用例 | cosbench 用例 | latency(ms) | iops(op/s) | bandwidth(MB/s) | 磁盘使用率 | CPU负载 |
| —————- | ———— |———— | ———————— | ———– |———– |
| 64K write | c:u(1-100),o:u(1-3000),div(o) 3000worker | 246 | 2322 | 154 |98 |22 |
| 64K read | c:u(1-100),o:u(1-3000),div(o) 3000worker | 112 | 4100 | 267 |5 |10 |

readonly

| 用例 | cosbench 用例 | latency(ms) | iops(op/s) | bandwidth(MB/s) | 磁盘使用率 | CPU负载 |
| —————- | ———— |———— | ———————— | ———– |———– |
| 64K write | c:u(1-100),o:u(1-3000),div(o) 3000worker | 1534 | 1662 | 105 |99 |21 |
| 64K read | c:u(1-100),o:u(1-3000),div(o) 3000worker | 123 | 4085 | 264 |2 |10 |

测试结论

1、添加缓存池能大大减少请求的时延,提高的相对性能取决于cache池的ssd数量,一般情况下,cache池容量需要为data池的 1/10 或 1/8.

2、缓存池的应用可能不仅仅在加速,可能还可以应用于迁移方案,具体的需要调研。

3、readonly 模式由于不存在脏数据,可以上单副本,提升cache池的性能。

4、对于缓存穿透,cache采用了布隆过滤器(用于判断存在),缓存雪崩可通过自带crush算法进行故障恢复适当规避,故障无法恢复时需进一步研究。

缓存穿透是指访问某个数据时,cache中没有,且data池也没有,查询是失败,当用户多的时候,同时访问这个不存在的数据,就会给data池很大的压力。

缓存雪崩指的是 缓存层出现了错误,不能正常工作了,大量的请求会往data池压,导致data池存在挂掉的情况。