2025-02-15

1、概念
近年來,內存容量不斷提高,價格不斷下跌,操作系統已經可以支持更大的地址空間,充分利用技術提升系統性能成為一個熱點。
可從數據庫方面來考慮,此種方案可使用“內存數據庫”,另一種方案是使用“內存對象緩存系統”,將某些量小、使用次數多的數據以key/value的方式保存在內存對象緩存系統中,減少數據庫查詢訪問帶來的性能下降。
在本文中,主要講述後者,但在本章也對“內存數據庫”的概念進行說明。
1.1 內存數據庫
內存數據庫,就是將數據放在內存中直接操作的數據庫。相對於磁盤,內存的數據讀寫速度要高出幾個數量級,將數據保存在內存中相比從磁盤上訪問能夠極大地提高應用的性能。
在數據庫技術中,目前主要使用兩種方法來使用大量的內存:
1)              在傳統的數據庫中,增大緩沖池,將一個事務所涉及的數據都放在緩沖池中,組織成相應的數據結構來進行查詢和更新處理,也就是常說的共享內存技術,這種方法優化的主要目標是最小化磁盤訪問。
2)              使用內存數據庫技術(也叫主存數據庫)技術,也就是幹脆重新設計一種數據庫管理系統,對查詢處理、並發控制和恢復的算法和數據結構進行重新設計,以更有效的使用CPU周期和內存,這種技術近乎把整個數據庫放進內存中,因而產生一種根本性的變化。
1.2 內存對象緩存系統
內存對象緩存系統用於動態Web應用以減輕數據庫負載。它通過在內存中緩存數據和對象來減少讀取數據庫的次數,從而提供動態、數據庫驅動網站的速度。
常用的是Memcached 是一個高性能的分佈式內存對象緩存系統,它基於一個存儲鍵/值對的hashmap。其守護進程(daemon )是用C寫的,但是客戶端可以用任何語言來編寫,並通過memcached協議與守護進程通信。但是它並不提供冗餘(例如,復制其hashmap條目);當某個服務器S停止運行或崩潰瞭,所有存放在S上的鍵/值對都將丟失。
Memcached由Danga Interactive開發,用於提升LiveJournal.com訪問速度的。LJ每秒動態頁面訪問量幾千次,用戶700萬。Memcached將數據庫負載大幅度降低,更好的分配資源,更快速訪問。
2、應用場景
Memcached一般與數據庫系統配合使用,它用於存儲一些經常需要讀取的數據,從來達到提高性能的目的。
下面講幾個在呼叫中心系統中的應用實例。
2.1 任務可執行性檢查
例如,外呼任務需要在指定“任務開始時間”和“任務結束時間”中運行,並且在“啟用”狀態才能運行,而且單個外呼任務的進程需要通過查詢數據庫定時檢查(例如:1分鐘)該任務是繼續運行,還是終止運行。當多個外呼任務並行運行時,這種查詢會給系統增加負擔。
若使用內存對象緩存系統,將每個任務的狀態信息以key/value對的方式存儲在Memcached的“任務信息表”中(key=任務id,value:是否可運行標志),在插入任務時,在Memcached的“任務狀態表”中插入一條數據;當“任務開始時間”和“任務結束時間”或“狀態”發生改變時,根據key對應改變Memcached的“任務信息表”的value值。在外呼任務進行中,查詢任務是否可運行時不再需要查詢數據庫,一般情況下隻需要查詢Memcached即可,從而減少瞭對數據庫的訪問。
2.2 日程檢查
         在呼叫中心路由時,有時候需要提供根據日程進行路由的功能,例如滿足某個日程(例如:每年的“10月1日00:00:00”到“10月7日23:59:59”的日程)時才路由到A中繼。一個日程可以對應多個條目,若在路由前需要要驗證當前時間是否滿足日程,那對應需要查詢兩表的信息,還需要是否滿足日程條件。當並發很多個用戶進行呼叫時,這種檢查會給系統增加很大的負荷,在此處也可以使用內存對象緩存系統來解決問題。
         開發定時觸發(1分鐘執行一次)程序去日程表和日程條目表中查詢多條信息,將當前時間與某個日程比較,將其存入Memcached中的“日程表”中,key為日程鍵,value為當前時間是否在當前日程內的標志。
         路由程序根據日程鍵檢查Memcached中的“日程表”,若滿足某個日程,則路由到A中繼,基本不再需要針對日程對數據庫進行查詢。
