1. 程式人生 > >spring4.0.9結合redis進行資料的快取

spring4.0.9結合redis進行資料的快取

一、今天將以前公司的由SpringMVC搭建的框架和redis整合。Spring版本為4.0.9  redis安裝在linux(CentOS6.5)下面。資料庫使用mysql。

由於redis,

 1、優異的讀寫效能

我在這裡使用了Redis-benchmark這個自帶的效能測試工具進行了測試,由於是學習的機子,效能一般所以測試結果

測試命令: redis-benchmark -h 192.168.100.131 -p 6379 -c 100 -n 100000

100個併發連線,100000個請求,檢測host為localhost 埠為6379的redis伺服器效能

====== SET ======   

100000 requests completed in 5.15 seconds

19432.57 requests per second

====== GET ======
  100000 requests completed in 5.51 seconds

18162.01 requests per second

每秒鐘可以set 或者get差不多2萬條。

2 支援資料持久化,支援AOF和RDB兩種持久化方式
3 支援主從複製,主機會自動將資料同步到從機,可以進行讀寫分離。
4 資料結構豐富:string(字串)、list(連結串列)、set(集合)、zset(sorted set --有序集合)和hash(雜湊型別)

該專案是一個作品投票的專案,是一個網際網路專案的一個子專案,我們知道作品投票有個特點就是:作品這個表是經常被查詢,資料基本不變,我們就沒有必要每次使用者進入頁面的時候就進行資料庫查詢,我們只要在redis中直接讀取就可以了,下面我通過這個專案進行spring和redis進行整合。

1、專案的POM.xml檔案加入所需要的jar包

<dependency>
		    <groupId>org.apache.commons</groupId>
		    <artifactId>commons-pool2</artifactId>
		    <version>2.4.2</version>
		</dependency> 
		<dependency>
		    <groupId>redis.clients</groupId>
		    <artifactId>jedis</artifactId>
		    <version>2.8.0</version>
		</dependency>
	
		<dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-redis</artifactId>
        <version>1.6.6.RELEASE</version>
    </dependency> 

2、為了便於redis配置的管理,我們把一些配置資訊單獨出來放入redis.properties
redis.hostName=192.168.100.131
redis.port=6379
redis.timeout=15000
redis.usePool=true
redis.maxIdle=6
redis.minEvictableIdleTimeMillis=300000
redis.numTestsPerEvictionRun=3
redis.timeBetweenEvictionRunsMillis=60000
3、需要加入redis-context.xml 配置如下
<beans xmlns="http://www.springframework.org/schema/beans" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" default-autowire="byName"> 
 <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> 
  <!-- <property name="maxIdle" value="6"></property> 
  <property name="minEvictableIdleTimeMillis" value="300000"></property> 
  <property name="numTestsPerEvictionRun" value="3"></property> 
  <property name="timeBetweenEvictionRunsMillis" value="60000"></property> -->
  
  <property name="maxIdle" value="${redis.maxIdle}"></property> 
  <property name="minEvictableIdleTimeMillis" value="${redis.minEvictableIdleTimeMillis}"></property> 
  <property name="numTestsPerEvictionRun" value="${redis.numTestsPerEvictionRun}"></property> 
  <property name="timeBetweenEvictionRunsMillis" value="${redis.timeBetweenEvictionRunsMillis}"></property>
 </bean> 
 <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" destroy-method="destroy"> 
  <property name="poolConfig" ref="jedisPoolConfig"></property> 
  <property name="hostName" value="${redis.hostName}"></property> 
  <property name="port" value="${redis.port}"></property> 
  <property name="timeout" value="${redis.timeout}"></property> 
  <property name="usePool" value="${redis.usePool}"></property>
  <property name="password" value="${redis.password}"></property>
  
 </bean> 
 <bean id="jedisTemplate" class="org.springframework.data.redis.core.RedisTemplate"> 
  <property name="connectionFactory" ref="jedisConnectionFactory"></property> 
  <property name="keySerializer"> 
   <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/> 
  </property> 
  <property name="valueSerializer"> 
   <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/> 
  </property> 
 </bean> 
</beans> 

4、在Spring配置檔案中加入讀取redis.properties 和redis-context.xml程式碼

