Memcache理論
關於Memcache的理論知識,網上的資料鋪天蓋地,這裏就不重複羅列,作爲一名CodeMonkey,學習任何新知識的最好方式就是DIY.
參考資源:
http://kb.cnblogs.com/page/42731/
http://kb.cnblogs.com/page/42732/
http://kb.cnblogs.com/page/42733/
http://kb.cnblogs.com/page/42734/
http://kb.cnblogs.com/page/42735/
http://www.blogjava.net/chhbjh/archive/2012/02/21/370472.html
http://suhuanzheng7784877.iteye.com/blog/2026914
http://kb.cnblogs.com/page/42775/
安裝
下載memcached 的windows版本並解壓到某一目錄,如D:\Memcache
在Dos 窗口運行memcached.exe -d install 安裝memcached服務
如果是第一次安裝出現該錯誤提示,則需要以管理員的身份運行cmd,再次進行安裝
可以通過memcached.exe -h 查看運行命令參數信息
啓動服務
啓動memcache服務並監聽3686端口,默認端口是11211
查看window資源管理器,可以看到memcached.exe已經作爲服務啓動了
操作示例
連接memcache的java客戶端有很多,這裏我們使用了java_memcache。下載對應的jar包後引入到java項目中,就可以很方便地連接操作memcache了。
話不多說,直接上代碼。
使用Java_Memcache客戶端連接Memcache
import com.danga.MemCached.*;
public class MemcacheUtility {
private static MemCachedClient memCache;
private static final MemcacheUtility MemcacheInstance = new MemcacheUtility();
private MemcacheUtility(){
memCache = new MemCachedClient();
SockIOPool sockpool= SockIOPool.getInstance();
//設置緩存服務器地址,可以設置多個實現分佈式緩存
sockpool.setServers(new String[]{"127.0.0.1:11211","127.0.0.1:11212"});
//設置初始連接5
sockpool.setInitConn(5);
//設置最小連接5
sockpool.setMinConn(5);
//設置最大連接250
sockpool.setMaxConn(250);
//設置每個連接最大空閒時間1個小時
sockpool.setMaxIdle(1000 * 3600);
sockpool.setMaintSleep(30);
sockpool.setNagle(false);
sockpool.setSocketTO(3000);
sockpool.setSocketConnectTO(0);
sockpool.initialize();
}
public static MemcacheUtility getMemcacheClientInstance(){
return MemcacheInstance;
}
public Object get(String key){
Object obj = memCache.get(key);
return obj;
}
public void set(String key, Object value){
memCache.set(key, value);
}
//Memcache的操作還有replace/add/delete等,這裏只用get/set最簡單的操作來演示一下
}
使用MemcacheUtility對Memcache進行get/set操作
public class TestMemcaceAndJDBC {
public static void main(String[] args) {
MemcacheUtility mem = MemcacheUtility.getMemcacheClientInstance();
for(int i=0;i<10; i++){
mem.set("Data" + i, i);
}
for(int i=0;i<10;i++){
Object obj = mem.get("Data" + i);
if(obj != null){
System.out.println(((Integer)obj).intValue());
}
}
}
}
如果你打開任務管理器,你會注意到在啓動memcached後第一次運行該Java程序時,memcached.exe進程所佔用的內存會增大1MB,這是memcache內存分配模型所決定的。
應用舉例
Memcache作爲一個緩存的實現,最主要的功能就是將磁盤中數據(包括數據庫、文件系統以及運算的結果)保存在內存中,減少應用程序讀取/計算數據時訪問IO的次數,提升應用的響應速度,保證良好的用戶體驗,進而保證產品的競爭性。
通常來說,適合存放在緩存中的數據具有如下特點:1)頻繁讀取,2)很少更新,3)對實時性,一致性要求不是特別高
例如:在設計某個網站上的熱門新聞或熱門話題時,可以將對應的信息緩存在memcache中,根據實際情況設置過期時間,每個外部web請求到達應用服務時,直接從memcache中獲取,如果從memcache中獲取到了就直接返回給web 應用顯示在瀏覽器中,如果從memcache中沒有獲取到,則發起IO請求訪問數據庫,將查詢的數據緩存進memcache中,同時返回給web應用顯示在瀏覽器中。
下面使用mysql創建一個news表表示新聞信息,作爲演示該表僅包含了新聞的title和content,其中hits表示新聞點擊量,點擊量最多的前10條表示爲熱門新聞。
DB 層設計
create database memcache;
use memcache;
create table `news` (
`id` int primary key auto_increment,
`title` varchar(20) ,
`content` varchar(800) ,
`hits` int(11) ,
) ENGINE=InnoDB;
應用層設計
數據Model
News.java
package Model;
import java.io.Serializable;
public class News implements Serializable {
private int id;
private String title;
private String content;
private int hits;
//......省略get/set
public String toString(){
StringBuilder sb= new StringBuilder();
sb.append("[title: ");
sb.append(title);
sb.append(" ],[content: ");
sb.append(content);
sb.append("].");
return sb.toString();
}
}
數據訪問DAO
Note:這裏使用Mysql,因此該應用程序需要引入mysql對應的jar包。
MysqlHelper.java
package DAO;
import java.sql.*;
public class MysqlHelper {
public static Connection getConnection() {
Connection cn = null;
try {
Class.forName("com.mysql.jdbc.Driver");
cn = DriverManager
.getConnection("jdbc:mysql://localhost:3306/memcache?user=root&password=root");
} catch (ClassNotFoundException clazze) {
System.out.println("mysql driver class is not found");
} catch (SQLException sqle) {
System.out.println("mysql connection get failed!");
}
return cn;
}
public static Statement getStatement(Connection cn) {
Statement stmt = null;
if (cn != null) {
try {
stmt = cn.createStatement();
} catch (SQLException e) {
System.out
.println("mysql statement get error at getStatement!");
}
}
return stmt;
}
public static ResultSet executeQuery(Statement stmt, String sql) {
ResultSet rs = null;
if (stmt != null) {
try {
rs = stmt.executeQuery(sql);
} catch (SQLException e) {
System.out
.println("mysql statement execute error at getResultSet!");
}
}
return rs;
}
public static boolean executeUpdate(Statement stmt, String sql) {
int affectrows = 0;
if (stmt != null) {
try {
affectrows = stmt.executeUpdate(sql);
} catch (SQLException e) {
System.out
.println("mysql statement execute error at getResultSet!");
}
}
return affectrows > 0;
}
public static void close(ResultSet rs, Statement stmt) {
//略
}
public static void closeConnection(Connection cn) {
//略
}
}
MysqlNewsDAO.java
package DAO;
import Model.News;
import java.sql.*;
import java.util.*;
public class MysqlNewsDAO {
public List<News> getHotNews() {
List<News> news = new ArrayList<News>();
Connection cn = MysqlHelper.getConnection();
Statement stmt = MysqlHelper.getStatement(cn);
ResultSet rs = MysqlHelper.executeQuery(stmt,
"select id, title, content,hits from news order by hits desc limit 0,10");
try {
if (rs != null) {
while (rs.next()) {
News oneNews = new News();
oneNews.setId(rs.getInt(1));
oneNews.setTitle(rs.getString(2));
oneNews.setContent(rs.getString(3));
oneNews.setHits(rs.getInt(4));
news.add(oneNews);
}
}
} catch (SQLException e) {
System.out.println("Result Set Error!");
} finally {
MysqlHelper.close(rs, stmt);
MysqlHelper.closeConnection(cn);
}
return news;
}
public boolean addNews(News news) {
Connection cn = MysqlHelper.getConnection();
Statement stmt = MysqlHelper.getStatement(cn);
StringBuilder sql = new StringBuilder(
"insert into News(title,content,hits) values('");
sql.append(news.getTitle());
sql.append("','");
sql.append(news.getContent());
sql.append("',");
sql.append(news.getHits());
sql.append(")");
boolean ret = MysqlHelper.executeUpdate(stmt, sql.toString());
MysqlHelper.close(null, stmt);
MysqlHelper.closeConnection(cn);
return ret;
}
}
業務邏輯 BLL
package BLL;
import Model.News;
import Memcache.MemcacheUtility;
import DAO.MysqlNewsDAO;
import java.util.*;
public class NewsBLL {
@SuppressWarnings("unchecked")
public List<News> getHotNews() {
MemcacheUtility mem = MemcacheUtility.getMemcacheClientInstance();
Object newsInMem = mem.get("HotNews");
if (newsInMem != null) {
return (List<News>) newsInMem;
}
MysqlNewsDAO newsDAO = new MysqlNewsDAO();
List<News> news = newsDAO.getHotNews();
mem.set("HotNews", news);
return news;
}
public List<News> getHotNew2(){
MysqlNewsDAO newsDAO = new MysqlNewsDAO();
List<News> news = newsDAO.getHotNews();
return news;
}
public void addNews(News news){
MysqlNewsDAO newsDAO = new MysqlNewsDAO();
newsDAO.addNews(news);
}
}
Note:這裏的MemcacheUtility 就是操作示例中MemcacheUtility.java
數據展示UI
import Model.News;
import BLL.NewsBLL;
import java.util.*;
import Memcache.MemcacheUtility;
public class TestMemcaceAndJDBC {
public static void main(String[] args) {
NewsBLL newsBll = new NewsBLL();
//首先往數據庫中插入100條新聞數據
System.out.println("Begin to insert 100 news");
for(int i=0;i<100; i++){
News news = new News();
news.setTitle("title---"+i);
news.setContent("content---"+i);
news.setHits(i);
newsBll.addNews(news);
}
//使用memcache,獲取熱門新聞100次
System.out.print("Using Memcache to get hot news!");
long start = System.currentTimeMillis();
for(int i=0;i<100;i++){
List<News> news = newsBll.getHotNews();
for(News item : news){
//System.out.println(item);
}
}
long end = System.currentTimeMillis();
System.out.println("Using Memcache takes: " +(end-start));
System.out.println("\n----------------------\n");
//不使用memcache,直接訪問數據庫獲取熱門新聞100次
System.out.print("Not Using Memcache to get hot news!");
start = System.currentTimeMillis();
for(int i=0;i<100;i++){
List<News> news= newsBll.getHotNew2();
for(News item : news){
//System.out.println(item);
}
}
end = System.currentTimeMillis();
System.out.println("Not Using Memcache takes: " +(end-start));
}
}
通過運行程序,我們發現使用memcache能大大提前數據的訪問速度。