3、應用禁忌
3.1 不要將Memcached當數據庫用
Memcached 的首要目的就是加快數據的響應時間,否則數據從其他數據源構建或恢復需要很長時間。一個典型的例子就是從一個數據庫中恢復信息,特別是在信息顯示給用戶前 需要對信息進行格式化或處理的時候。Memcached 被設計用來將信息存儲在內存中以避免每次在數據需要恢復時重復執行相同的任務。
切不可將 Memcached 用作運行應用程序所需信息的惟一信息源;數據應總是可以從其他信息源獲取。此外,要記住 Memcached 隻是一個鍵/值的存儲。不能在數據上執行查詢,或者對內容進行迭代來提取信息。
3.2 不要使用Memcached存儲大批量數據
         首先Memcached空間大小不算大,而且主要用於緩存,所以不應該在Memcached中存儲大批量數據。若在Memcached中按照一定格式存儲數據表的行數據,而且表的數據量又很大,首先空間不允許,而且查詢非常麻煩,此類數據適合保存在數據庫中。
3.2 Memcached並不安全
為瞭確保最佳性能,Memcached 並未提供任何形式的安全性,沒有身份驗證,也沒有加密。這意味著對 memcached 服務器的訪問應該這麼處理:一是通過將它們放到應用程序部署環境相同的私有側,二是如果安全性是必須的,那麼就使用 UNIX® socket 並隻允許當前主機上的應用程序訪問此 Memcached 服務器。
這多少犧牲瞭一些靈活性和彈性,以及跨網絡上的多臺機器共享 RAM 緩存的能力,但這是在目前的情況下確保 memcached 數據安全性的惟一一種解決方案。
1、Memcached介紹
Memcached是高性能的,分佈式的內存對象緩存系統,用於在動態應用中減少數據庫負載,提升訪問速度。Memcached由Danga Interactive開發,用於提升LiveJournal.com訪問速度的。LJ每秒動態頁面訪問量幾千次,用戶700萬。Memcached將數據庫負載大幅度降低,更好的分配資源,更快速訪問。
Memcached的最新版是采用c語言進行開發和設計的,它是一個應用軟件,是作為緩存服務器的服務器端運行在服務器上的,需要使用特定的語言編寫客戶端與其進行通信來進行數據的緩存和獲取。
在系統中,通常將Memcached安裝運行在服務器上,然後通過對需要的數據進行緩存,所有數據的緩存設置和存取操作,以及數據的更新後替換操作全部需要程序來進行。
2、Memcached的安裝
2.1 主程序的安裝
一般的服務器都是采用Linux,筆者隻是講述在Windows上如何安裝Memcached,在Linux上的安裝請參考網上其它資料。
Windows版本的下載地址為:http://code.jellycan.com/memcached/
當前win32的最新版本是1.2.6,下載頁面參考如下:


  
在上圖中點擊“memcached-1.2.6-win32-bin.zip”進入下載頁面,下載後,將其解壓到D盤下,解壓後的D:"memcached-1.2.6-win32-bin目錄下有一個memcached.exe。
在Windows的命令行(cmd命令進入命令行)窗口進入該目錄,首先運行:
memcached.exe -d install
上面這行表示安裝Memcached為服務,這樣才能正常運行。接著運行如下這樣來啟動Memcached,還可指定-l參數,表示啟動的IP,-m表示緩存大小:
memcached.exe -d start
若指定瞭-m,則表示緩存大小為-m後的數字,單位是M,例如:
memcached.exe –l 127.0.0.1 –m 32 -d start
運行參考如下圖所示:
  
2.2 Java客戶端的安裝
下載地址為:https://github.com/gwhalin/Memcached-Java-Client
下載頁面參考如下:


 
         在上圖中點擊右側區域的“Downloads”,彈出的下載小窗口如下圖:


   
         當前最新的版本是2.5.2,點擊“java_memcached-release_2.5.2.zip”下載。下載後解壓,目錄結構如下圖所示:


   
         在應用中,需要將“java_memcached-release_2.5.2.jar”包拷貝到Java項目中。
3、Memcached的使用
3.1 創建項目
在MyEclipse中創建一個名為memcacheddemo的測試項目,src放源代碼,bin放classes文件,lib放jar包,並將java_memcached-release_2.5.2.jar拷貝到lib目錄中,目錄結構如下:
  
3.2 SockIOPool類及其常用方法
         SockIOPool是socket連接池類,常用方法如下:
l setServers(String[] servers):設置服務器信息數組;
l setWeights(String[] weights):設置服務器權重數組;
l setInitConn(int count):設置初始連接數;
l setMinConn(int minConn):設置最小連接數;
l setMaxConn(int maxConn):設置最大連接數;
l setMaxIdle(long arg0):設置最大處理時間;
l setMaintSleep(long arg0):主線程的睡眠時間;
l initialize():初始化連接池。
3.3 MemCachedClient類及其常用方法
         MemCachedClient類用於對Memcached內存對象緩存系統進行操作,常用方法如下:
