Memcached入門


Memcached是一個高性能的分布式內存對象緩存系統,用於動態Web應用以減輕數據庫負載。

它通過在內存中緩存數據和對象來減少讀取數據庫的次數,從而提供動態、數據庫驅動網站的速度。

Memcached基於一個存儲鍵值對的hashmap。其守護進程(daemon )是用C寫的,但是客戶端可以用任何語言來編寫,並通過memcached協議與守護進程通信。

 

下面來了解下Memcached怎么用~~

一、准備工作

http://code.jellycan.com/memcached/

下載memcached的windows版

再下載一個java_memcached-release.jar

 

二、安裝

解壓memcached-1.2.5-win32-bin.zip,CMD進入其目錄,然后執行如下命令:

c:>memcached.exe -d install
c:>memcached.exe -l 127.0.0.1 -m 32 -d start

第一行是安裝memcached成為服務,這樣才能正常運行,否則運行失敗!

第二行是啟動memcached的,這里簡單的只分配32M內存了(默認64M),然后監聽本機端口和以守護進行運行。

執行完畢后,我們就可以在任務管理器中看到memcached.exe這個進程了。

如果想要在同一台Windows機器中安裝2個Memcached,請看這里

三、使用

現在服務器已經正常運行了,下面我們就來寫java的客戶端連接程序。

將java_memcached-release.zip解壓,把java_memcached-release.jar文件復制到java項目的lib目錄下,

然后我們來編寫代碼,比如我提供的一個應用類如下:

package memcached.test;

import java.util.Date;

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

/**
 * 使用memcached的緩存實用類.
 */
public class MemCached {
    // 創建全局的唯一實例
    protected static MemCachedClient mcc = new MemCachedClient();

    protected static MemCached memCached = new MemCached();

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

        // 獲取socke連接池的實例對象
        // 這個類用來創建管理客戶端和服務器通訊連接池,
        // 客戶端主要的工作(包括數據通訊、服務器定位、hash碼生成等)都是由這個類完成的。
        SockIOPool pool = SockIOPool.getInstance();

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

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

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

        // 設置連接心跳監測開關
        // true:每次通信都要進行連接是否有效的監測,造成通信次數倍增,加大網絡負載,
        // 因此在對HighAvailability要求比較高的場合應該設為true
        // 默認狀態是false,建議保持默認。
        pool.setAliveCheck(false);

        // 設置連接失敗恢復開關
        // 設置為true,當宕機的服務器啟動或中斷的網絡連接后,這個socket連接還可繼續使用,否則將不再使用.
        // 默認狀態是true,建議保持默認。
        pool.setFailback(true);

        // 設置容錯開關
        // true:當當前socket不可用時,程序會自動查找可用連接並返回,否則返回NULL
        // 默認狀態是true,建議保持默認。
        pool.setFailover(true);

        // 設置hash算法
        // alg=0 使用String.hashCode()獲得hash code,該方法依賴JDK,可能和其他客戶端不兼容,建議不使用
        // alg=1 使用original 兼容hash算法,兼容其他客戶端
        // alg=2 使用CRC32兼容hash算法,兼容其他客戶端,性能優於original算法
        // alg=3 使用MD5 hash算法
        // 采用前三種hash算法的時候,查找cache服務器使用余數方法。采用最后一種hash算法查找cache服務時使用consistent方法。
        // 默認值為0
        pool.setHashingAlg(0);

        // 設置是否使用Nagle算法,因為我們的通訊數據量通常都比較大(相對TCP控制數據)而且要求響應及時,
        // 因此該值需要設置為false(默認是true)
        pool.setNagle(false);
        
        // 設置socket的讀取等待超時值
        pool.setSocketTO(3000);
        
        // 設置socket的連接等待超時值
        pool.setSocketConnectTO(0);

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

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

    private MemCached() {

    }

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

    /**
     * 添加一個指定的鍵值對到緩存中.
     * 
     * @param key
     * @param value
     * @return
     */
    public boolean add(String key, Object value) {
        return mcc.add(key, value);
    }

    /**
     * 添加一個指定的鍵值對到緩存中.
     * 
     * @param key
     * @param value
     * @param expiry 多久之后過期
     * @return
     */
    public boolean add(String key, Object value, Date expiry) {
        return mcc.add(key, value, expiry);
    }
    
    
    public boolean set(String key, Object value) {
        return mcc.set(key, value);
    }

    public boolean set(String key, Object value, Date expiry) {
        return mcc.set(key, value, expiry);
    }

    public boolean replace(String key, Object value) {
        return mcc.replace(key, value);
    }

