初識Memcached

 

Memcached服務器


        Memcached是高性能的,分佈式的內存對象緩存系統,用於在動態應用中減少數據庫負載,提升訪問速度。Memcached由Danga Interactive(運營LiveJournal的技術團隊)開發,用於提升LiveJournal.com訪問速度的。  LJ每秒動態頁面訪問量是幾千次,用戶700萬。Memcached將數據負載大幅度降低,更好的分配資源,更快速訪問。

        其實Memcache是這個項目的名稱,而memcached是它服務器端的主程序文件名

        Memcached可以應對任意多個連接,使用非阻塞的網絡IO。由於它的工作機制是在內存中開闢一塊空間,然後建立一個HashTable,Memcached自管理這些HashTable.


        雖然memcached使用了同樣的“Key=>Value”方式組織數據,但是它和共享內存、APC等本地緩存有非常大的區別。Memcached是分佈式的,也就是說它不是本地的。它基於網絡連接(當然它也可以使用localhost)方式完成服務,本身它是一個獨立於應用的程序或守護進程(Daemon方式)。

 

        Memcached最吸引人的一個特性就是支持分佈式部署;也就是說可以在一羣機器上建立一堆 Memcached 服務,每個服務可以根據具體服務器的硬件配置使用不同大小的內存塊,這樣一來,理論上可以建立一個無限巨大的基於內存的cache storage 系統。


        Memcached使用libevent庫實現網絡連接服務,理論上可以處理無限多的連接,但是它和Apache不同,它更多的時候是面向穩定的持續連接的,所以它實際的併發能力是有限制的。在保守情況下memcached的最大同時連接數爲200,這和Linux線程能力有關係,這個數值是可以調整的。關於libevent可以參考相關文檔。 Memcached內存使用方式也和APC不同。APC是基於共享內存和MMAP的,memcachd有自己的內存分配算法和管理方式,它和共享內存沒有關係,也沒有共享內存的限制,通常情況下,每個memcached進程可以管理2GB的內存空間,如果需要更多的空間,可以增加進程數。

 

        Memcached在很多時候都是作爲數據庫前端cache使用的。因爲它比數據庫少了很多SQL解析、磁盤操作等開銷,而且它是使用內存來管理數據的,所以它可以提供比直接讀取數據庫更好的性能,在大型系統中,訪問同樣的數據是很頻繁的,memcached可以大大降低數據庫壓力,使系統執行效率提升。另外,memcached也經常作爲服務器之間數據共享的存儲媒介,例如在SSO系統中保存系統單點登陸狀態的數據就可以保存在memcached中,被多個應用共享。


        需要注意的是,使用Memcache的網站一般流量都是比較大的,爲了緩解數據庫的壓力,讓Memcache作爲一個緩存區域,把部分信息保存在內存中,在前端能夠迅速的進行存取。由於memcached使用內存管理數據,所以它是易失的,當服務器重啓,或者memcached進程中止,數據便會丟失,所以memcached不能用來持久保存數據。很多人的錯誤理解,memcached的性能非常好,好到了內存和硬盤的對比程度,其實memcached使用內存並不會得到成百上千的讀寫速度提高,它的實際瓶頸在於網絡連接,它和使用磁盤的數據庫系統相比,好處在於它本身非常“輕”,因爲沒有過多的開銷和直接的讀寫方式,它可以輕鬆應付非常大的數據交換量,所以經常會出現兩條千兆網絡帶寬都滿負荷了,memcached進程本身並不佔用多少CPU資源的情況。


       Memcached是“分佈式”的內存對象緩存系統,所以那些不需要“分佈”的,不需要共享的,或者乾脆規模小到只有一臺服務器的應用,memcached不會帶來任何好處,相反還會拖慢系統效率,因爲網絡連接同樣需要資源,即使是UNIX本地連接也一樣。

 

Windows下的Memcache安裝

1. 下載memcache的windows穩定版,解壓放某個盤下面,比如在c:/memcached

2. 在終端(也即cmd命令界面)下輸入 c:/memcached/memcached.exe -d install            --安裝memcached成爲服務,這樣才能正常運行,否則運行失敗!
 

3. 再輸入: c:/memcached/memcached.exe -d start    --啓動memcached的。

   
      以後memcached將作爲windows的一個服務每次開機時自動啓動。這樣服務器端已經安裝完畢了。


Linux下的安裝:

1.下載memcached和libevent,放到 /tmp 目錄下

# cd /tmp
# wget http://www.danga.com/memcached/dist/memcached-1.2.0.tar.gz
# wget http://www.monkey.org/~provos/libevent-1.2.tar.gz

2.先安裝libevent:
# tar zxvf libevent-1.2.tar.gz
# cd libevent-1.2
# ./configure –prefix=/usr
# make
# make install