l add(String key, Object value):添加一個鍵值對到緩存中;
l add(String key, Object value,Date expires):添加一個鍵值對到緩存中,並設置其超時時間;
l set(String key, Object value):在緩存中設置一個鍵的值;
l set(String key, Object value, Date expires):在緩存中設置一個鍵的值,並設置其超時時間;
l get(String key):獲得某個鍵的值。
l incr(String key):為某個鍵上的值執行+1操作;
l decr(String key):為某個鍵上的值執行-1操作;
l replace(String key, String value):將某個鍵的值替換成新的值;
l replace(String key, String value, Date expires):將某個鍵的值替換成新的值,並設置其超時時間。
3.4 使用實例
         在memcacheddemo工程的源碼目錄創建測試的Java類MemcachedTest,該類的代碼參考如下:
import java.util.Date;

import com.danga.MemCached.MemCachedClient;
import com.danga.MemCached.SockIOPool;

/**
 * 使用memcached的緩存測試類.
 * @author 阿蜜果
 */
public class MemcachedTest {
    // 創建全局的唯一實例
    protected static MemCachedClient mcc = new MemCachedClient();

    protected static MemcachedTest memCached = new MemcachedTest();

    // 設置與緩存服務器的連接池
    static {
        // 服務器列表和其權重
        String[] servers = {"127.0.0.1:11211"};
        Integer[] weights = {3};

        // 獲取socket連接池的實例對象
        SockIOPool pool = SockIOPool.getInstance();

        // 設置服務器信息
        pool.setServers(servers);
        pool.setWeights(weights);

        // 設置初始連接數、最小和最大連接數以及最大處理時間
        pool.setInitConn(5);
        pool.setMinConn(5);
        pool.setMaxConn(250);
        pool.setMaxIdle(1000 * 60 * 60 * 6);

        // 設置主線程的睡眠時間
        pool.setMaintSleep(30);

        // 設置TCP的參數,連接超時等
        pool.setNagle(false);
        pool.setSocketTO(3000);
        pool.setSocketConnectTO(0);

        // 初始化連接池
        pool.initialize();

        // 壓縮設置,超過指定大小(單位為K)的數據都會被壓縮
        mcc.setCompressEnable(true);
        mcc.setCompressThreshold(64 * 1024);
    }

    /**
     * 保護型構造方法,不允許實例化
     */
    protected MemcachedTest() {

    }

    /**
     * 獲取唯一實例.
     * @return
     */
    public static MemcachedTest getInstance() {
        return memCached;
    }

    /**
     * 添加一個指定的值到緩存中.
     * @param key 鍵
     * @param value 值
     * @return 在緩存中若該key不存在,並成功添加返回true,否則將返回false
     */
    public boolean add(String key, Object value) {
        return mcc.add(key, value);
    }

    /**
     * 添加一個鍵值對到緩存中.
     * @param key 鍵
     * @param value 值
     * @param expires 超時時間
     * @return 在緩存中若該key不存在,並成功添加返回true,否則將返回false
     */
    public boolean add(String key, Object value, Date expires) {
        return mcc.add(key, value, expires);
    }

    /**
     * 將某個鍵的值改變成新值,首先需要保證該鍵存在.
     * @param key 鍵
     * @param value 值
     * @return 成功返回true,失敗返回false
     */
    public boolean replace(String key, Object value) {
        return mcc.replace(key, value);
    }

    /**
     * 將某個鍵的值改變成新值,首先需要保證該鍵存在.
     * @param key 鍵
     * @param value 值
     * @param expires 超時時間
     * @return 成功返回true,失敗返回false
     */
    public boolean replace(String key, Object value, Date expires) {
        return mcc.replace(key, value, expires);
    }

    /**
     * 添加一個指定的值到緩存中.
     * @param key
     * @param value
     * @return 成功返回true,否則返回false
     */
    public boolean set(String key, Object value) {
        return mcc.set(key, value);
    }
   
    /**
     * 添加一個指定的值到緩存中,並設置其超時時間.
     * @param key 鍵
     * @param value 值
     * @param expires 超時時間
     * @return 成功返回true,否則返回false
     */
    public boolean set(String key, Object value, int expires) {
        return mcc.set(key, value, expires);
    }
   
    /**
     * 根據指定的關鍵字獲取對象.
     * @param key
     * @return 返回value
     */
    public Object get(String key) {
        return mcc.get(key);
    }

    /**
     * 將指定key的value值+1,將返回最後的value值
     * @param key
     * @return 返回最後的value值
     */
    public long incr(String key) {
        return mcc.incr(key);
    }
   
    /**
     * 將指定key的value值-1,將返回最後的value值
     * @param key
     * @return 返回最後的value值
     */
    public long decr(String key) {
        return mcc.decr(key);
    }
   
    /**
     * 測試方法
     * @param args
     */
    public static void main(String[] args) {
        MemcachedTest cache = MemcachedTest.getInstance();
        cache.set("count", 123);
        System.out.println("count=" + cache.get("count"));
        boolean flag = cache.add("schedule_2", "0");
        System.out.println("flag=" + flag);
        System.out.println("schedule_2=" + cache.get("schedule_2"));
    }
}
      運行結果為:
count=123
flag=true
schedule_2=0

作者“ERDP技術架構”

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *