关于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条表示为热门新闻。
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;
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();
}
}
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 getHotNews() {
List news = new ArrayList();
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;
}
}
package BLL;
import Model.News;
import Memcache.MemcacheUtility;
import DAO.MysqlNewsDAO;
import java.util.*;
public class NewsBLL {
@SuppressWarnings("unchecked")
public List getHotNews() {
MemcacheUtility mem = MemcacheUtility.getMemcacheClientInstance();
Object newsInMem = mem.get("HotNews");
if (newsInMem != null) {
return (List) newsInMem;
}
MysqlNewsDAO newsDAO = new MysqlNewsDAO();
List news = newsDAO.getHotNews();
mem.set("HotNews", news);
return news;
}
public List getHotNew2(){
MysqlNewsDAO newsDAO = new MysqlNewsDAO();
List news = newsDAO.getHotNews();
return news;
}
public void addNews(News news){
MysqlNewsDAO newsDAO = new MysqlNewsDAO();
newsDAO.addNews(news);
}
}
Note:这里的MemcacheUtility 就是操作示例中MemcacheUtility.java
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 = 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= newsBll.getHotNew2();
for(News item : news){
//System.out.println(item);
}
}
end = System.currentTimeMillis();
System.out.println("Not Using Memcache takes: " +(end-start));
}
}
通过运行程序,我们发现使用memcache能大大提前数据的访问速度。