3.測試libevent是否安裝成功:
# ls -al /usr/lib | grep libevent
lrwxrwxrwx 1 root root 21 11?? 12 17:38 libevent-1.2.so.1 -> libevent-1.2.so.1.0.3
-rwxr-xr-x 1 root root 263546 11?? 12 17:38 libevent-1.2.so.1.0.3
-rw-r–r– 1 root root 454156 11?? 12 17:38 libevent.a
-rwxr-xr-x 1 root root 811 11?? 12 17:38 libevent.la
lrwxrwxrwx 1 root root 21 11?? 12 17:38 libevent.so -> libevent-1.2.so.1.0.3


4.安裝memcached,同時需要安裝中指定libevent的安裝位置:
# cd /tmp
# tar zxvf memcached-1.2.0.tar.gz
# cd memcached-1.2.0
# ./configure –with-libevent=/usr
# make
# make install

如果中間出現報錯,請仔細檢查錯誤信息,按照錯誤信息來配置或者增加相應的庫或者路徑。
安裝完成後會把memcached放到 /usr/local/bin/memcached ,

5.測試是否成功安裝memcached:
# ls -al /usr/local/bin/mem*
-rwxr-xr-x 1 root root 137986 11?? 12 17:39 /usr/local/bin/memcached
-rwxr-xr-x 1 root root 140179 11?? 12 17:39 /usr/local/bin/memcached-debug


memcached的基本設置:

1.啓動Memcache的服務器端:
# /usr/local/bin/memcached -d -m 10 -u root -l 192.168.0.200 -p 12000 -c 256 -P /tmp/memcached.pid

-d選項是啓動一個守護進程,
-m是分配給Memcache使用的內存數量,單位是MB,這裏是10MB,
-u是運行Memcache的用戶,這裏是root,
-l是監聽的服務器IP地址,如果有多個地址的話,這裏指定了服務器的IP地址192.168.0.200,
-p是設置Memcache監聽的端口,這裏設置了12000,最好是1024以上的端口,
-c選項是最大運行的併發連接數,默認是1024,這裏設置了256,按照你服務器的負載量來設定,
-P是設置保存Memcache的pid文件,這裏是保存在 /tmp/memcached.pid,

2.如果要結束Memcache進程,執行:

# kill `cat /tmp/memcached.pid`

也可以啓動多個守護進程,不過端口不能重複。

3.重啓apache,service httpd restart

 

java的客戶端連接程序:

       將java_memcached-release_1.6.zip解壓後的目錄中的java_memcached-release_2.0.jar文件複製到java項目的lib目錄下。

 

package utils.cache;

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連接池的實例對象
        SockIOPool  sockIOPool = SockIOPool.getInstance();

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

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

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

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

        //sockIOPool.setFailover(bFailover);
        //sockIOPool.setAliveCheck(bAliveCheck);

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

        // 壓縮設置,超過指定大小(單位爲K)的數據都會被壓縮     

        if (memCachedClient == null)
        {
            mcc = new MemCachedClient(sPoolName);
            mcc.setCompressEnable(true);
            mcc.setCompressThreshold(4096);
            mcc.setPrimitiveAsString(true);
        }
    }

/*

<h3>基於Spring的配置,如下:</h3>
  <pre>
   &lt;bean id="memCachedService" class="com.ms.memcached.MemCachedServiceImpl"&gt;
    &lt;constructor-arg index="0" value="${memcached.pool.name}" /&gt;
    &lt;constructor-arg index="1" value="${memcached.pool.servers}" /&gt;
    &lt;constructor-arg index="2" value="${memcached.pool.initConn}" /&gt;
    &lt;constructor-arg index="3" value="${memcached.pool.maxConn}" /&gt;
    &lt;constructor-arg index="4" value="${memcached.pool.minConn}" /&gt;
    &lt;constructor-arg index="5" value="${memcached.pool.socketTO}" /&gt;
    &lt;constructor-arg index="6" value="${memcached.pool.maintSleep}" /&gt;
    &lt;constructor-arg index="7" value="${memcached.pool.nagle}" /&gt;
    &lt;constructor-arg index="8" value="${memcached.pool.failover}" /&gt;
    &lt;constructor-arg index="9" value="${memcached.pool.aliveCheck}" /&gt;
    &lt;/bean&gt;
    </pre>
    <h3>利用com.MS.cache.properties來設置參數,如下:</h3>
    <pre>
    memcached.pool.name = MS
    memcached.pool.servers = 192.168.9.132:12000,192.168.9.133:12000
    memcached.pool.initConn = 128
    memcached.pool.maxConn = 1024
    memcached.pool.minConn = 20
    memcached.pool.socketTO = 3000
    memcached.pool.maintSleep = 30
    memcached.pool.nagle = false
    memcached.pool.failover = true
    memcached.pool.aliveCheck = true
    </pre>

*/

 
    /**
     * 保護型構造方法,不允許實例化!
     */
    protected MemCached()
    {
       
    }
   
    /**
     * 獲取唯一實例.
     */
    public static MemCached getInstance()
    {
        return memCached;
    }
   
    /**
     * 添加一個指定的值到緩存中.
     * @param key
     * @param value
     */

    //新增指定key的緩存內容,但不覆蓋已存在的內容。
    public boolean add(String key, Object value)
    {
        return mcc.add(key, value);
    }
  

   //expiry過期時間
    public boolean add(String key, Object value, Date expiry)
    {
        return mcc.add(key, value, expiry);
    }
  

    //新增或覆蓋指定Key的緩存內容
    public boolean set(String key, Object value)
    {
        return mcc.set(key, value);
    }
  

    //lExpiry過期時間
    public boolean set(String key, Object value, long lExpiry)
    {
        return mcc.set(key, value, new Date(lExpiry));
    }

 
   //根據指定的Key獲取緩存內容

    public boolean get(String key)
    {
        return mcc.get(key);
    }


    //根據指定Key更新緩存內容
    public boolean replace(String key, Object value)
    {
        return mcc.replace(key, value);
    }
  

    //lExpiry 指定的時間
    public boolean replace(String key, Object value, long lExpiry)
    {
        return mcc.replace(key, value, new Date(lExpiry));
    }

     //根據指定Key刪除緩存內容
    public boolean delete(String key, Object value)
    {
        return mcc.delete(key, value);
    }


    //根據指定Key在指定時間後刪除緩存內容
     public boolean delete(String key, Object value, long lExpiry)
    {
        return mcc.delete(key, value, new Date(lExpiry));
    }

  

    //檢測Cache中當前Key是否存在
     public boolean exists(String key)
    {
        return mcc.exists(key);
    }

   //根據指定一批Key批量獲取緩存內容。
   /*
   * @param sKeys 指定的一批Key。
   * @return Object[oValue]
   */
    public Object[] getMultiArray(String[] sKeys) throws ServiceException
   {
       return memCachedClient.getMultiArray(sKeys);
   }

   /**
   * 根據指定一批Key批量獲取緩存內容。
   *
   * @param sKeys 指定的一批Key。
   * @return Map<sKey, oValue>
   */
    public Map<String, Object> getMulti(String[] sKeys) throws ServiceException
   {
       return memCachedClient.getMulti(sKeys);
   }
       
    public static void main(String[] args)
    {
        MemCached memCached= MemCached.getInstance();
        memCached.add("hello", 234);
        System.out.print("get value : " + memCached.get("hello"));
    }
}


    那麼我們就可以通過簡單的像main方法中操作的一樣存入一個變量,然後再取出進行查看,我們可以看到先調用了add,然後再進行get,我們運行一次後,234這個值已經被我們存入了memcached的緩存中的了,我們將main方法中紅色的那一行註釋掉後,我們再運行還是可以看到get到的value也是234,即緩存中我們已經存在了數據了。

    對基本的數據我們可以操作,對於普通的POJO而言,如果要進行存儲的話,那麼比如讓其實現java.io.Serializable接口,因爲memcached是一個分佈式的緩存服務器,多臺服務器間進行數據共享需要將對象序列化的,所以必須實現該接口,否則會報錯的。

Entity

/**
  * 獲取當前實體的緩存Id
  *
  * @return
  */
 public String getCacheId()
 {
  return getCacheId(this.getClass(), sBreedId);
 }

 

get

  public Breed getBreedById(String sBreedId) throws ServiceException
  {
     Breed breed = (Breed)memCachedService.get(getCacheId(Breed.class, sBreedId));
 
     if(breed == null)
     {
        breed = service.get("breed.getBreedById", sBreedId);
  
        if(breed != null)
        {
           memCachedService.set(breed.getBreedId(), breed);
        }
     }
 
     return breed;
  } 
 


save

   memCachedService.set(spider.getCacheId(), breed);

 

update

   memCachedService.replace(spider.getCacheId(), breed);

 

remove

   memCachedService.delete(getCacheId(Spider.class, IbreedId));
 或
   memCachedService.delete(breed.getCacheId());

listAll

   public List listAll() throws ServiceException
   {
      List breeds = new ArrayList ();
 
      List breedIds = (List)memCachedService.get(getKeyByMap("Breed", null));
 
      if(ObjectUtils.isEmpty(breedIds))
      {
         breeds = service.list("breed.getAllBreed", null);
  
         if (!ObjectUtils.isEmpty(breeds))
         {
            breedIds = new ArrayList();
   
            for (Breed breed : breeds)
            {
                breedIds.add(breed.getBreedId());
            }
   
            memCachedService.set(getKeyByMap("Breed", null), breedIds);
         }
      }
      else
      {
           for (String sBreedId : breedIds)
           {
               Breed breed = getBreedById(sBreedId);
   
               if (breed != null)
               {
                  breeds.add(breed);
               }
           }
      }
 
      return breeds;
    }

發佈了30 篇原創文章 · 獲贊 2 · 訪問量 18萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章