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>
<bean id="memCachedService" class="com.ms.memcached.MemCachedServiceImpl">
<constructor-arg index="0" value="${memcached.pool.name}" />
<constructor-arg index="1" value="${memcached.pool.servers}" />
<constructor-arg index="2" value="${memcached.pool.initConn}" />
<constructor-arg index="3" value="${memcached.pool.maxConn}" />
<constructor-arg index="4" value="${memcached.pool.minConn}" />
<constructor-arg index="5" value="${memcached.pool.socketTO}" />
<constructor-arg index="6" value="${memcached.pool.maintSleep}" />
<constructor-arg index="7" value="${memcached.pool.nagle}" />
<constructor-arg index="8" value="${memcached.pool.failover}" />
<constructor-arg index="9" value="${memcached.pool.aliveCheck}" />
</bean>
</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;
}