Web项目在运行时,通常需要从数据库中进行读写。随着操作数据量的增大,以及访问量的集中,数据库的负载增加,数据库响应变慢,网站访问速度变慢的情况。Memcached就是用来解决这些问题的。
Memcached是一个开源的高性能的分布式缓存系统。主要用于减轻数据库负载,加速Web应用访问。它是基于内存的Key-Value存储系统,主要存储Value较小的数据,Value大小不能超过1M。总的来说,Memcached支持的数据结构是键值对,在支持的数据结构上要比Redis简单。
Memcached的使用场景有:
访问频度极高的业务,如社交网络,电子商务,游戏,广告等,可以将频繁访问的数据存储到Memcached中,从而减少对数据库的访问。
促销类业务,秒杀类业务,这些业务访问压力非常大,一般数据库根本无法承载这么大的业务量。
计数器,如点赞人数,文章阅读人数等。
存储小图片,减轻硬盘存储系统的访问压力。
那么,我们为什么要基于内存来存储呢?我们先来看一张比较计算机中各种存储介质处理数据速率的图片:
内存,磁盘,CPU的运行方式不同。磁盘是毫秒级,内存是微妙级,CPU是纳秒级。可以说,比较内存和磁盘的速度差异,内存比磁盘快10万~100万倍。传输速度和总线的速度差异,虽然有SSD(Solid State Drives,固态硬盘),但是其速度还是无法和内存相比。
在操作中,如果无法在内存中计算的话,就必须搜索磁盘上的数据,但是磁盘I/O输入输出非常耗时。一般网站的一个请求响应时间要控制在1秒内。
我们来看MemCached的物理架构:
使用Memcached时的数据读取流程是:先从Memcached中取数据,如果取不到再从数据库中取数据,然后把数据保存到Memcached中。
使用Memcached时的数据更新流程是:先更新数据库,然后再更新Memcached缓存,或者删除数据缓存。
我们接下来看Memcached的应用:
首先,要安装Memcached服务器。我们以win7系统为例,切换到Memcached路径,在DOS窗口的命令行中执行安装与启动命令:
#安装Memcached服务器
memcached -d install
#启动Memcached服务
net strat "memcached server"
安装与启动Memcached服务后,我们接下来在idea开发工具中创建一个maven项目,在里面通过代码查看Memcached的基本操作:
package com.itszt.DemoMC.domain;
/**
* 订单实体类,id,名称,下订单时间
*/
public class OrderRequest {
private int orderId;
private String orderName,orderTime;
public OrderRequest() {
}
public OrderRequest(int orderId, String orderName, String orderTime) {
this.orderId = orderId;
this.orderName = orderName;
this.orderTime = orderTime;
}
public int getOrderId() {
return orderId;
}
@Override
public String toString() {
return "OrderRequest{" +
"orderId=" + orderId +
", orderName='" + orderName + '\'' +
", orderTime='" + orderTime + '\'' +
'}';
}
public void setOrderId(int orderId) {
this.orderId = orderId;
}
public String getOrderName() {
return orderName;
}
public void setOrderName(String orderName) {
this.orderName = orderName;
}
public String getOrderTime() {
return orderTime;
}
public void setOrderTime(String orderTime) {
this.orderTime = orderTime;
}
}
********************************************
package com.itszt.DemoMC;
import com.alibaba.fastjson.JSON;
import com.itszt.DemoMC.domain.OrderRequest;
import net.rubyeye.xmemcached.MemcachedClient;
import net.rubyeye.xmemcached.XMemcachedClientBuilder;
import net.rubyeye.xmemcached.exception.MemcachedException;
import net.rubyeye.xmemcached.utils.AddrUtil;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeoutException;
/**
* MemCached的基本操作
*/
public class App {
public static void main(String[] args) {
//创建操作MemCached的客户端
XMemcachedClientBuilder memcachedClientBuilder = new XMemcachedClientBuilder(AddrUtil.getAddresses("192.168.1.160:11211"));
MemcachedClient memcachedClient =null;
try {
memcachedClient = memcachedClientBuilder.build();
System.out.println("memcachedClient = " + memcachedClient);
boolean booDel = memcachedClient.delete("data1");
if(booDel){
System.out.println("删除成功");
}else{
System.out.println("无此数据");
}
//向MemCached中插入字符串数据
/*memcachedClient.add("data1",0,"测试数据1");
memcachedClient.add("data2",0,"测试数据2");*/
/*memcachedClient.set("data1",0,"测试数据1");
memcachedClient.set("data2",0,"测试数据2");*/
} catch (IOException e) {
e.printStackTrace();
} /*catch (InterruptedException e) {
e.printStackTrace();
} catch (MemcachedException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}*/ catch (InterruptedException e) {
e.printStackTrace();
} catch (MemcachedException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
//从MemCached中取单个数据
/*try {
Object data1 = memcachedClient.get("data1");
System.out.println("data1 = " + data1);
} catch (TimeoutException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (MemcachedException e) {
e.printStackTrace();
}*/
//取出一批数据
List keys=new ArrayList<>();
keys.add("data1");
keys.add("data2");
try {
//从memcached中读取字符串
Map objectMap = memcachedClient.get(keys);
System.out.println("objectMap = " + objectMap);
} catch (TimeoutException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (MemcachedException e) {
e.printStackTrace();
}
//存储对象
OrderRequest orderRequest = new OrderRequest(1, "大黄", new Date().toString());
System.out.println(JSON.toJSON(orderRequest).toString());
try {
memcachedClient.set("order_"+orderRequest.getOrderId(),0, JSON.toJSON(orderRequest).toString());
} catch (TimeoutException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (MemcachedException e) {
e.printStackTrace();
}
try {
String obj = memcachedClient.get("order_" + orderRequest.getOrderId());
OrderRequest orderFromJson = JSON.parseObject(obj, OrderRequest.class);
System.out.println("orderFromJson = " + orderFromJson);
} catch (TimeoutException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (MemcachedException e) {
e.printStackTrace();
}
}
}
上面代码是MemCached的基本操作,我们在工作中是要结合数据库来使用的,为此,我们在maven中配置mybatis环境,同时在数据库中建一张测试表user(int uid,varchar username,varchar userpwd)。在maven项目的pom.xml文件中的配置如下:
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
com.itszt.DemoMC
DemoMC
1.0
jar
DemoMC
http://maven.apache.org
UTF-8
junit
junit
3.8.1
test
com.googlecode.xmemcached
xmemcached
2.3.2
com.alibaba
fastjson
1.2.44
org.mybatis
mybatis
3.2.8
mysql
mysql-connector-java
5.1.39
org.slf4j
slf4j-simple
1.7.25
log4j
log4j
1.2.17
DemoMC
org.apache.maven.plugins
maven-compiler-plugin
1.7
1.7
接下来,我们再看mybatis-config.xml配置文件:
/p>
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
在将上述配置文件完毕后,我们接下来写一个小案例,其文件体系如下图所示:
我们接下来看代码:
package com.itszt.DemoMC.util;
import net.rubyeye.xmemcached.MemcachedClient;
import net.rubyeye.xmemcached.XMemcachedClientBuilder;
import net.rubyeye.xmemcached.exception.MemcachedException;
import net.rubyeye.xmemcached.utils.AddrUtil;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* memcached工具类
*/
public class MCUtil {
private static MemcachedClient memcachedClient =null;
static {
XMemcachedClientBuilder memcachedClientBuilder = new XMemcachedClientBuilder(AddrUtil.getAddresses("192.168.1.160:11211"));
try {
memcachedClient = memcachedClientBuilder.build();
} catch (IOException e) {
e.printStackTrace();
}
}
public static boolean setData(String key,int expire,Object obj){
try {
memcachedClient.set(key,expire,obj);
return true;
} catch (TimeoutException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (MemcachedException e) {
e.printStackTrace();
}
return false;
}
public static T getData(String key){
try {
return memcachedClient.get(key);
} catch (TimeoutException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (MemcachedException e) {
e.printStackTrace();
}
return null;
}
}
*********************************************
package com.itszt.DemoMC.demo;
import java.io.Serializable;
/**
* 实体类,映射数据库a0314中的user表
*/
public class User implements Serializable{
private int uid;
private String username,userpwd;
public User() {
}
@Override
public String toString() {
return "User{" +
"uid=" + uid +
", username='" + username + '\'' +
", userpwd='" + userpwd + '\'' +
'}';
}
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getUserpwd() {
return userpwd;
}
public void setUserpwd(String userpwd) {
this.userpwd = userpwd;
}
}
************************************************
package com.itszt.DemoMC.demo;
/**
* 接口,操作数据库
*/
public interface UserDao {
//根据用户名和密码查询用户
public User findUserByNameAndPwd(String username,String userpwd);
//根据uid修改用户名
public boolean resetUsername(int uid,String username);
}
*************************************************
package com.itszt.DemoMC.demo;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
/**
* 操作数据库用
*/
public interface UserDaoDB extends UserDao{
@Override
@Select("select * from user where username=#{username} and userpwd=#{userpwd}")
User findUserByNameAndPwd(@Param("username") String username,@Param("userpwd") String userpwd);
@Override
@Update("update user set username=#{username} where uid=#{uid}")
boolean resetUsername(@Param("uid") int uid,@Param("username") String username);
}
************************************************
UserDaoDB.xml配置内容:
/p>
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
************************************************
package com.itszt.DemoMC.demo;
import com.itszt.DemoMC.util.MCUtil;
/**
* 操作MemCached缓存
*/
public class UserDaoCached implements UserDao{
@Override
public User findUserByNameAndPwd(String username, String userpwd) {
return MCUtil.getData("user_"+username+"_"+userpwd);
}
@Override
public boolean resetUsername(int uid, String username) {
boolean boo = MCUtil.setData("user_" + uid, 0, username);
return boo;
}
}
************************************************
package com.itszt.DemoMC.demo;
import com.itszt.DemoMC.util.MCUtil;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
/**
* UserDao的实现类
*/
public class UserDaoImpl implements UserDao{
private UserDaoCached userDaoCached;
private UserDaoDB userDaoDB;
SqlSession sqlSession;
public UserDaoImpl(){
userDaoCached=new UserDaoCached();
String resource="mybatis-config.xml";
InputStream inputStream=null;
try {
inputStream=Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
sqlSession = sqlSessionFactory.openSession();
userDaoDB= sqlSession.getMapper(UserDaoDB.class);
}
@Override
public User findUserByNameAndPwd(String username, String userpwd) {
User userByNameAndPwd=null;
//先从缓存查找,若缓存没有,则再从数据库查找
userByNameAndPwd=userDaoCached.findUserByNameAndPwd(username,userpwd);
if(userByNameAndPwd==null){
System.out.println("缓存里没有,从数据库中查找");
userByNameAndPwd=userDaoDB.findUserByNameAndPwd(username,userpwd);
if(userByNameAndPwd==null){
System.out.println("数据库无匹配项");
return null;
}
//从数据库查找到后,再添加入缓存
MCUtil.setData("user_"+username+"_"+userpwd,0,userByNameAndPwd);
System.out.println("成功保存到缓存");
}
System.out.println("从缓存获取");
return userByNameAndPwd;
}
@Override
public boolean resetUsername(int uid, String username) {
//先更新数据库,再更新缓存
try {
boolean boo = userDaoDB.resetUsername(uid, username);
if(boo){
sqlSession.commit();
userDaoCached.resetUsername(uid,username);
return true;
}else{
System.out.println("数据更新失败");
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
}
***********************************************
package com.itszt.DemoMC.demo;
/**
* 测试类
*/
public class Test {
public static void main(String[] args) {
//操作UserDaoImpl实现类
UserDaoImpl userDao=new UserDaoImpl();
User userByNameAndPwd = userDao.findUserByNameAndPwd("admin", "123456");
System.out.println("userByNameAndPwd = " + userByNameAndPwd);
//更新数据库中的数据,并更新到缓存中
boolean boo = userDao.resetUsername(userByNameAndPwd.getUid(), "admin123");
if(boo){
System.out.println("数据更新完毕");
User user = userDao.findUserByNameAndPwd("admin123", "123456");
System.out.println("user = " + user);
}else{
System.out.println("数据更新失败");
}
}
}
运行上述代码中的测试类Test后,显示结果如下:
从缓存获取
userByNameAndPwd = User{uid=1, username='admin', userpwd='123456'}
数据更新完毕
缓存里没有,从数据库中查找
成功保存到缓存
从缓存获取
user = User{uid=1, username='admin123', userpwd='123456'}
此时已功地实现了操作MemCached缓存和数据库。