<bean
		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations">
			<list>				
				<value>classpath:/config/redis.properties</value> 
			</list>
		</property>
	</bean>
<import resource="classpath*:modules/redis-context.xml" />
這樣以上環境就配置好了。下面我們加入程式碼進行測試

5、這裡需要加入監聽器,這個監聽器的作用是我們啟動專案的時候會把資料庫中的資料(這些資料基本不變),放入redis中,下次們去拿的資料,可以直接總redis中獲得,無需進入資料庫中讀取。

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Service;


@Service
public class StartAddCacheListener implements ApplicationListener<ContextRefreshedEvent> {
	// 日誌
	private final Logger log = Logger.getLogger(StartAddCacheListener.class);

	@Autowired
	private RedisCacheUtil<Object> redisCache;

	@Autowired
	private VoteService voteService;

	@Override
	public void onApplicationEvent(ContextRefreshedEvent event) {
		if (event.getApplicationContext().getDisplayName().equals("Root WebApplicationContext")) {
			System.out.println("-------------快取資料-------------");
			List<VoteProject> voteProjectList = voteService.getAllProject();

			Map<Integer, VoteProject> voteProjectMap = new HashMap<Integer, VoteProject>();

			int cityListSize = voteProjectList.size();

			for (int i = 0; i < cityListSize; i++) {
				voteProjectMap.put(voteProjectList.get(i).getId(), voteProjectList.get(i));
			}

			redisCache.setCacheIntegerMap("voteProjectMap", voteProjectMap);
		}
	}

}

6、將該監聽器放入spring配置檔案中
	<bean id="startAddCacheListener" class="com.eshine.vote.redis.listener.StartAddCacheListener"></bean>
7、新建一個util快取工具類
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

@Service
public class RedisCacheUtil<T> {

	@Autowired
	@Qualifier("jedisTemplate")
	public RedisTemplate redisTemplate;

	/**
	 * 快取基本的物件,Integer、String、實體類等
	 * 
	 * @param key
	 *            快取的鍵值
	 * @param value
	 *            快取的值
	 * @return 快取的物件
	 */
	public <T> ValueOperations<String, T> setCacheObject(String key, T value) {

		ValueOperations<String, T> operation = redisTemplate.opsForValue();
		operation.set(key, value);
		return operation;
	}

	/**
	 * 獲得快取的基本物件。
	 * 
	 * @param key
	 *            快取鍵值
	 * @param operation
	 * @return 快取鍵值對應的資料
	 */
	public <T> T getCacheObject(String key/* ,ValueOperations<String,T> operation */) {
		ValueOperations<String, T> operation = redisTemplate.opsForValue();
		return operation.get(key);
	}

	/**
	 * 快取List資料
	 * 
	 * @param key
	 *            快取的鍵值
	 * @param dataList
	 *            待快取的List資料
	 * @return 快取的物件
	 */
	public <T> ListOperations<String, T> setCacheList(String key, List<T> dataList) {
		ListOperations listOperation = redisTemplate.opsForList();
		if (null != dataList) {
			int size = dataList.size();
			for (int i = 0; i < size; i++) {

				listOperation.rightPush(key, dataList.get(i));
			}
		}

		return listOperation;
	}

	/**
	 * 獲得快取的list物件
	 * 
	 * @param key
	 *            快取的鍵值
	 * @return 快取鍵值對應的資料
	 */
	public <T> List<T> getCacheList(String key) {
		List<T> dataList = new ArrayList<T>();
		ListOperations<String, T> listOperation = redisTemplate.opsForList();
		Long size = listOperation.size(key);

		for (int i = 0; i < size; i++) {
			dataList.add((T) listOperation.leftPop(key));
		}

		return dataList;
	}

	/**
	 * 快取Set
	 * 
	 * @param key
	 *            快取鍵值
	 * @param dataSet
	 *            快取的資料
	 * @return 快取資料的物件
	 */
	public <T> BoundSetOperations<String, T> setCacheSet(String key, Set<T> dataSet) {
		BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
		/*
		 * T[] t = (T[]) dataSet.toArray(); setOperation.add(t);
		 */

		Iterator<T> it = dataSet.iterator();
		while (it.hasNext()) {
			setOperation.add(it.next());
		}

		return setOperation;
	}

