WEB性能優化--緩存

緩存是位於內存中的一塊用於臨時存儲數據的區域,在java web的開發中,主要用來優化程序與數據庫的交互,將頻繁的sql查詢操做或增,刪,改(通常的,查詢操做遠大於增,刪,改的操做),只當第一次請求時,直接請求數據庫,而後把結果放到緩存中,後續若再有相同的操做請求,直接去緩存中取值,大大減輕了數據庫服務器的壓力.(java項目的開發中,數據庫是最薄弱的一環)

一.緩存的簡單模擬實現代碼
1>沒有緩存時的代碼java

public User findById(Integer id){  

String sql="SELECT * FROM T_USER WHERE id=?";
return DBHelp.executeQueryForObject(User.class,sql,id);
}

2>加上緩存後的代碼web

public User findById(Integer id){   

/**首先去緩存中查找對象,有就直接返回;  
沒有在去數據庫中操做,而且把操做後的結果放入緩存中,方便後續的使用 */  
User user = cache.get("user"+id);  
if(user==null){  
String sql="SELECT * FROM T_USER WHERE id=?";  
return DBHelp.executeQueryForObject(User.class,sql,id);  
cache.set("user"+id,user); 
}  
return user;  
}

3>cache的簡單模擬實現算法

緩存的原理:緩存的內部實現是一個Map,Map<key,Object>中的key通常是具備標示性的給定,如user:0-->new User.
另外,緩存會基於指定的淘汰策略機制,決定數據的保留或移除.常見的緩存淘汰策略有三種:
1.基於數量
1.1 FIFO(first in first out):先進先出
弊端:若先出去的正好是常常要被請求的操做,這樣會大大影響效率
1.2 LRU:最近最長被使用
1.3 JVM
1.4 ...
2.基於時間
3.基於數量+時間
具體說明以下:
當緩存須要被清理時(好比空間佔用已經接近臨界值了),須要使用某種淘汰算法來決定清理掉哪些數據。經常使用的淘汰算法有下面幾種:
FIFO:First In First Out,先進先出。判斷被存儲的時間,離目前最遠的數據優先被淘汰。
LRU:Least Recently Used,最近最少使用。判斷最近被使用的時間,目前最遠的數據優先被淘汰。
LFU:Least Frequently Used,最不常常使用。在一段時間內,數據被使用次數最少的,優先被淘汰。sql

public class SimpleCache{

//HashTable是線程安全的,且不容許有null的key
private static Map<String,Object> cache = new HashTable<>();

/**取值,從緩存裏獲取數據,叫命中緩存*/
public static Object getObject(String key){
//判斷緩存中是否包含key,包含返回映射值;不包含返回null
if(map.containsKey(key)){
   return cache.get(key);
 }
 return null;
}

//放值
public static void setObject(String key,Object obj){
cache.put(key,obj);
}

//刪除指定值並返回
publlic static Object removeObject(String key){
return cache.remove(key);
}

}

經常使用緩存的開源框架:EHCACHE
通常的緩存都是本地緩存.
分佈式緩存 Redis , Memcached
問題:若查詢的是單個結果集,能夠直接去緩存中取值;但若是請求操做的結果集是一個集合,如List
則要進行緩存的時時刷新,以獲取最新的結果.如論壇中,不少人在同一時間段內發帖,後臺就必須不斷的作insert/update/delete操做,操做完後,應該把原先放在緩存中的數據刪除,這樣,當用戶刷新時,獲取的是最新的數據,而不是緩存中沒有更新的數據.
分佈式緩存能夠配置多臺主機用於緩存,組成一個集羣,這樣當一臺設備遇到故障後,能夠將任務分攤到集羣中的其餘設備上,不影響正常運行.
---
EHCACHE的使用:
在MAVEN中配置EHCACHE的依賴:ehcache.xml(必需要叫這個名)
數據庫

<ehcache>  
  <diskStore path="java.io.tmpdir" /> <!--java.io.tmpdir爲系統常量,表示當前用戶下的臨時文件夾-->  
  <defaultCache
    maxElementInMemory="1000"
    eternal="false"
    timeToIdleSeconds="120"
    timeToLiveSeconds="120"
    overflowToDisk="true"
   />
  <cache name="users"
    maxElementInMemory="1000"
    eternal="false" <!--表示是否永久存儲,若是爲true,將致使下面兩個參數無效-->
    timeToIdleSeconds="120"//閒置時間(最後訪問時間-當前時間)
    timeToLiveSeconds="120"//存活時間(當前時間-建立時間,閒置時間<=存活時間)
    overflowToDisk="true"// 
<ehcache/>

EhCacheUtil.java緩存

import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;

public class EhCacheUtil{
  private static CacheManager cacheManager = new CacheManager();
  
public static void set(String key,Object value){
Ehcache ehcache = cacheManager.getEhcache("myCache");
Element element = new Element(key,value);
ehcace.put(element);
} 

public static Object get(String key){
Ehcache ehcache = cacheManager.getEhcache("myCache");
Element element = ehcache.get(key);
if(element!=null){
  return element.getObjectValue();
}
return null;
}

public static void remove(String key){
Ehcache ehcache = cacheManager.getEhcache("myCache");
ehcache.remove(key);
}
}

測試代碼:安全

public class TestDao{
private Logger logger = LoggerFactory.getLogger(TestDao.class);  

//查詢單個結果
public Message findById(Integer id){  

Message message = (Message)EhCacheUtil.get("Message:"+id);
if(message == null){
String sql = "select * from t_message where id=?";
message = DBHelp.query(sql,new BeanHandler<>(Message.class,id));
EhCacheUtil.set("Message:"+id,message);
}else{
logger.debug("load message from cache");
}
return message;
}

//查詢多個結果(須要手動的刷新緩存) 
public List<Message> findAll(){

List<Message> messageList = EHCacheUtil.get("messageList");
if(messageList == null){
String sql = "select * from t_message order by id desc";
messageList = DBHelp.query(sql,new BeanListHandler<>(Message.class));
EhCacheUtil.set("messageList",messageList);
}
return messageList;
}

//新增數據
public void save(Message message){
String sql = "insert into t_message(message,author) values(?,?)";
DBHelp.update(sql,message.getMessage,message.getAuthor);
//刪除緩存中已有的集合
EhCacheUtil.remove("messageList");
}

public static  void main(String[] args){
TestDao testDao = new TestDao();
List<Message> messgeList = testDao.findAll();//from DB
//messageList =  testDao.findAll();from cache

//中間作了insert操做
Message message = new Message("你妹","王宏");
testDao.save(message);//insert removeCache
messageList =  testDao.findAll();from DB
messageList =  testDao.findAll();from cache
//使用斷言
Assert.assertEquals(25,messageList.size());
} 
}
相關文章
相關標籤/搜索