摘要:并且添加了監(jiān)聽(tīng)器,當(dāng)數(shù)據(jù)被刪除后會(huì)打印日志。六總結(jié)回顧緩存加載顯示插入緩存回收,定時(shí),,軟弱引用,顯示刪除接口方法,監(jiān)聽(tīng)器清理緩存時(shí)間只有在獲取數(shù)據(jù)時(shí)才或清理緩存,使用者可以單起線程采用方法主動(dòng)清理。
摘要: 學(xué)習(xí)Google內(nèi)部使用的工具包Guava,在Java項(xiàng)目中輕松地增加緩存,提高程序獲取數(shù)據(jù)的效率。一、什么是緩存?
根據(jù)科普中國(guó)的定義,緩存就是數(shù)據(jù)交換的緩沖區(qū)(稱作Cache),當(dāng)某一硬件要讀取數(shù)據(jù)時(shí),會(huì)首先從緩存中查找需要的數(shù)據(jù),如果找到了則直接執(zhí)行,找不到的話則從內(nèi)存中找。由于緩存的運(yùn)行速度比內(nèi)存快得多,故緩存的作用就是幫助硬件更快地運(yùn)行。
在這里,我們借用了硬件緩存的概念,當(dāng)在Java程序中計(jì)算或查詢數(shù)據(jù)的代價(jià)很高,并且對(duì)同樣的計(jì)算或查詢條件需要不止一次獲取數(shù)據(jù)的時(shí)候,就應(yīng)當(dāng)考慮使用緩存。換句話說(shuō),緩存就是以空間換時(shí)間,大部分應(yīng)用在各種IO,數(shù)據(jù)庫(kù)查詢等耗時(shí)較長(zhǎng)的應(yīng)用當(dāng)中。
二、緩存原理當(dāng)獲取數(shù)據(jù)時(shí),程序?qū)⑾葟囊粋€(gè)存儲(chǔ)在內(nèi)存中的數(shù)據(jù)結(jié)構(gòu)中獲取數(shù)據(jù)。如果數(shù)據(jù)不存在,則在磁盤或者數(shù)據(jù)庫(kù)中獲取數(shù)據(jù)并存入到數(shù)據(jù)結(jié)構(gòu)當(dāng)中。之后程序需要再次獲取數(shù)據(jù)時(shí),則會(huì)先查詢這個(gè)數(shù)據(jù)結(jié)構(gòu)。從內(nèi)存中獲取數(shù)據(jù)時(shí)間明顯小于通過(guò)IO獲取數(shù)據(jù),這個(gè)數(shù)據(jù)結(jié)構(gòu)就是緩存的實(shí)現(xiàn)。
這里引入一個(gè)概念,緩存命中率:從緩存中獲取到數(shù)據(jù)的次數(shù)/全部查詢次數(shù),命中率越高說(shuō)明這個(gè)緩存的效率好。由于機(jī)器內(nèi)存的限制,緩存一般只能占據(jù)有限的內(nèi)存大小,緩存需要不定期的刪除一部分?jǐn)?shù)據(jù),從而保證不會(huì)占據(jù)大量?jī)?nèi)存導(dǎo)致機(jī)器崩潰。
如何提高命中率呢?那就得從刪除一部分?jǐn)?shù)據(jù)著手了。目前有三種刪除數(shù)據(jù)的方式,分別是:FIFO(先進(jìn)先出)、LFU(定期淘汰最少使用次數(shù))、LRU(淘汰最長(zhǎng)時(shí)間未被使用)。
三、GuavaCache工作方式GuavaCache的工作流程:獲取數(shù)據(jù)->如果存在,返回?cái)?shù)據(jù)->計(jì)算獲取數(shù)據(jù)->存儲(chǔ)返回。由于特定的工作流程,使用者必須在創(chuàng)建Cache或者獲取數(shù)據(jù)時(shí)指定不存在數(shù)據(jù)時(shí)應(yīng)當(dāng)怎么獲取數(shù)據(jù)。GuavaCache采用LRU的工作原理,使用者必須指定緩存數(shù)據(jù)的大小,當(dāng)超過(guò)緩存大小時(shí),必定引發(fā)數(shù)據(jù)刪除。GuavaCache還可以讓用戶指定緩存數(shù)據(jù)的過(guò)期時(shí)間,刷新時(shí)間等等很多有用的功能。
四、GuavaCache使用Demo 4.1 簡(jiǎn)單使用有人說(shuō)我就想簡(jiǎn)簡(jiǎn)單單的使用cache,就像Map那樣方便就行。接下來(lái)展示一段簡(jiǎn)單的使用方式。
首先定義一個(gè)需要存儲(chǔ)的Bean,對(duì)象Man:
/** * @author jiangmitiao * @version V1.0 * @Title: 標(biāo)題 * @Description: Bean * @date 2016/10/27 10:01 */ public class Man { //身份證號(hào) private String id; //姓名 private String name; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Man{" + "id="" + id + """ + ", name="" + name + """ + "}"; } }
接下來(lái)我們寫一個(gè)Demo:
import com.google.common.cache.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.*; /** * @author jiangmitiao * @version V1.0 * @Description: Demo * @date 2016/10/27 10:00 */ public class GuavaCachDemo { private LoadingCacheloadingCache; //loadingCache public void InitLoadingCache() { //指定一個(gè)如果數(shù)據(jù)不存在獲取數(shù)據(jù)的方法 CacheLoader cacheLoader = new CacheLoader () { @Override public Man load(String key) throws Exception { //模擬mysql操作 Logger logger = LoggerFactory.getLogger("LoadingCache"); logger.info("LoadingCache測(cè)試 從mysql加載緩存ing...(2s)"); Thread.sleep(2000); logger.info("LoadingCache測(cè)試 從mysql加載緩存成功"); Man tmpman = new Man(); tmpman.setId(key); tmpman.setName("其他人"); if (key.equals("001")) { tmpman.setName("張三"); return tmpman; } if (key.equals("002")) { tmpman.setName("李四"); return tmpman; } return tmpman; } }; //緩存數(shù)量為1,為了展示緩存刪除效果 loadingCache = CacheBuilder.newBuilder().maximumSize(1).build(cacheLoader); } //獲取數(shù)據(jù),如果不存在返回null public Man getIfPresentloadingCache(String key){ return loadingCache.getIfPresent(key); } //獲取數(shù)據(jù),如果數(shù)據(jù)不存在則通過(guò)cacheLoader獲取數(shù)據(jù),緩存并返回 public Man getCacheKeyloadingCache(String key){ try { return loadingCache.get(key); } catch (ExecutionException e) { e.printStackTrace(); } return null; } //直接向緩存put數(shù)據(jù) public void putloadingCache(String key,Man value){ Logger logger = LoggerFactory.getLogger("LoadingCache"); logger.info("put key :{} value : {}",key,value.getName()); loadingCache.put(key,value); } }
接下來(lái),我們寫一些測(cè)試方法,檢測(cè)一下
public class Test { public static void main(String[] args){ GuavaCachDemo cachDemo = new GuavaCachDemo() System.out.println("使用loadingCache"); cachDemo.InitLoadingCache(); System.out.println("使用loadingCache get方法 第一次加載"); Man man = cachDemo.getCacheKeyloadingCache("001"); System.out.println(man); System.out.println(" 使用loadingCache getIfPresent方法 第一次加載"); man = cachDemo.getIfPresentloadingCache("002"); System.out.println(man); System.out.println(" 使用loadingCache get方法 第一次加載"); man = cachDemo.getCacheKeyloadingCache("002"); System.out.println(man); System.out.println(" 使用loadingCache get方法 已加載過(guò)"); man = cachDemo.getCacheKeyloadingCache("002"); System.out.println(man); System.out.println(" 使用loadingCache get方法 已加載過(guò),但是已經(jīng)被剔除掉,驗(yàn)證重新加載"); man = cachDemo.getCacheKeyloadingCache("001"); System.out.println(man); System.out.println(" 使用loadingCache getIfPresent方法 已加載過(guò)"); man = cachDemo.getIfPresentloadingCache("001"); System.out.println(man); System.out.println(" 使用loadingCache put方法 再次get"); Man newMan = new Man(); newMan.setId("001"); newMan.setName("額外添加"); cachDemo.putloadingCache("001",newMan); man = cachDemo.getCacheKeyloadingCache("001"); System.out.println(man); } }
測(cè)試結(jié)果如下:
4.2 高級(jí)特性由于目前使用有局限性,接下來(lái)只講我用到的一些方法。
我來(lái)演示一下GuavaCache自帶的兩個(gè)Cache
GuavaCacheDemo.java import com.google.common.cache.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.*; /** * @author jiangmitiao * @version V1.0 * @Description: Demo * @date 2016/10/27 10:00 */ public class GuavaCachDemo { private Cachecache; private LoadingCache loadingCache; private RemovalListener removalListener; public void Init(){ //移除key-value監(jiān)聽(tīng)器 removalListener = new RemovalListener (){ public void onRemoval(RemovalNotification notification) { Logger logger = LoggerFactory.getLogger("RemovalListener"); logger.info(notification.getKey()+"被移除"); //可以在監(jiān)聽(tīng)器中獲取key,value,和刪除原因 notification.getValue(); notification.getCause();//EXPLICIT、REPLACED、COLLECTED、EXPIRED、SIZE }}; //可以使用RemovalListeners.asynchronous方法將移除監(jiān)聽(tīng)器設(shè)為異步方法 //removalListener = RemovalListeners.asynchronous(removalListener, new ThreadPoolExecutor(1,1,1000, TimeUnit.MINUTES,new ArrayBlockingQueue (1))); } //loadingCache public void InitLoadingCache() { //指定一個(gè)如果數(shù)據(jù)不存在獲取數(shù)據(jù)的方法 CacheLoader cacheLoader = new CacheLoader () { @Override public Man load(String key) throws Exception { //模擬mysql操作 Logger logger = LoggerFactory.getLogger("LoadingCache"); logger.info("LoadingCache測(cè)試 從mysql加載緩存ing...(2s)"); Thread.sleep(2000); logger.info("LoadingCache測(cè)試 從mysql加載緩存成功"); Man tmpman = new Man(); tmpman.setId(key); tmpman.setName("其他人"); if (key.equals("001")) { tmpman.setName("張三"); return tmpman; } if (key.equals("002")) { tmpman.setName("李四"); return tmpman; } return tmpman; } }; //緩存數(shù)量為1,為了展示緩存刪除效果 loadingCache = CacheBuilder.newBuilder(). //設(shè)置2分鐘沒(méi)有獲取將會(huì)移除數(shù)據(jù) expireAfterAccess(2, TimeUnit.MINUTES). //設(shè)置2分鐘沒(méi)有更新數(shù)據(jù)則會(huì)移除數(shù)據(jù) expireAfterWrite(2, TimeUnit.MINUTES). //每1分鐘刷新數(shù)據(jù) refreshAfterWrite(1,TimeUnit.MINUTES). //設(shè)置key為弱引用 weakKeys(). // weakValues().//設(shè)置存在時(shí)間和刷新時(shí)間后不能再次設(shè)置 // softValues().//設(shè)置存在時(shí)間和刷新時(shí)間后不能再次設(shè)置 maximumSize(1). removalListener(removalListener). build(cacheLoader); } //獲取數(shù)據(jù),如果不存在返回null public Man getIfPresentloadingCache(String key){ return loadingCache.getIfPresent(key); } //獲取數(shù)據(jù),如果數(shù)據(jù)不存在則通過(guò)cacheLoader獲取數(shù)據(jù),緩存并返回 public Man getCacheKeyloadingCache(String key){ try { return loadingCache.get(key); } catch (ExecutionException e) { e.printStackTrace(); } return null; } //直接向緩存put數(shù)據(jù) public void putloadingCache(String key,Man value){ Logger logger = LoggerFactory.getLogger("LoadingCache"); logger.info("put key :{} value : {}",key,value.getName()); loadingCache.put(key,value); } public void InitDefault() { cache = CacheBuilder.newBuilder(). expireAfterAccess(2, TimeUnit.MINUTES). expireAfterWrite(2, TimeUnit.MINUTES). // refreshAfterWrite(1,TimeUnit.MINUTES).//沒(méi)有cacheLoader的cache不能設(shè)置刷新,因?yàn)闆](méi)有指定獲取數(shù)據(jù)的方式 weakKeys(). // weakValues().//設(shè)置存在時(shí)間和刷新時(shí)間后不能再次設(shè)置 // softValues().//設(shè)置存在時(shí)間和刷新時(shí)間后不能再次設(shè)置 maximumSize(1). removalListener(removalListener). build(); } public Man getIfPresentCache(String key){ return cache.getIfPresent(key); } public Man getCacheKeyCache(final String key) throws ExecutionException { return cache.get(key, new Callable () { public Man call() throws Exception { //模擬mysql操作 Logger logger = LoggerFactory.getLogger("Cache"); logger.info("Cache測(cè)試 從mysql加載緩存ing...(2s)"); Thread.sleep(2000); logger.info("Cache測(cè)試 從mysql加載緩存成功"); Man tmpman = new Man(); tmpman.setId(key); tmpman.setName("其他人"); if (key.equals("001")) { tmpman.setName("張三"); return tmpman; } if (key.equals("002")) { tmpman.setName("李四"); return tmpman; } return tmpman; } }); } public void putCache(String key,Man value){ Logger logger = LoggerFactory.getLogger("Cache"); logger.info("put key :{} value : {}",key,value.getName()); cache.put(key,value); } }
在這個(gè)demo中,分別采用了Guava自帶的兩個(gè)Cache:LocalLoadingCache和LocalManualCache。并且添加了監(jiān)聽(tīng)器,當(dāng)數(shù)據(jù)被刪除后會(huì)打印日志。
Main:
public static void main(String[] args){ GuavaCachDemo cachDemo = new GuavaCachDemo(); cachDemo.Init(); System.out.println("使用loadingCache"); cachDemo.InitLoadingCache(); System.out.println("使用loadingCache get方法 第一次加載"); Man man = cachDemo.getCacheKeyloadingCache("001"); System.out.println(man); System.out.println(" 使用loadingCache getIfPresent方法 第一次加載"); man = cachDemo.getIfPresentloadingCache("002"); System.out.println(man); System.out.println(" 使用loadingCache get方法 第一次加載"); man = cachDemo.getCacheKeyloadingCache("002"); System.out.println(man); System.out.println(" 使用loadingCache get方法 已加載過(guò)"); man = cachDemo.getCacheKeyloadingCache("002"); System.out.println(man); System.out.println(" 使用loadingCache get方法 已加載過(guò),但是已經(jīng)被剔除掉,驗(yàn)證重新加載"); man = cachDemo.getCacheKeyloadingCache("001"); System.out.println(man); System.out.println(" 使用loadingCache getIfPresent方法 已加載過(guò)"); man = cachDemo.getIfPresentloadingCache("001"); System.out.println(man); System.out.println(" 使用loadingCache put方法 再次get"); Man newMan = new Man(); newMan.setId("001"); newMan.setName("額外添加"); cachDemo.putloadingCache("001",newMan); man = cachDemo.getCacheKeyloadingCache("001"); System.out.println(man); /////////////////////////////////// System.out.println(" 使用Cache"); cachDemo.InitDefault(); System.out.println("使用Cache get方法 第一次加載"); try { man = cachDemo.getCacheKeyCache("001"); } catch (ExecutionException e) { e.printStackTrace(); } System.out.println(man); System.out.println(" 使用Cache getIfPresent方法 第一次加載"); man = cachDemo.getIfPresentCache("002"); System.out.println(man); System.out.println(" 使用Cache get方法 第一次加載"); try { man = cachDemo.getCacheKeyCache("002"); } catch (ExecutionException e) { e.printStackTrace(); } System.out.println(man); System.out.println(" 使用Cache get方法 已加載過(guò)"); try { man = cachDemo.getCacheKeyCache("002"); } catch (ExecutionException e) { e.printStackTrace(); } System.out.println(man); System.out.println(" 使用Cache get方法 已加載過(guò),但是已經(jīng)被剔除掉,驗(yàn)證重新加載"); try { man = cachDemo.getCacheKeyCache("001"); } catch (ExecutionException e) { e.printStackTrace(); } System.out.println(man); System.out.println(" 使用Cache getIfPresent方法 已加載過(guò)"); man = cachDemo.getIfPresentCache("001"); System.out.println(man); System.out.println(" 使用Cache put方法 再次get"); Man newMan1 = new Man(); newMan1.setId("001"); newMan1.setName("額外添加"); cachDemo.putloadingCache("001",newMan1); man = cachDemo.getCacheKeyloadingCache("001"); System.out.println(man); }
測(cè)試結(jié)果如下:
由上述結(jié)果可以表明,GuavaCache可以在數(shù)據(jù)存儲(chǔ)到達(dá)指定大小后刪除數(shù)據(jù)結(jié)構(gòu)中的數(shù)據(jù)。我們可以設(shè)置定期刪除而達(dá)到定期從數(shù)據(jù)庫(kù)、磁盤等其他地方更新數(shù)據(jù)等(再次訪問(wèn)時(shí)數(shù)據(jù)不存在重新獲?。?。也可以采用定時(shí)刷新的方式更新數(shù)據(jù)。
還可以設(shè)置移除監(jiān)聽(tīng)器對(duì)被刪除的數(shù)據(jù)進(jìn)行一些操作。通過(guò)RemovalListeners.asynchronous(RemovalListener,Executor)方法將監(jiān)聽(tīng)器設(shè)為異步,筆者通過(guò)實(shí)驗(yàn)發(fā)現(xiàn),異步監(jiān)聽(tīng)不會(huì)在刪除數(shù)據(jù)時(shí)立刻調(diào)用監(jiān)聽(tīng)器方法。
五、GuavaCache結(jié)構(gòu)初探類結(jié)構(gòu)圖
GuavaCache并不希望我們?cè)O(shè)置復(fù)雜的參數(shù),而讓我們采用建造者模式創(chuàng)建Cache。GuavaCache分為兩種Cache:Cache,LoadingCache。LoadingCache繼承了Cache,他比Cache主要多了get和refresh方法。多這兩個(gè)方法能干什么呢?
在第四節(jié)高級(jí)特性demo中,我們看到builder生成不帶CacheLoader的Cache實(shí)例。在類結(jié)構(gòu)圖中其實(shí)是生成了LocalManualCache類實(shí)例。而帶CacheLoader的Cache實(shí)例生成的是LocalLoadingCache。他可以定時(shí)刷新數(shù)據(jù),因?yàn)?strong>獲取數(shù)據(jù)的方法已經(jīng)作為構(gòu)造參數(shù)方法存入了Cache實(shí)例中。同樣,在get時(shí),不需要像LocalManualCache還需要傳入一個(gè)Callable實(shí)例。
實(shí)際上,這兩個(gè)Cache實(shí)現(xiàn)類都繼承自LocalCache,大部分實(shí)現(xiàn)都是父類做的。
六、總結(jié)回顧緩存加載:CacheLoader、Callable、顯示插入(put)
緩存回收:LRU,定時(shí)(expireAfterAccess,expireAfterWrite),軟弱引用,顯示刪除(Cache接口方法invalidate,invalidateAll)
監(jiān)聽(tīng)器:CacheBuilder.removalListener(RemovalListener)
清理緩存時(shí)間:只有在獲取數(shù)據(jù)時(shí)才或清理緩存LRU,使用者可以單起線程采用Cache.cleanUp()方法主動(dòng)清理。
刷新:主動(dòng)刷新方法LoadingCache.referesh(K)
信息統(tǒng)計(jì):CacheBuilder.recordStats() 開(kāi)啟Guava Cache的統(tǒng)計(jì)功能。Cache.stats() 返回CacheStats對(duì)象。(其中包括命中率等相關(guān)信息)
獲取當(dāng)前緩存所有數(shù)據(jù):cache.asMap(),cache.asMap().get(Object)會(huì)刷新數(shù)據(jù)的訪問(wèn)時(shí)間(影響的是:創(chuàng)建時(shí)設(shè)置的在多久沒(méi)訪問(wèn)后刪除數(shù)據(jù))
LocalManualCache和LocalLoadingCache的選擇ManualCache可以在get時(shí)動(dòng)態(tài)設(shè)置獲取數(shù)據(jù)的方法,而LoadingCache可以定時(shí)刷新數(shù)據(jù)。如何取舍?我認(rèn)為在緩存數(shù)據(jù)有很多種類的時(shí)候采用第一種cache。而數(shù)據(jù)單一,數(shù)據(jù)庫(kù)數(shù)據(jù)會(huì)定時(shí)刷新時(shí)采用第二種cache。
具體工程中的情況也歡迎大家與我交流,互相學(xué)習(xí)。
參考資料:http://www.cnblogs.com/peida/...
https://github.com/tiantianga...
http://www.blogjava.net/DLevi...
http://ifeve.com/google-guava...
更多文章:http://blog.gavinzh.com
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/61831.html
摘要:并且添加了監(jiān)聽(tīng)器,當(dāng)數(shù)據(jù)被刪除后會(huì)打印日志。六總結(jié)回顧緩存加載顯示插入緩存回收,定時(shí),,軟弱引用,顯示刪除接口方法,監(jiān)聽(tīng)器清理緩存時(shí)間只有在獲取數(shù)據(jù)時(shí)才或清理緩存,使用者可以單起線程采用方法主動(dòng)清理。 摘要: 學(xué)習(xí)Google內(nèi)部使用的工具包Guava,在Java項(xiàng)目中輕松地增加緩存,提高程序獲取數(shù)據(jù)的效率。一、什么是緩存?根據(jù)科普中國(guó)的定義,緩存就是數(shù)據(jù)交換的緩沖區(qū)(稱作Cache)...
摘要:前言在上文源碼分析原理中分析了的相關(guān)原理。我在北京模擬執(zhí)行你在哪兒回復(fù)最后執(zhí)行結(jié)果開(kāi)始提問(wèn)提問(wèn)完畢,我去干其他事了收到消息你在哪兒等待響應(yīng)中。。。。?;貜?fù)我在北京這樣一個(gè)模擬的異步事件回調(diào)就完成了。 showImg(https://segmentfault.com/img/remote/1460000015643387?w=2048&h=1150); 前言 在上文「Guava 源碼分析...
摘要:前言在上文源碼分析原理中分析了的相關(guān)原理。我在北京模擬執(zhí)行你在哪兒回復(fù)最后執(zhí)行結(jié)果開(kāi)始提問(wèn)提問(wèn)完畢,我去干其他事了收到消息你在哪兒等待響應(yīng)中。。。。?;貜?fù)我在北京這樣一個(gè)模擬的異步事件回調(diào)就完成了。 showImg(https://segmentfault.com/img/remote/1460000015643387?w=2048&h=1150); 前言 在上文「Guava 源碼分析...
摘要:緩存總體可分為兩種集中式緩存和分布式緩存集中式緩存與分布式緩存的區(qū)別其實(shí)就在于集中與非集中的概念,其對(duì)象可能是服務(wù)器內(nèi)存條硬盤等。內(nèi)存條版本緩存集中在一臺(tái)服務(wù)器的一條內(nèi)存條上,為集中式緩存。 背景 緩存的主要作用是暫時(shí)在內(nèi)存中保存業(yè)務(wù)系統(tǒng)的數(shù)據(jù)處理結(jié)果,并且等待下次訪問(wèn)使用。在日長(zhǎng)開(kāi)發(fā)有很多場(chǎng)合,有一些數(shù)據(jù)量不是很大,不會(huì)經(jīng)常改動(dòng),并且訪問(wèn)非常頻繁。但是由于受限于硬盤IO的性能或者遠(yuǎn)程...
摘要:緩存總體可分為兩種集中式緩存和分布式緩存集中式緩存與分布式緩存的區(qū)別其實(shí)就在于集中與非集中的概念,其對(duì)象可能是服務(wù)器內(nèi)存條硬盤等。內(nèi)存條版本緩存集中在一臺(tái)服務(wù)器的一條內(nèi)存條上,為集中式緩存。 背景 緩存的主要作用是暫時(shí)在內(nèi)存中保存業(yè)務(wù)系統(tǒng)的數(shù)據(jù)處理結(jié)果,并且等待下次訪問(wèn)使用。在日長(zhǎng)開(kāi)發(fā)有很多場(chǎng)合,有一些數(shù)據(jù)量不是很大,不會(huì)經(jīng)常改動(dòng),并且訪問(wèn)非常頻繁。但是由于受限于硬盤IO的性能或者遠(yuǎn)程...
閱讀 4658·2021-10-13 09:39
閱讀 541·2021-09-06 15:02
閱讀 3306·2019-08-30 15:53
閱讀 1096·2019-08-30 13:04
閱讀 2178·2019-08-30 11:27
閱讀 2067·2019-08-26 13:51
閱讀 2179·2019-08-26 11:33
閱讀 2957·2019-08-26 10:36