	/**
	 * 獲得快取的set
	 * 
	 * @param key
	 * @param operation
	 * @return
	 */
	public Set<T> getCacheSet(String key/*
										 * ,BoundSetOperations<String,T>
										 * operation
										 */) {
		Set<T> dataSet = new HashSet<T>();
		BoundSetOperations<String, T> operation = redisTemplate.boundSetOps(key);

		Long size = operation.size();
		for (int i = 0; i < size; i++) {
			dataSet.add(operation.pop());
		}
		return dataSet;
	}

	/**
	 * 快取Map
	 * 
	 * @param key
	 * @param dataMap
	 * @return
	 */
	public <T> HashOperations<String, String, T> setCacheMap(String key, Map<String, T> dataMap) {

		HashOperations hashOperations = redisTemplate.opsForHash();
		if (null != dataMap) {

			for (Map.Entry<String, T> entry : dataMap.entrySet()) {

				/*
				 * System.out.println("Key = " + entry.getKey() + ", Value = " +
				 * entry.getValue());
				 */
				hashOperations.put(key, entry.getKey(), entry.getValue());
			}

		}

		return hashOperations;
	}

	/**
	 * 獲得快取的Map
	 * 
	 * @param key
	 * @param hashOperation
	 * @return
	 */
	public <T> Map<String, T> getCacheMap(String key/*
													 * ,HashOperations<String,String
													 * ,T> hashOperation
													 */) {
		Map<String, T> map = redisTemplate.opsForHash().entries(key);
		/* Map<String, T> map = hashOperation.entries(key); */
		return map;
	}

	/**
	 * 快取Map
	 * 
	 * @param key
	 * @param dataMap
	 * @return
	 */
	public <T> HashOperations<String, Integer, T> setCacheIntegerMap(String key, Map<Integer, T> dataMap) {
		HashOperations hashOperations = redisTemplate.opsForHash();
		if (null != dataMap) {

			for (Map.Entry<Integer, T> entry : dataMap.entrySet()) {

				/*
				 * System.out.println("Key = " + entry.getKey() + ", Value = " +
				 * entry.getValue());
				 */
				hashOperations.put(key, entry.getKey(), entry.getValue());
			}

		}

		return hashOperations;
	}

	/**
	 * 獲得快取的Map
	 * 
	 * @param key
	 * @param hashOperation
	 * @return
	 */
	public <T> Map<Integer, T> getCacheIntegerMap(String key/*
															 * ,HashOperations<
															 * String,String,T>
															 * hashOperation
															 */) {
		Map<Integer, T> map = redisTemplate.opsForHash().entries(key);
		/* Map<String, T> map = hashOperation.entries(key); */
		return map;
	}

}

8、測試類
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;



@Controller("/RedisTest")
@RequestMapping({ "/RedisTest*" })
public class RedisTest {

	@Autowired
	private RedisCacheUtil<Object> redisCache;

	@RequestMapping("/testGetCache")
	public ModelAndView testGetCache(HttpServletRequest req, HttpServletResponse rsp) {

		ModelAndView mav = new ModelAndView("html/website/index");

		Map<Integer, VoteProject> voteProjectMap = redisCache.getCacheIntegerMap("voteProjectMap");

		for (int key : voteProjectMap.keySet()) {
			System.out.println("key = " + key + ",value=" + voteProjectMap.get(key).getContent());
		}
		return mav;
	}

}
以上我們已經將程式碼以及配置全部搭建好了,現在我資料庫中一共有17條資料。如下圖所示


下面我啟動專案,日誌提示將讀取17條資料


 9、上面提到,有個測試工具類,我們觸發它,檢視控制檯列印資料

我這裡資料庫並不多,只有十幾條,如果數量達到幾百萬上千萬的時候,我每次重啟專案都去查詢資料庫的一千萬條記錄,顯然不合理,所以,對這個專案整合只是一個開始,後面我會嘗試在表中加入大量資料,並且對資料進行持久化,如果有時間和機會會在專案嘗試redis的主從複製以及讀寫分離等等。