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 uint32_t min_read_recency_for_promote; /// minimum number of HitSet to check before promote on read uint32_t min_write_recency_for_promote; /// minimum number of HitSet to check before promote on write uint32_t hit_set_grade_decay_rate; /// current hit_set has highest priority on objects ///temperature count,the follow hit_set's priority deca ///by this params than pre hit_set uint32_t hit_set_search_last_n; /// accumulate atmost N hit_sets for temperature bool is_tier() const { return tier_of >= 0; } bool has_tiers() const { return !tiers.empty(); } void clear_tier() { tier_of = -1; clear_read_tier(); clear_write_tier(); clear_tier_tunables(); } bool has_read_tier() const { return read_tier >= 0; } void clear_read_tier() { read_tier = -1; } bool has_write_tier() const { return write_tier >= 0; } void clear_write_tier() { write_tier = -1; } void clear_tier_tunables() { if (cache_mode != CACHEMODE_NONE) flags |= FLAG_INCOMPLETE_CLONES; cache_mode = CACHEMODE_NONE; target_max_bytes = 0; target_max_objects = 0; cache_target_dirty_ratio_micro = 0; cache_target_dirty_high_ratio_micro = 0; cache_target_full_ratio_micro = 0; hit_set_params = HitSet::Params(); hit_set_period = 0; hit_set_count = 0; hit_set_grade_decay_rate = 0; hit_set_search_last_n = 0; grade_table.resize(0); } ... };
這些參數詳細記錄了cache tier的具體實現細節,總結如下:
- tier的存儲池:自身的tier pool ID(
tier_of
字段),以及以自身作爲tier的其他pool的ID集合(tiers
字段) - cache mode:定義了六種cache模式,默認爲
CACHEMODE_NONE
,按照用戶配置執行相應的操作,爲cache tier實現的核心部分 - cache tunable參數:在執行flush和evict操作時,用戶可爲cache這兩種行爲設置的多種觸發參數,以便和具體的應用場景匹配
2.2 實現主體
基於爲cache tier定義的各項參數,在具體實現的時候,依附於PG爲主體實現具體的功能。pg_pool_t
作爲底層存儲的定義,屬於PGPool
的info
字段,用來記錄一個存儲池的詳細屬性。PG
類定義了在存儲池上完成最基本數據讀寫的實體,包含了一個它所屬的存儲池的pool
字段。
struct PGPool { CephContext* cct; epoch_t cached_epoch; int64_t id; string name; uint64_t auid; pg_pool_t info; ... }; class PG : public DoutPrefixProvider { ... protected: PGPool pool; ... };
PG
類定義了針對全部數據請求的所有功能,主要包括shard分片、recovery和backfill功能、狀態收集和統計、blocked請求的等待處理、scrub處理等功能,對於具體的數據讀寫請求的處理,定義了必須進一步實現的全部抽象接口,其中所有請求的入口接口如下:
virtual void do_request(OpRequestRef& op, ThreadPool::TPHandle &handle) = 0;
目前的PG針對數據讀寫請求的全部實現都由PrimaryLogPG
類負責,這裏主要關注cache tier相關的具體實現。
2.3 Objecter
Objecter本身實現上是一個Dispatcher,對外可作爲OSD的一個client,負責向tier的storage層的存儲池讀寫數據;對內作爲本存儲池的OSD的一個成員,負責在
各個cache mode的具體實現中,生成具體的Operation OP並提交。
2.3.1 註冊與啓動
OSD的main函數創建了7個messenger用來處理各類網絡rpc消息,其中包括爲Objecter創建對獨立messenger。OSD類爲一個Dispatcher,負責實現網絡處理的各個接口,其包含一個OSDService對象,負責完成除網絡交互之外的具體實現。Objecter爲OSDService的一個成員,由OSDService在構造時動態創建Objecter對象,並在構造函數調用Objecter的init函數初始化Objecter的內部數據結構。
在對象創建完成之後,main函數啓動這7個messenger,此時還不能無法處理網絡請求,隨後調用OSD的init函數,其負責將其OSDService成員的Objecter成員註冊到相應messenger的dispatcher隊列:
objecter_messenger->add_dispatcher_head(service.objecter);
同時,也會將OSD自身添加到其他messenger的Dispatcher隊列,並完成一系列的如初始化MON和MGR的client對象、啓動OSD的多個工作線程池、啓動tick等初始化操作,其中包括連接MON進行鑑權,在鑑權通過之後調用OSDService成員的final_init函數,負責啓動Objecter:
void OSDService::final_init() { objecter->start(osdmap.get()); }
2.3.2 Tick與OSDSession
Objecter的start函數用來啓動objecter服務,其工作內容爲:啓動tick並拷貝一份osdmap。tick的間隔時間由配置文件制定:objecter_tick_interval
,默認爲5秒。Objecter會通過拷貝的osdmap信息,主動向其他目標OSD發起連接,並以內嵌類OSDSession表示,本身維護了一個osd ID到OSDSession的映射結構map<int,OSDSession*>
。OSDSession的成員包括目標OSD的ID、底層網絡連接等基礎信息之外,主要包括到目標OSD的各類OP操作的隊列:
struct OSDSession { ... map ops; map linger_ops; map command_ops; ... int osd; ConnectionRef con; ... };
每一次tick的任務就是遍歷Objecter維護的OSDSession映射結構,循環檢查它的OP隊列,包括普通的OP隊列、linger OP隊列、command OP隊列,依據如下標準執行是否發送依次MPing消息:
- 普通OP:配置的
objecter_timeout
,默認10秒,依據OP的開始時間是否超過這個超時時間來決定是否發送 - linger OP(執行比較緩慢的操作):只要存在於隊列中就會發送
- command OP:只要存在於隊列中就會發送
2.3.3 ObjectOperation
Objecter負責爲cache tier的具體實現中需要與其他存儲池的OSD進行數據交互時提供支持,其本質就是執行一系列ObjectStore抽象的操作,併爲cache tier封裝一些特定的操作,這部分統一由ObjectOperation類負責完成,實現非常直接:
struct ObjectOperation { vector ops; int flags; int priority; vector out_bl; vector out_handler; vector out_rval; };
一共就上述6個成員,最重要的就是OSDOp構成的數組,對於一個具體的操作,可能對應多個OSDOp操作,或者可以將多個操作的OP合併後發送,另外包含一個標誌位和優先級;同時定義了三個同樣與OSDOp對應的輸出信息的數組,包括輸出內容、輸出handler、返回值。
在此基礎上,使用一個add_op
函數添加一個新的OSDOp成員到內部的ops
數組中,並返回引用,再定義了大量的針對Object的具體操作,用來支持cache tier的具體實現,主要分爲如下幾類:
- ObjectStore抽象的對象操作:如
stat
、read
、write
、append
、getxattr
、omap_get_keys
、omap_get_values
等 - PG和Scrub操作:包括
pg_ls
、scrub_ls
,用來從其他OSD獲取PG、Scrub信息 - 針對cache tier特殊操作:包括
is_dirty
、undirty
、hit_set_ls/get
、copy_get
、copy_from
、cache_flush/try_flush
、cache_evict
、cache_pin
、cache_unpin
針對cache tier的操作爲實現cache tier的不同模式提供了便利,其中flush和evict直接影響cache tier的行爲,其具體處理方式如下:
- flush操作:若給定的cache tier中的對象是dirty的,就將其寫入到backing tier;如果對象是clean,則不做任何操作。當該操作與update併發執行時,
cache_flush
將會阻塞update操作,cache_try_flush
則會立即返回一個EAGAIN錯誤而不會阻塞。 - evict操作:如果給定的cache tier中的對象是clean則將其從cache tier中刪除,否則返回EBUSY。
另外提供的cache_pin
、cache_unpin
還可以將某個對象在cache tier中進行鎖定和解鎖。
3.3.4 具體實現
Objecter要主動向其他OSD發起各類OP操作,封裝了具體的方法,包括write
、read
、pg_read
、getxattr
等,每個方法使用的具體OP,通過上述ObjectOperation類封裝的工廠方法來構造,創建好之後調用統一的op_submit
函數進行提交,最終從維護的OSDSession映射結構中獲取底層網絡連接,調用其send_message
方法將OP發送出去。對於read
方法,其具體實現如下:
Op *prepare_read_op( const object_t& oid, const object_locator_t& oloc, ObjectOperation& op, snapid_t snapid, bufferlist *pbl, int flags, Context *onack, version_t *objver = NULL, int *data_offset = NULL, uint64_t features = 0, ZTracer::Trace *parent_trace = nullptr) { Op *o = new Op(oid, oloc, op.ops, flags | global_op_flags | CEPH_OSD_FLAG_READ, onack, objver, data_offset, parent_trace); o->priority = op.priority; o->snapid = snapid; o->outbl = pbl; if (!o->outbl && op.size() == 1 && op.out_bl[0]->length()) o->outbl = op.out_bl[0]; o->out_bl.swap(op.out_bl); o->out_handler.swap(op.out_handler); o->out_rval.swap(op.out_rval); return o; } ceph_tid_t read( const object_t& oid, const object_locator_t& oloc, ObjectOperation& op, snapid_t snapid, bufferlist *pbl, int flags, Context *onack, version_t *objver = NULL, int *data_offset = NULL, uint64_t features = 0) { Op *o = prepare_read_op(oid, oloc, op, snapid, pbl, flags, onack, objver, data_offset); if (features) o->features = features; ceph_tid_t tid; op_submit(o, &tid); return tid; } void Objecter::op_submit(Op *op, ceph_tid_t *ptid, int *ctx_budget) { shunique_lock rl(rwlock, ceph::acquire_shared); ... _op_submit(op, rl, ptid); } void Objecter::_op_submit(Op *op, shunique_lock& sul, ceph_tid_t *ptid) { ... MOSDOp * m = _prepare_osd_op(op); ... _send_op(op, m); } void Objecter::_send_op(Op *op, MOSDOp *m) { ... op->session->con->send_message(m); ... }
Objecter作爲可與其他OSD進行數據讀寫交互的模塊,除了發送OP之外,還需接收響應,因此實現了Dispatcher的全部接口,重新定義ms_dispatch
函數來處理其他OSD返回給自己的信息。通過接收的消息類型進行分發,只支持如下幾種消息:
CEPH_MSG_OSD_OPREPLY
:普通的OSD讀寫等OP操作的響應,可進行fast dispatch,調用handle_osd_op_reply
處理CEPH_MSG_OSD_BACKOFF
:執行BACKOFF的消息,調用handle_osd_backoff
處理CEPH_MSG_WATCH_NOTIFY
:可進行fast dispatch,調用handle_watch_notify
處理MSG_COMMAND_REPLY
:僅在發送消息的源也是OSD時纔會處理,調用handle_command_reply
處理,否則不處理MSG_GETPOOLSTATSREPLY
:獲取存儲池統計信息的返回,調用handle_get_pool_stats_reply
處理CEPH_MSG_POOLOP_REPLY
:執行存儲池操作的返回,調用handle_pool_op_reply
處理CEPH_MSG_STATFS_REPLY
:獲取文件系統信息的返回,調用handle_fs_stats_reply
處理CEPH_MSG_OSD_MAP
:交互OSDMAP信息的處理,調用handle_osd_map
處理
這裏我們主要關注第一類消息,就是針對OSD普通的讀寫等OP操作的響應對處理,具體流程如下:
- 從維護的OSDSession映射結構中獲取該消息對應的OSDSession
- 判斷是否需要重試,如果需要就從OSDSession中刪除這個OP,並重新調用
_op_submit
提交該請求 - 獲取消息的結果,判斷是否需要進行redirect,如果需要就從OSDSession刪除該OP,並添加新的redirect目的地後調用
_op_submit
重新提交 - 獲取消息的輸出數據、返回值,並據此調用這個OP創建時註冊的處理完成時的回調handler
2.4 IO請求
由於cache tier依附於PG實現,因此cache tier的IO請求路徑就是對OSD的任意一次IO請求路徑,在這個路徑上會判斷PG所在的存儲池是否是其他存儲池的cache tier或是否有其他存儲池爲自身的cache tier,如果均沒有則執行正常的讀寫請求;否則就依據具體的策略並基於Objecter提供的方法來完成對cache tier的處理。
2.5 各cache mode處理
對於配置了cache tier存儲池,按照3.4節點IO請求路徑,具體在maybe_handle_cache_detail
函數中,依據不同的cache模式進行switch-case分別進行處理, 在maybe_handle_cache_detail
函數中具體分爲如下六種模式依次處理,詳見下述分析。
2.5.1 FORWARD
FORWARD模式表示所有到達cache tier存儲池的請求都不會處理,直接將它的後端存儲池的ID回覆給請求方,並返回-ENOENT
的錯誤號,具體實現比較簡單。
該模式的用途是在刪除WRITEBACK
模式的cache tier時,需將其cache mode先設置爲FORWARD,並主動調用cache tier的flush和evict操作,確保cache tier存儲池的對象全部evict和flush到後端存儲池,保證這個過程中不會有新的數據寫入。
2.5.2 READONLY
READONLY模式是指對於所有的寫請求,都直接調用do_cache_redirect
函數,與FORWARD模式同樣處理;對於所有的讀請求,會先判斷是否存在於cache tier存儲池中,如果存在就直接返回,否則會先調用Objecter從後端存儲池讀取一份數據,並創建一個ObjectContext對象保存,將讀取數據返回給客戶。
start_copy
會創建一個CopyOp對象,該對象保存了請求的參數、返回值、大小、數據緩衝區、omap和xattr緩衝區等,最終調用OSDService的Objecter成員的read方法向目標存儲池的OSD發起讀取請求。之後調用wait_for_blocked_object
將該OP加入到內部維護的一個稱爲waiting_for_blocked_object
的map結構中,key爲tid,value爲OP。start_copy
在發起請求之前會設置好成功時的回調函數,這個回調函數會調用kick_object_context_blocked
用來從維護的map結構中查詢到之前的OP,調用requeue_ops
將這個OP加入到OSD的請求隊列中重新執行,並從map結構中刪除。注意:requeue_ops
會使用enqueue_front
插入到OSD的ShardedOpWQ的開頭。
2.5.3 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模式的區別。
2.5.4 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
2.5.5 READFORWARD
READFORWARD是FORWARD與WRITEBACK模式的綜合。對於所有讀請求執行與FORWARD一樣的處理,調用do_cache_redirect
,直接返回後端存儲池給用戶,並返回-ENOENT
的錯誤號;對於寫請求則與WRITEBACK模式相同處理,先調用promote_object
從後端讀取一份數據保存並加入到OP隊列重新執行,把數據寫入到cache tier存儲池中。
2.5.6 READPROXY
READPROXY是PROXY與WRITEBACK模式的綜合。對於所有讀請求執行與PROXY模式一樣的處理,調用do_proxy_read
,僅從後端存儲池讀取數據並返回給用戶,不存儲讀取到的對象;對於寫請求則與WRITEBACK模式相同處理,先調用promote_object
從後端讀取一份數據並保存並加入到OP隊列重新執行,把數據寫入到cache tier存儲池中。
3 總結
cache tier是ceph在底層抽象的統一軟件定義存儲ObjectStore的基礎上,實現的一種緩存存儲層,具體依託於ceph的CRUSH MAP和CRUSH RULE,針對不同的存儲池配置相應的規則,從而映射到具有存儲性能差異的硬件上,從而提升總體性能並降低存儲成本。cache tier的實現具體是在底層的存儲池層面,因此對於所有基於ObjectStore抽象實現的上層應用都可受惠,包括RGW、CEPHFS、RBD。cache tier本質上可以看做CPU與內存間的高速緩存的一個外延,當數據從內存要寫入磁盤時,通過使用性能較高的磁盤作爲cache tier層,來彌補內存與普通磁盤間的性能gap,該思想雖然也有如文件系統的page cache、磁盤本身的緩存等實現,但ceph的cache tier相當於是從軟件定義存儲的角度來對這種思想進行補充。
3.1 實用性
根據上述分析,cache tier雖然提供了六種模式,但FORWARD本身是一種爲WRITEBACK切換提供的過渡模式,並無實用價值。PROXY模式則完全充當一個代理來轉發請求,同樣並無太大的實用價值。另外四種模式,最重要的就是WRITEBACK模式,這是cache tier實現的最具實用性的模式,在這種模式下配置相應的cache tier參數控制其行爲。另外三種READONLY、READPROXY、READFORARD三種是針對讀請求的比例不同而提供的針對性模式。下面總結了他們的實用場景:
- WRITEBACK:寫請求較多或讀寫比較均勻的場景,讀寫的數據都會在cache tier存儲池中保存,通過配置flush、evict參數執行向後端存儲池的更新和剔除
- READONLY:適合以讀爲主的場景,對於極少量讀寫請求直接讓客戶端去寫入到後端存儲池,而讀請求則會將對象緩存起來,提升總體讀請求的性能
- READPROXY與READFORWARD:適合以寫爲主的場景,對於少量的讀請求,可以選擇使用proxy方式進行處理,也可以選擇讓客戶端直接寫入到後端存儲池
3.2 侷限性
cache tier的實現本身依附於存儲池的分片,也就是PG,而PG本身要實現包括副本同步、快照、scrub、recovery等一系列功能,cache tier的實現與他們都混合在一起,大大增加了整體代碼實現的複雜性,增加了在極端場景出現bug的可能性,從而對系統穩定性會有一定的影響。因此對於cache tier功能本身需要進行嚴格的測試,同時對於開啓了cache tier的存儲池,也需要謹慎的配置各項參數,嚴密的進行監控。
cache tier提供的多種模式雖然針對不同場景而設置,但並不全面,還有進一步開發的空間。首先可借鑑CPU多級緩存的思想,ceph可支持超過兩級的cache tier的配置,但是由於目前的代碼實現將其混合在PG中,這種擴展難度很大改動也非常大。其次,對於寫請求較少的場景,僅提供了READONLY模式,也可提供類似CPU緩存的write through的模式,同時也還有write allocate等處理方式,均可借鑑,但這些擴展都受目前的實現方式的制約。