MemCached緩存操作


  Web項目在運行時,通常需要從數據庫中進行讀寫。隨着操作數據量的增大,以及訪問量的集中,數據庫的負載增加,數據庫響應變慢,網站訪問速度變慢的情況。Memcached就是用來解決這些問題的。

  Memcached是一個開源的高性能的分布式緩存系統。主要用於減輕數據庫負載,加速Web應用訪問。它是基於內存的Key-Value存儲系統,主要存儲Value較小的數據,Value大小不能超過1M。總的來說,Memcached支持的數據結構是鍵值對,在支持的數據結構上要比Redis簡單。

  Memcached的使用場景有:

  訪問頻度極高的業務,如社交網絡,電子商務,游戲,廣告等,可以將頻繁訪問的數據存儲到Memcached中,從而減少對數據庫的訪問。

  促銷類業務,秒殺類業務,這些業務訪問壓力非常大,一般數據庫根本無法承載這么大的業務量。

  計數器,如點贊人數,文章閱讀人數等。

  存儲小圖片,減輕硬盤存儲系統的訪問壓力。

  那么,我們為什么要基於內存來存儲呢?我們先來看一張比較計算機中各種存儲介質處理數據速率的圖片:  

  內存,磁盤,CPU的運行方式不同。磁盤是毫秒級,內存是微妙級,CPU是納秒級。可以說,比較內存和磁盤的速度差異,內存比磁盤快10萬~100萬倍。傳輸速度和總線的速度差異,雖然有SSDSolid 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<String> keys=new ArrayList<>();
        keys.add("data1");
        keys.add("data2");
        try {
            //從memcached中讀取字符串
            Map<String, Object> 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文件中的配置如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.itszt.DemoMC</groupId>
  <artifactId>DemoMC</artifactId>
  <version>1.0</version>
  <packaging>jar</packaging>

  <name>DemoMC</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>com.googlecode.xmemcached</groupId>
      <artifactId>xmemcached</artifactId>
      <version>2.3.2</version>
    </dependency>


    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.44</version>
    </dependency>

    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.2.8</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.39</version>
    </dependency>

    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-simple</artifactId>
      <version>1.7.25</version>
    </dependency>

    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>

  </dependencies>

  <build>
    <finalName>DemoMC</finalName>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.7</source>
          <target>1.7</target>
        </configuration>
      </plugin>
    </plugins>
  </build>

</project>

  接下來,我們再看mybatis-config.xml配置文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!--DTD約束引入-->
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<!--全局配置開始-->
<configuration>

    <!-- 運行環境設置  1.連接  2.事務-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/a0314?useUnicode=true&CharsetEncodig=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="2018"/>
            </dataSource>
        </environment>
    </environments>

    <!--  引入配置文件2-->
    <mappers>
        <package name="com.itszt.DemoMC.demo"></package>
    </mappers>
</configuration>

  在將上述配置文件完畢后,我們接下來寫一個小案例,其文件體系如下圖所示:

  我們接下來看代碼:  

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> 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配置內容:
<?xml version="1.0" encoding="UTF-8" ?>
<!--引入DTD約束-->
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itszt.DemoMC.demo.UserDaoDB">
</mapper>
************************************************
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緩存和數據庫。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM