java動態緩存成長小紀(一)——創建一個簡單的緩存


        在實際項目中,我們經常會需要使用到緩存。一般來說,緩存介質是內存;而常見的DB將數據存儲在硬盤中的;緩存讀取是電脈沖,而硬盤讀取是機械地讀取轉動的硬盤,速度差異是上百倍的。所以往往可以通過緩存,對經常用到的數據進行訪問,以提高速度。

        創建緩存實際上就是針對兩個對象,1. Cache對象,即一個緩存對象;2. CacheManager對象,即一個管理不同緩存的對象,其核心實際上就是一個Map,用來保存與獲取不同緩存。

      最簡單的緩存 實現如下:

/**
* 項目名稱:
* 文件說明:創建一個緩存管理器 <span style="font-family: Arial, Helvetica, sans-serif;"></span>
* 主要特點:
* 版本號:1.0
* 創建時間:2013-12-3
**/
package NBOffer;

import java.util.HashMap;

public class CacheManager {

static HashMap<String,Cache> cacheMap=new HashMap<String,Cache>();

public static Cache getCache(String id)
{
return cacheMap.get(id);
}

public static void putCache(Cache cache)
{
cacheMap.put(cache.id, cache);
}

public static void main(String[] args) {
Cache cache1=new Cache("1","A1");
Cache cache2=new Cache("2","A2");
CacheManager.putCache(cache1);
CacheManager.putCache(cache2);
CacheManager.getCache("1").showInfo();
}
}

class Cache
{
String id;//相當於主鍵
Object val;
public Cache(String id)
{
new Cache(id,null);
}

public Cache(String id,Object val)
{
this.id=id;
this.val=val;
}

public void setValue(Object val)
{
this.val=val;
}

public void showInfo()
{
System.out.println("Cache的ID是: "+id+" Cache的值是: "+val);
}
}


問題1: 哪一步能夠體現出緩存相對於直接讀取庫的優點?

實際上是getCache()方法,是從HashMap中讀取,也就是從JVM中讀取,處於內存中。


問題2: 緩存若未能命中,豈不是返回空的Cache了?

是的,所以我們需要對getCache進行改進,未能命中說明緩存里面沒有,需要從數據庫里面取數。


/**
* 項目名稱:
* 文件說明:創建一個緩存管理器 <span style="font-family: Arial, Helvetica, sans-serif;"></span>
* 主要特點:
* 版本號:1.0
* 創建時間:2013-12-3
**/
package NBOffer;

import java.util.HashMap;

public class CacheManager {

static HashMap<String,Cache> cacheMap=new HashMap<String,Cache>();

public static Cache getCache(String id)
{
if(cacheMap.get(id)==null)
{
Object val=getFromDB(id);
cacheMap.put(id, new Cache(id,val));
}
return cacheMap.get(id);
}

public static void putCache(Cache cache)
{
cacheMap.put(cache.id, cache);
}

public static Object getFromDB(String id)
{
System.out.println("緩慢地從內存中讀取id="+id+"對應的數據。。。");
return new String("value"+id);
}

public static void main(String[] args) {
Cache cache1=new Cache("1","value1");
Cache cache2=new Cache("2","value2");
CacheManager.putCache(cache1);
CacheManager.putCache(cache2);
CacheManager.getCache("3").showInfo();
}
}

class Cache
{
String id;//相當於主鍵
Object val;
public Cache(String id)
{
new Cache(id,null);
}

public Cache(String id,Object val)
{
this.id=id;
this.val=val;
}

public void setValue(Object val)
{
this.val=val;
}

public void showInfo()
{
System.out.println("Cache的ID是: "+id+" Cache的值是: "+val);
}
}


問題3:如果我想定時一段時間刷新緩存,比如,每隔15min刷新一次所有緩存,那我應該怎么辦?

為了方便觀察,我們設置為每間隔1s刷新一次。

/**
* 項目名稱:
* 文件說明:創建一個緩存管理器
* 主要特點:
* 版本號:1.0
* 創建時間:2013-12-3
**/
package NBOffer;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

public class CacheManager {

static HashMap<String,Cache> cacheMap=new HashMap<String,Cache>();

public static Cache getCache(String id)
{
if(cacheMap.get(id)==null)
{
Object val=getFromDB(id);
cacheMap.put(id, new Cache(id,val));
}
return cacheMap.get(id);
}

public static void putCache(Cache cache)
{
cacheMap.put(cache.id, cache);
}

public static Object getFromDB(String id)
{
System.out.println("緩慢地從內存中讀取id="+id+"對應的數據。。。");
return new String("value"+id);
}

public static void refreshCaches()
{
System.out.println("刷新緩存。。。");
Set<String> keySet=cacheMap.keySet();
Iterator it=keySet.iterator();
while(it.hasNext())
{
String id=(String) it.next();
Object val=getFromDB(id);
cacheMap.put(id, new Cache(id,val));
}
}

public static void main(String[] args) {
Cache cache1=new Cache("1","value1");
Cache cache2=new Cache("2","value2");
CacheManager.putCache(cache1);
CacheManager.putCache(cache2);
CacheManager.getCache("3").showInfo();

Thread refreshTD=new Thread()
{
public void run()
{
while(true)
{
refreshCaches();
try {
Thread.sleep(1000);//每一秒刷新一次
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
refreshTD.start();
}
}

class Cache
{
String id;//相當於主鍵
Object val;

public Cache(String id)
{
new Cache(id,null);
}

public Cache(String id,Object val)
{
this.id=id;
this.val=val;
}

public void setValue(Object val)
{
this.val=val;
}

public void showInfo()
{
System.out.println("Cache的ID是: "+id+" Cache的值是: "+val);
}
}

問題4:上面你說的緩存是存在問題的,因為每次你都是統一刷新,有的數據剛剛在14min59s的時候上傳的,你就得讓它在1s后刷新嗎?另外,有的數據變化的很快,不能15min刷新的,而是需要沒1min刷新一次,你這樣做真的好嗎?還有,有的數據在這15min中之內都不會用到,還有必要刷新嗎?

上述三個問題的確需要改進,那么我可以針對不同的緩存,並且在自由需要(發送請求+緩存不存在/失效)的時候才進行刷新,再進行優化。代碼如下:

/**
* 項目名稱:
* 文件說明:創建一個緩存管理器 lcx
* 主要特點:
* 版本號:1.0
* 創建時間:2013-12-3
**/
package NBOffer;

import java.util.Date;
import java.util.HashMap;

public class CacheManager {

static HashMap<String,Cache> cacheMap=new HashMap<String,Cache>();

public static Cache getCache(String id)
{
//對於不存在或者失效的緩存統一處理
if(cacheMap.get(id)==null||!cacheMap.get(id).isValid())
{
Object val=getFromDB(id);
Cache cache=new Cache(id,val);
cache.latest=new Date();
cacheMap.put(id, cache);
}
return cacheMap.get(id);
}

public static void putCache(Cache cache)
{
cache.latest=new Date();
cacheMap.put(cache.id, cache);
}

public static Object getFromDB(String id)
{
System.out.println("緩慢地從內存中讀取id="+id+"對應的數據。。。");
return new String("value"+id);
}

public static void main(String[] args) {
Cache cache1=new Cache("1","value1");
Cache cache2=new Cache("2","value2");
cache2.invalidTime=5000;//設置失效視角是5s
CacheManager.putCache(cache1);
CacheManager.putCache(cache2);
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
e.printStackTrace();
}
CacheManager.getCache("1");
CacheManager.getCache("2");
}
}
class Cache
{
String id;//相當於主鍵
Object val;
Date latest;//最近刷新時間
long invalidTime=10000;//默認失效時間10s

public Cache(String id)
{
new Cache(id,null);
}

public Cache(String id,Object val)
{
this.id=id;
this.val=val;
}

public void setValue(Object val)
{
this.val=val;
}

/**
* 判斷現在緩存是否有效
* @return
*/
public boolean isValid()
{
return (new Date()).getTime()-latest.getTime()<invalidTime;
}

public void showInfo()
{
System.out.println("Cache的ID是: "+id+" Cache的值是: "+val);
}
}

問題5:既然都做到這個份上了,那么關於緩存的幾個名詞都得說說了。

緩存命中率:緩存命中率=從緩存中讀數/(緩存讀數+磁盤讀數)。

緩存失效:緩存超過了失效時間。過期了(也可能有其他原因)。

緩存穿透:對於並不存在的數據,每次都會想DB發送請求,對DB的危害非常大。

緩存雪崩:對於某個失效的緩存在短時間內大量訪問,可能造成DB宕機。

緩存算法:LRU、LFU、LIFO


下一節,將針對不同的緩存算法進行研究。




注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com