    public boolean replace(String key, Object value, Date expiry) {
        return mcc.replace(key, value, expiry);
    }
    
    

    /**
     * 根據指定的關鍵字獲取對象.
     * 
     * @param key
     * @return
     */
    public Object get(String key) {
        return mcc.get(key);
    }
}
MemCached

寫個Main方法測試下:

    public static void main(String[] args) {
        MemCached cache = MemCached.getInstance();
        boolean result1 = cache.add("hello", 1234, new Date(1000 * 2));// 設置2秒后過期
        System.out.println("第一次add : " + result1);
        System.out.println("Value : " + cache.get("hello"));
        
        boolean result2 =cache.add("hello", 12345, new Date(1000 * 2));// add fail
        System.out.println("第二次add : " + result2);
        
        boolean result3 =cache.set("hello", 12345, new Date(1000 * 2));// set successes
        System.out.println("調用set : " + result3);
        
        System.out.println("Value : " + cache.get("hello"));

        try {
            Thread.sleep(1000 * 2);
            System.out.println("已經sleep2秒了....");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Value : " + cache.get("hello"));
    }

執行結果如下:

第一次add : true
Value : 1234
第二次add : false
調用set : true
Value : 12345
已經sleep2秒了....
Value : null

說明:

  1. 第二次add失敗是因為"hello"這個key已經存在了。
  2. 調用set成功,是因為set的時候覆蓋了已存在的鍵值對,這正是add和set的不同之處
  3. 設置過期之間之后,cache按時自動失效

 

上面的例子是對於基本數據類型,對於普通的POJO而言,如果要進行存儲的話,那么比如讓其實現java.io.Serializable接口。

因為memcached是一個分布式的緩存服務器,多台服務器間進行數據共享需要將對象序列化的,所以必須實現該接口,否則會報錯的(java.io.NotSerializableException)。

下面來試試POJO的存儲:

package memcached.test;
public class Person implements java.io.Serializable {
    private static final long serialVersionUID = 1L;

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
簡單的POJO對象

Main方法如下:

    public static void main(String[] args) {
        MemCached cache = MemCached.getInstance();

        Person p1 = new Person();
        p1.setName("Jack");
        cache.add("bean", p1);
        
        Person p2 = (Person) cache.get("bean");
        System.out.println("name=" + p2.getName());//Jack
        p2.setName("Rose");
        
        // cache.replace("bean", p2);
        Person p3 = (Person) cache.get("bean");
        System.out.println("name=" + p3.getName());
    }

上面的代碼中,我們通過p2.setName("Rose")修改了對象的名字,

最后一行打印的會是什么呢?

name=Jack
name=Jack

Why?

這是因為我們修改的對象並不是緩存中的對象,而是通過序列化過來的一個實例對象

那么要修改怎么辦?使用replace,注釋掉的那一行把注釋去掉就可以了。

 

四、其他

Memcached的命令參數說明

-p <num>                監聽的端口
-l <ip_addr>            連接的IP地址, 默認是本機
-d start                   啟動memcached服務
-d restart                重起memcached服務
-d stop|shutdown    關閉正在運行的memcached服務
-d install                  安裝memcached服務
-d uninstall              卸載memcached服務
-u <username>       以<username>的身份運行 (僅在以root運行的時候有效)
-m <num>              最大內存使用,單位MB。默認64MB
-M                          內存耗盡時返回錯誤,而不是刪除項
-c <num>                最大同時連接數,默認是1024
-f <factor>              塊大小增長因子,默認是1.25
-n <bytes>             最小分配空間,key+value+flags默認是48
-h                          顯示幫助

 Memcached也可以在控制台中添加鍵值對,首先使用命令“telnet 127.0.0.1 11211”進入到Memcached控制台,

然后使用set、add、replace、get、delete來操作。

更詳細操作可參照這里

 

五、Memcached的優勢和不足

說到Memcached的優勢,那當然是:速度快,操作簡便,易擴展

不足的話,主要有2點:

  1. 數據的臨時性(數據僅保存在內存中)
  2. 只能通過指定鍵來讀取數據,不支持模糊查詢

 

六、Memcached停止時的保障措施

如果數據庫的訪問量比較大,就需要提前做好准備,以便應對在memcached停止時發生的負載問題。

如果能在停止memcached之前,把數據復制到其他的server就好了。恩,這個可以通過repcached來實現。

repcached是日本人開發的實現memcached復制功能,
它是一個單master、單slave的方案,但它的master/slave都是可讀寫的,而且可以相互同步
如果master壞掉,slave偵測到連接斷了,它會自動listen而成為master

 

 Memcached 集群架構方面的問題

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM