ceph的 cache tier實現分析

1 基本介紹

1.1 設計思想

數據的存儲可劃分爲active和inactive兩大類,active數據是小部分,會頻繁訪問,使用更高性能的底層存儲介質進行存儲;inactive的數據是全集,使用廉價的存儲介質存儲。這種分冷熱的思想與CPU的多級緩存、操作系統的cache、各類軟件系統的軟件緩存是相通的,但ceph通過軟件層面的配合,充分利用不同的硬件介質和軟件存儲模式,爲上層用戶提供透明統一的訪問接口。

目的:在兼顧存儲成本前提下,通過配合使用不同的存儲介質,並使用不同的軟件存儲模式,達到更好的數據存取性能。
說明:cache tier是ceph在其抽象的ObjectStore層面實現的冷熱分層,上層的RGW/CEPHFS/RBD均可受益,其中RGW本身還可據此實現生命週期功能。

1.2 實現要點

  1. CRUSH Map:通過指定不同的root,並創建多個相應的crush_rule,用來從軟件上定義如何使用底層存儲介質
  2. pool的crush_rule:創建不同存儲池,爲各自設置對應的crush_rule
  3. pool的存儲策略:通過crush_rule的類型來指定軟件層面的存儲模式,包括多副本(副本數可配)、EC(K、M可配)
  4. tier關聯:動態配置兩個存儲池的關聯關係和遷移策略,包括none、writeback、forward、readonly、readforward、proxy、readproxy
  5. Objecter:屏蔽底層實現,爲客戶端的讀寫請求提供統一接口

1.3 總體架構

ceph官網提供的tier存儲技術的總體架構如下圖:

對於設置了cache tier的存儲池,客戶的讀寫請求是無感的,通過單獨的objecter模塊負責實現cache tier和storage tier的聯動,並根據管理員配置的各項cache tier的策略,自動實現數據的更新和同步。

1.4 特殊性

  1. 適用於熱點訪問場景
    添加tier之後,能夠提升總體的性能與具體的數據存取場景高度依賴,因爲需要將熱存儲池的數據遷移到冷存儲池,因此對訪問小部分熱點數據的場景比較適合,絕大多數請求只會訪問一小部分數據。
  2. 通用場景性能較差且benchmark困難
    通用的非熱點訪問的場景下,都不是cache友好的,都會有性能損失。另外通用的benckmark方法無法發揮tier的優勢,不會只訪問一小部分的數據,因此性能數據也表現不佳。
  3. 對象遍歷操作不穩定
    由於tier層的小部分熱數據的存在,如果用戶直接調用librados庫的對象遍歷API,可能會得到不一致的結果,但是直接使用RGW/RBD/CephFS沒有影響。
  4. 複雜性提升
    使用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作爲底層存儲的定義,屬於PGPoolinfo字段,用來記錄一個存儲池的詳細屬性。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抽象的對象操作:如statreadwriteappendgetxattromap_get_keysomap_get_values
  • PG和Scrub操作:包括pg_lsscrub_ls,用來從其他OSD獲取PG、Scrub信息
  • 針對cache tier特殊操作:包括is_dirtyundirtyhit_set_ls/getcopy_getcopy_fromcache_flush/try_flush
    cache_evictcache_pincache_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_pincache_unpin還可以將某個對象在cache tier中進行鎖定和解鎖。

3.3.4 具體實現

Objecter要主動向其他OSD發起各類OP操作,封裝了具體的方法,包括writereadpg_readgetxattr等,每個方法使用的具體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操作的響應對處理,具體流程如下:

  1. 從維護的OSDSession映射結構中獲取該消息對應的OSDSession
  2. 判斷是否需要重試,如果需要就從OSDSession中刪除這個OP,並重新調用_op_submit提交該請求
  3. 獲取消息的結果,判斷是否需要進行redirect,如果需要就從OSDSession刪除該OP,並添加新的redirect目的地後調用_op_submit重新提交
  4. 獲取消息的輸出數據、返回值,並據此調用這個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_opsin_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存儲池,其處理流程如下:

  1. 判斷cache tier存儲池的狀態是否已滿,如果已滿,則對於讀請求直接調用do_proxy_read,對於寫請求直接將OP加入到waiting_for_cache_not_full隊列,並在下一次有新的請求達到時重新放入OP隊列處理。
  2. 在cache tier未滿的情況下,先判斷是否必須進行promote,如果需要就調用promote_object,先阻塞當前請求,從後端存儲池讀取一份數據到cache tier存儲池,並在完成之後再將當前請求加入OP隊列
  3. 在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標誌的兩種情況如下:

  1. CEPH_OSD_OP_DELETE請求會設置skip promote
  2. read、sync_read、sparse_read、checksum、writefull請求若設置了CEPH_OSD_OP_FLAG_FADVISE_NOCACHECEPH_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等處理方式,均可借鑑,但這些擴展都受目前的實現方式的制約。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章