上下级平台之间数据同步方案_Alluxio与底层存储系统之间的元数据同步机制
作者簡(jiǎn)介:林意群,Apache Hadoop PMC member,Apache Ozone PMC member,擁有多年參與開源社區(qū)經(jīng)驗(yàn),主要專注于存儲(chǔ)領(lǐng)域的研究和學(xué)習(xí),目前任eBay Hadoop team 大數(shù)據(jù)研發(fā)工程師。
前 言
Alluxio作為一套構(gòu)建于底層存儲(chǔ)系統(tǒng)之上的中間層,它必不可少的會(huì)涉及到與底層系統(tǒng)之間metadata之間的同步問題。外部client請(qǐng)求訪問Alluxio系統(tǒng),然后Alluxio再?gòu)牡讓酉到y(tǒng)中(為稱呼方便,后面都簡(jiǎn)稱為Underlying FileSystem, UFS)查詢真實(shí)的元數(shù)據(jù)信息,然后再返回給client。當(dāng)然為了減少對(duì)于UFS的壓力,我們當(dāng)然不會(huì)每次都去查UFS。本文我們來聊聊Alluxio內(nèi)部對(duì)此元數(shù)據(jù)同步處理的設(shè)計(jì)實(shí)現(xiàn),它是最大可能性做到元數(shù)據(jù)請(qǐng)求處理的高效性以及數(shù)據(jù)的精準(zhǔn)性的。
Alluxio內(nèi)部的元數(shù)據(jù)同步行為
首先,這里我們需要想清楚一個(gè)基本的問題:作為一套構(gòu)建于底層存儲(chǔ)系統(tǒng)之上的Cache層,Alluxio內(nèi)部會(huì)存在哪些元數(shù)據(jù)需要同步的情況。
從元數(shù)據(jù)同步的源頭,目標(biāo)來劃分,總共為2類:
1)Alluxio內(nèi)部metadata先修改,UFS后修改,此過程是從Alluxio到UFS的metadata同步。
2)UFS的metadata先被修改,Alluxio隨后同步此修改,此過程則為從UFS到Alluxio的metadata同步。
在上述兩種情形中,1)較之于2)來說同步控制更為簡(jiǎn)單一些,因?yàn)锳lluxio本身作為外部請(qǐng)求的處理入口,它能第一時(shí)間知道請(qǐng)求的發(fā)生處理,然后它來自己控制后續(xù)如何做UFS底層存儲(chǔ)系統(tǒng)的metadata同步。Alluxio率先更新metadata后,對(duì)于外界來說,其元數(shù)據(jù)已經(jīng)是最新狀態(tài)的了。這時(shí)Alluxio可以選擇靈活的策略來更新UFS中滯后的metadata了,比如它可以采用異步更新的方式或者強(qiáng)制同步更新的方式。歸納起來一句話,1)情況下元數(shù)據(jù)同步更新的主動(dòng)權(quán)完全掌握在Alluxio系統(tǒng)這邊。
相比較而言,元數(shù)據(jù)同步較為復(fù)雜的是第二種情況:底層系統(tǒng)metadata發(fā)生改變(存在外部程序直接訪問UFS導(dǎo)致metadata發(fā)生改變),又沒有途徑能夠通知到Alluxio,而且Alluxio是外界請(qǐng)求訪問的服務(wù)。
2)的情況如下圖右半邊圖所示,1)則為下圖左半圖所示情形:
上面右半圖顯示的就是底層存儲(chǔ)系統(tǒng)HDFS存在額外更新的情況,需要Alluxio去同步來自Hive這邊的對(duì)HDFS的額外更新。
下面我們來看看Alluxio內(nèi)部是如何解決上面這種棘手的情況的。
基于給定時(shí)間,path粒度的UFS?Status?Cache
既然說存在UFS元數(shù)據(jù)意外更新的情況,為了保證Alluxio對(duì)外數(shù)據(jù)服務(wù)的準(zhǔn)確性,我們很容易想到一種極端的做法,就是準(zhǔn)實(shí)時(shí)地去同步HDFS中的metadata。
說到準(zhǔn)實(shí)時(shí)的同步UFS中的metadata,就會(huì)涉及到兩大核心問題:- 多久時(shí)間的同步,time interval是設(shè)定多少,時(shí)間過短會(huì)導(dǎo)致大量的RPC請(qǐng)求查詢UFS,過長(zhǎng)又會(huì)有數(shù)據(jù)延時(shí)性的問題。
- 同步多少量的metadata,一個(gè)目錄?一個(gè)文件?
有人可能會(huì)對(duì)上圖理解上有點(diǎn)疑惑,Alluxio本身作為Cache層,為什么還在內(nèi)部又做了一層Cache?注意這里Cache的對(duì)象已經(jīng)不一樣了,上圖Cache顯示的是從UFS查詢到的metadata信息。
上述步驟過程如下所述:
(1)Client發(fā)起文件信息查詢請(qǐng)求
(2)Alluxio收到請(qǐng)求,檢查其內(nèi)部UFS Status Cache是否存在未過期(在cache更新時(shí)間間隔內(nèi))的對(duì)應(yīng)的UFS Status,如果有則返回給Client。
(3)如果沒有,則發(fā)起請(qǐng)求到UFS,進(jìn)行最新狀態(tài)文件信息的查詢,并加到UFS Status Cache中,同時(shí)更新此Path的Status的同步時(shí)間。
上圖Alluxio內(nèi)部角色介紹為:- UfsSyncPathCache,此類用于記錄那些被Cache了的Status的Path路徑,此類存有各Path最近一次的metadata同步時(shí)間。
- UfsStatusCache, 此類cache了實(shí)際Path對(duì)應(yīng)的metadata cache,此類同時(shí)cache了以及,path對(duì)應(yīng)子文件status的映射關(guān)系。其中路徑對(duì)應(yīng)孩子文件信息的cache是為了加速目錄級(jí)別的list查詢。
我們知道存儲(chǔ)系統(tǒng)在list大目錄情況時(shí)的開銷是比較大的,因此上面的children file list的cache可以在一定程度上提升請(qǐng)求的響應(yīng)速度的。
這里主要來看Alluxio是如何做基于時(shí)間粒度的metadata cache的,相關(guān)代碼邏輯如下:
UfsSyncPathCache.java類
/** * The logic of shouldSyncPath need to consider the difference between file and directory, * with the variable isGetFileInfo we just process getFileInfo specially. * * There are three cases needed to address: * 1. the ancestor directories * 2. the direct parent directory * 3. the difference with file and directory * * @param path the path to check * @param intervalMs the sync interval, in ms * @param isGetFileInfo the operate is from getFileInfo or not * @return true if a sync should occur for the path and interval setting, false otherwise */ public boolean shouldSyncPath(String path, long intervalMs, boolean isGetFileInfo) { if (intervalMs < 0) { // Never sync. return false; } if (intervalMs == 0) { // Always sync. return true; } // 1)從cache中取出給定path的最近一次的同步時(shí)間 SyncTime lastSync = mCache.getIfPresent(path); // 2)判斷是否同步時(shí)間已經(jīng)超過過期間隔時(shí)間 if (!shouldSyncInternal(lastSync, intervalMs, false)) { // Sync is not necessary for this path. return false; } int parentLevel = 0; String currPath = path; while (!currPath.equals(AlluxioURI.SEPARATOR)) { try { // 3)如果時(shí)間超出,則進(jìn)行父目錄的查找,判斷父目錄是否達(dá)到需要更新的時(shí)間 currPath = PathUtils.getParent(currPath); parentLevel++; lastSync = mCache.getIfPresent(currPath); if (!shouldSyncInternal(lastSync, intervalMs, parentLevel > 1 || !isGetFileInfo)) { // Sync is not necessary because an ancestor was already recursively synced return false; } } catch (InvalidPathException e) { // this is not expected, but the sync should be triggered just in case. LOG.debug("Failed to get parent of ({}), for checking sync for ({})", currPath, path); return true; } } // trigger a sync, because a sync on the path (or an ancestor) was performed recently return true; }如上如果需要進(jìn)行metadata的sync操作,則會(huì)觸發(fā)后續(xù)的ufs status的查詢?nèi)缓蠹拥経fsStatusCache中。如果涉及到目錄下的文件信息的查詢,為了避免可能出現(xiàn)查詢子文件數(shù)量很多,查詢較慢的情況,alluxio做成了異步線程處理的方式。
UfsStatusCache.java
/** * Submit a request to asynchronously fetch the statuses corresponding to a given directory. * * Retrieve any fetched statuses by calling {@link #fetchChildrenIfAbsent(AlluxioURI, MountTable)} * with the same Alluxio path. * * If no {@link ExecutorService} was provided to this object before instantiation, this method is * a no-op. * * @param path the path to prefetch * @param mountTable the Alluxio mount table * @return the future corresponding to the fetch task */ @Nullable public Future> prefetchChildren(AlluxioURI path, MountTable mountTable) { if (mPrefetchExecutor == null) { return null; } try { Future> job = mPrefetchExecutor.submit(() -> getChildrenIfAbsent(path, mountTable)); Future> prev = mActivePrefetchJobs.put(path, job); if (prev != null) { prev.cancel(true); } return job; } catch (RejectedExecutionException e) { LOG.debug("Failed to submit prefetch job for path {}", path, e); return null; } }對(duì)于純單個(gè)文件的查詢請(qǐng)求,Alluxio采用了簡(jiǎn)單直接的辦法,每次嘗試做一次sync操作,如果cache在有效期內(nèi),則實(shí)際不會(huì)做實(shí)際metadata同步行為,然后從UFS cache中l(wèi)oad metadata返回結(jié)果。 @Override public FileInfo getFileInfo(AlluxioURI path, GetStatusContext context) throws FileDoesNotExistException, InvalidPathException, AccessControlException, IOException { Metrics.GET_FILE_INFO_OPS.inc(); long opTimeMs = System.currentTimeMillis(); try (RpcContext rpcContext = createRpcContext(); FileSystemMasterAuditContext auditContext = createAuditContext("getFileInfo", path, null, null)) { // 執(zhí)行sync metadata的操作,實(shí)際由cache interval時(shí)間控制 if (syncMetadata(rpcContext, path, context.getOptions().getCommonOptions(), DescendantType.ONE, auditContext, LockedInodePath::getInodeOrNull, (inodePath, permChecker) -> permChecker.checkPermission(Mode.Bits.READ, inodePath), true)) { // If synced, do not load metadata. context.getOptions().setLoadMetadataType(LoadMetadataPType.NEVER); } LoadMetadataContext lmCtx = LoadMetadataContext.mergeFrom( LoadMetadataPOptions.newBuilder().setCreateAncestors(true).setCommonOptions( FileSystemMasterCommonPOptions.newBuilder() .setTtl(context.getOptions().getCommonOptions().getTtl()) .setTtlAction(context.getOptions().getCommonOptions().getTtlAction())));...}還有一種比較典型地需要load metadata的場(chǎng)景是文件或目錄不存在于alluxio的情況。
以上就是本文所要簡(jiǎn)單闡述的Alluxio與底層存儲(chǔ)系統(tǒng)間元數(shù)據(jù)的同步方式相關(guān)的內(nèi)容,Alluxio本身作為底層存儲(chǔ)cache層,在內(nèi)部新維護(hù)了UFS的cache來做與底層UFS的status的同步。而且用戶可以按照實(shí)際場(chǎng)景需要來設(shè)定這個(gè)cache需要同步的間隔時(shí)間。另外一方面,UFS status cache的引入也減少了list查詢操作的代價(jià),在這點(diǎn)上比client直接訪問底層存儲(chǔ)系統(tǒng)做大目錄list要高效不少。
引 用
https://dzone.com/articles/two-ways-to-keep-files-in-sync-between-alluxio-and
·end·
—如果喜歡,快分享給你的朋友們吧—
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的上下级平台之间数据同步方案_Alluxio与底层存储系统之间的元数据同步机制的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 运行android程序时显示stop,A
- 下一篇: jdk db版本_企业视频会议系统音视频