上一篇總結了redis sentinel(哨兵方案)的配置流程,本篇就redis整合ssm框架進行說明。目前,大多數公司用redis主要做緩存用,對於那些不常變動的數據來說,我們將其緩存在redis中,可以大大減少數據庫的壓力。
一、Spring集成redis
1.在resource目錄下創建spring-redis.xml文件,內容如下:
- <?xml version="1.0" encoding="UTF-8"?>
- <beansxmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"
- xmlns:p="http://www.springframework.org/schema/p"
- xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
- http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
- http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
- <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
- <property name="maxIdle" value="2000" />
- <property name="maxTotal" value="20000" />
- <property name="minEvictableIdleTimeMillis" value="300000"></property>
- <property name="numTestsPerEvictionRun" value="3"></property>
- <property name="timeBetweenEvictionRunsMillis" value="60000"></property>
- <property name="maxWaitMillis" value="20000" />
- <property name="testOnBorrow" value="false" />
- </bean>
- <bean id="sentinelConfig"
- class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
- <property name="master">
- <bean class="org.springframework.data.redis.connection.RedisNode">
- <property name="name" value="mymaster"></property>
- </bean>
- </property>
- <property name="sentinels">
- <set>
- <bean class="org.springframework.data.redis.connection.RedisNode">
- <constructor-arg name="host" value="192.168.12.90" />
- <constructor-arg name="port" value="7505" />
- </bean>
- <bean class="org.springframework.data.redis.connection.RedisNode">
- <constructor-arg name="host" value="192.168.12.90" />
- <constructor-arg name="port" value="7506" />
- </bean>
- </set>
- </property>
- </bean>
- <!-- 在此將sentinel配置集成到redis連接池中 -->
- <bean id="jedisConnectionFactory"
- class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
- <property name="timeout" value="20000"></property>
- <property name="poolConfig" ref="jedisPoolConfig"></property>
- <constructor-arg name="sentinelConfig" ref="sentinelConfig"></constructor-arg>
- </bean>
- <bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
- <property name="connectionFactory" ref="jedisConnectionFactory" />
- </bean>
- </beans>
在上述配置中,我們用jedis 池化管理方案,將sentinel納入配置中去,這樣就不用代碼中用JedisSentinelPool了,直接用JedisPool就ok了。別忘了,將此文件加入web.xml中:
- <context-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>classpath:spring-mybatis.xml,classpath:spring-redis.xml</param-value>
- </context-param>
2.測試
新建測試類,內容如下:
- package com.dg;
- import java.util.Collection;
- import java.util.List;
- import java.util.Map;
- import java.util.Set;
- import javax.annotation.Resource;
- import org.junit.Test;
- import org.junit.runner.RunWith;
- import org.springframework.data.redis.connection.RedisSentinelConnection;
- import org.springframework.data.redis.connection.RedisServer;
- import org.springframework.data.redis.core.StringRedisTemplate;
- import org.springframework.test.context.ContextConfiguration;
- import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
- import com.alibaba.fastjson.JSONObject;
- import com.dg.bean.User;
- @RunWith(SpringJUnit4ClassRunner.class)
- @ContextConfiguration(locations = { "classpath:spring-redis.xml" })
- public class SpringRedis {
- @Resource(name = "stringRedisTemplate")
- private StringRedisTemplate stringRedisTemplate;
- /**
- * redis 讀寫測試
- */
- @Test
- public void testSpringRedis() {
- try {
- // ApplicationContext context = new
- // ClassPathXmlApplicationContext("spring-redis.xml");
- // StringRedisTemplate stringRedisTemplate =
- // context.getBean("stringRedisTemplate",
- // StringRedisTemplate.class);
- // String讀寫
- stringRedisTemplate.delete("myStr");
- stringRedisTemplate.opsForValue().set("myStr", "http://yjmyzz.cnblogs.com/");
- System.out.println(stringRedisTemplate.opsForValue().get("myStr"));
- System.out.println("---------------");
- // List讀寫
- stringRedisTemplate.delete("myList");
- stringRedisTemplate.opsForList().rightPush("myList", "A");
- stringRedisTemplate.opsForList().rightPush("myList", "B");
- stringRedisTemplate.opsForList().leftPush("myList", "0");
- List<String> listCache = stringRedisTemplate.opsForList().range("myList", 0, -1);
- for (String s : listCache) {
- System.out.println(s);
- }
- System.out.println("---------------");
- // Set讀寫
- stringRedisTemplate.delete("mySet");
- stringRedisTemplate.opsForSet().add("mySet", "A");
- stringRedisTemplate.opsForSet().add("mySet", "B");
- stringRedisTemplate.opsForSet().add("mySet", "C");
- Set<String> setCache = stringRedisTemplate.opsForSet().members("mySet");
- for (String s : setCache) {
- System.out.println(s);
- }
- System.out.println("---------------");
- // Hash讀寫
- stringRedisTemplate.delete("myHash");
- stringRedisTemplate.opsForHash().put("myHash", "PEK", "北京");
- stringRedisTemplate.opsForHash().put("myHash", "SHA", "上海虹橋");
- stringRedisTemplate.opsForHash().put("myHash", "PVG", "浦東");
- Map<Object, Object> hashCache = stringRedisTemplate.opsForHash().entries("myHash");
- for (Map.Entry<Object, Object> entry : hashCache.entrySet()) {
- System.out.println(entry.getKey() + " - " + entry.getValue());
- }
- System.out.println("---------------");
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- /**
- * redis 得到所有的master and slave 信息
- */
- @Test
- public void testGetAllMasterAndSlave() {
- RedisSentinelConnection conn = stringRedisTemplate.getConnectionFactory().getSentinelConnection();
- for (RedisServer master : conn.masters()) {
- System.out.println("master => " + master);// 打印master信息
- Collection<RedisServer> slaves = conn.slaves(master);
- // 打印該master下的所有slave信息
- for (RedisServer slave : slaves) {
- System.out.println("slaves of " + master + " => " + slave);
- }
- System.out.println("--------------");
- }
- }
- /*
- * 測試redis 緩存object 和 list 類型數據(方案:用json將object和list序列化)
- */
- @Test
- public void testRedisCacheObjectAndList() {
- User user1 = new User("zhangsan", "123456", "[email protected]", "15824812342", 'M', 22);
- // // fastJson 序列化
- // String jsonStr = JSONObject.toJSONString(user1);
- // System.out.println(">>>>>>>>>>>>>>>>" + jsonStr);
- // // fastJson 反序列化
- // user1 = JSONObject.parseObject(jsonStr, User.class);
- // System.out.println(">>>>>>>>>>>>>>>>>>" + user1);
- stringRedisTemplate.delete("user1");
- // 將object 用 json 序列化後保存redis
- stringRedisTemplate.opsForValue().set("user1", JSONObject.toJSONString(user1));
- user1 = JSONObject.parseObject(stringRedisTemplate.opsForValue().get("user1"), User.class);
- System.out.println("-----------------------" + user1);
- }
- }
- /**測試redis客戶端*/
- @Test
- public void testRedis(){
- Jedis jedis = new Jedis("192.168.12.90", 6379);
- jedis.set("name", "mrdg");
- jedis.set("age", "24");
- System.out.println("name:"+jedis.get("name"));
- System.out.println("age:"+jedis.get("age"));
- System.out.println("tel:"+jedis.get("Tel"));
- System.out.println("no:"+jedis.get("No"));
- }
- /**測試redis集羣方案*/
- @Test
- public void testCluster(){
- Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>();
- //Jedis Cluster will attempt to discover cluster nodes automatically
- jedisClusterNodes.add(new HostAndPort("192.168.12.90", 7001));
- JedisCluster jc = new JedisCluster(jedisClusterNodes);
- jc.set("foo", "bar");
- String value = jc.get("foo");
- System.out.println(value);
- try {
- jc.close();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
上面的測試類,cluster方案集羣環境爲(詳見http://blog.csdn.net/donggang1992/article/details/50981954):
sentinel方案環境爲(詳見http://blog.csdn.net/donggang1992/article/details/50981341):
二、mybatis集成redis進行緩存配置
1.mybatis開啓緩存支持
在spring-mabatis.xml中添加下列內容:
- <!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 -->
- <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
- <property name="dataSource" ref="dataSource" />
- <!-- 自動掃描mapping.xml文件 -->
- <property name="mapperLocations" value="classpath:com/dg/mapping/*.xml"></property>
- <!-- 開啓緩存支持 -->
- <property name="configurationProperties">
- <props>
- <prop key="cacheEnabled">true</prop>
- <!-- 查詢時,關閉關聯對象即時加載以提高性能 -->
- <prop key="lazyLoadingEnabled">false</prop>
- <!-- 設置關聯對象加載的形態,此處爲按需加載字段(加載字段由SQL指定),不會加載關聯表的所有字段,以提高性能 -->
- <prop key="aggressiveLazyLoading">true</prop>
- <!-- 對於未知的SQL查詢,允許返回不同的結果集以達到通用的效果 -->
- <prop key="multipleResultSetsEnabled">true</prop>
- <!-- 允許使用列標籤代替列名 -->
- <prop key="useColumnLabel">true</prop>
- <!-- 允許使用自定義的主鍵值(比如由程序生成的UUID 32位編碼作爲鍵值),數據表的PK生成策略將被覆蓋 -->
- <prop key="useGeneratedKeys">true</prop>
- <!-- 給予被嵌套的resultMap以字段-屬性的映射支持 -->
- <prop key="autoMappingBehavior">FULL</prop>
- <!-- 對於批量更新操作緩存SQL以提高性能 -->
- <prop key="defaultExecutorType">BATCH</prop>
- <!-- 數據庫超過25000秒仍未響應則超時 -->
- <prop key="defaultStatementTimeout">25000</prop>
- </props>
- </property>
- </bean>
2.新建cache包,並創建RedisCache類
RedisCache類的內容爲:
- package com.dg.cache;
- import java.util.Set;
- import java.util.concurrent.locks.ReadWriteLock;
- import java.util.concurrent.locks.ReentrantReadWriteLock;
- import org.apache.commons.codec.digest.DigestUtils;
- import org.apache.ibatis.cache.Cache;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- import com.dg.util.SerializeUtil;
- import redis.clients.jedis.Jedis;
- import redis.clients.jedis.JedisPool;
- import redis.clients.jedis.JedisPoolConfig;
- /*
- * 使用第三方緩存服務器,處理二級緩存
- */
- publicclass RedisCache implements Cache {
- privatestatic Logger logger = LoggerFactory.getLogger(RedisCache.class);
- /** The ReadWriteLock. */
- privatefinal ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
- private String id;
- private JedisPool jedisPool;
- privatestaticfinalint DB_INDEX = 1;
- privatefinal String COMMON_CACHE_KEY = "COM:";
- privatestaticfinal String UTF8 = "utf-8";
- private ApplicationContext context;
- /**
- * 按照一定規則標識key
- */
- private String getKey(Object key) {
- StringBuilder accum = new StringBuilder();
- accum.append(COMMON_CACHE_KEY);
- accum.append(this.id).append(":");
- accum.append(DigestUtils.md5Hex(String.valueOf(key)));
- return accum.toString();
- }
- /**
- * redis key規則前綴
- */
- private String getKeys() {
- return COMMON_CACHE_KEY + this.id + ":*";
- }
- publicRedisCache() {
- }
- publicRedisCache(final String id) {
- if (id == null) {
- thrownew IllegalArgumentException("必須傳入ID");
- }
- context = new ClassPathXmlApplicationContext("spring-redis.xml");
- JedisPoolConfig jedisPoolConfig = (JedisPoolConfig) context.getBean("jedisPoolConfig");
- jedisPool = new JedisPool(jedisPoolConfig, "192.168.12.90", 7504);
- logger.debug(">>>>>>>>>>>>>>>>>>>>>MybatisRedisCache:id=" + id);
- this.id = id;
- }
- @Override
- public String getId() {
- returnthis.id;
- }
- @Override
- publicintgetSize() {
- Jedis jedis = null;
- int result = 0;
- boolean borrowOrOprSuccess = true;
- try {
- jedis = jedisPool.getResource();
- jedis.select(DB_INDEX);
- Set<byte[]> keys = jedis.keys(getKeys().getBytes(UTF8));
- if (null != keys && !keys.isEmpty()) {
- result = keys.size();
- }
- logger.debug(this.id + "---->>>>總緩存數:" + result);
- } catch (Exception e) {
- borrowOrOprSuccess = false;
- if (jedis != null)
- jedisPool.returnBrokenResource(jedis);
- } finally {
- if (borrowOrOprSuccess)
- jedisPool.returnResource(jedis);
- }
- return result;
- }
- @Override
- publicvoidputObject(Object key, Object value) {
- Jedis jedis = null;
- boolean borrowOrOprSuccess = true;
- try {
- jedis = jedisPool.getResource();
- jedis.select(DB_INDEX);
- byte[] keys = getKey(key).getBytes(UTF8);
- jedis.set(keys, SerializeUtil.serialize(value));
- logger.debug("添加緩存--------" + this.id);
- // getSize();
- } catch (Exception e) {
- borrowOrOprSuccess = false;
- if (jedis != null)
- jedisPool.returnBrokenResource(jedis);
- } finally {
- if (borrowOrOprSuccess)
- jedisPool.returnResource(jedis);
- }
- }
- @Override
- public Object getObject(Object key) {
- Jedis jedis = null;
- Object value = null;
- boolean borrowOrOprSuccess = true;
- try {
- jedis = jedisPool.getResource();
- jedis.select(DB_INDEX);
- value = SerializeUtil.unserialize(jedis.get(getKey(key).getBytes(UTF8)));
- logger.debug("從緩存中獲取-----" + this.id);
- // getSize();
- } catch (Exception e) {
- borrowOrOprSuccess = false;
- if (jedis != null)
- jedisPool.returnBrokenResource(jedis);
- } finally {
- if (borrowOrOprSuccess)
- jedisPool.returnResource(jedis);
- }
- return value;
- }
- @Override
- public Object removeObject(Object key) {
- Jedis jedis = null;
- Object value = null;
- boolean borrowOrOprSuccess = true;
- try {
- jedis = jedisPool.getResource();
- jedis.select(DB_INDEX);
- value = jedis.del(getKey(key).getBytes(UTF8));
- logger.debug("LRU算法從緩存中移除-----" + this.id);
- // getSize();
- } catch (Exception e) {
- borrowOrOprSuccess = false;
- if (jedis != null)
- jedisPool.returnBrokenResource(jedis);
- } finally {
- if (borrowOrOprSuccess)
- jedisPool.returnResource(jedis);
- }
- return value;
- }
- @Override
- publicvoidclear() {
- Jedis jedis = null;
- boolean borrowOrOprSuccess = true;
- try {
- jedis = jedisPool.getResource();
- jedis.select(DB_INDEX);
- // 如果有刪除操作,會影響到整個表中的數據,因此要清空一個mapper的緩存(一個mapper的不同數據操作對應不同的key)
- Set<byte[]> keys = jedis.keys(getKeys().getBytes(UTF8));
- logger.debug("出現CUD操作,清空對應Mapper緩存======>" + keys.size());
- for (byte[] key : keys) {
- jedis.del(key);
- }
- // 下面是網上流傳的方法,極大的降低系統性能,沒起到加入緩存應有的作用,這是不可取的。
- // jedis.flushDB();
- // jedis.flushAll();
- } catch (Exception e) {
- borrowOrOprSuccess = false;
- if (jedis != null)
- jedisPool.returnBrokenResource(jedis);
- } finally {
- if (borrowOrOprSuccess)
- jedisPool.returnResource(jedis);
- }
- }
- @Override
- public ReadWriteLock getReadWriteLock() {
- return readWriteLock;
- }
- }
然後,在mapper文件中添加下列一行使對應的表添加緩存支持:
- <cache eviction="LRU"type="com.dg.cache.RedisCache" />
我是以訂單表數據作爲緩存目標的(通常我們只是對不常變動的數據進行緩存,如登陸用戶信息、系統業務配置信息等,這裏我用訂單表只是作爲範例),應用結構如下:
3.測試
測試緩存的話,我們建一個頁面,然後查詢日期範圍的訂單列表,如下效果:
看輸出的日誌:
可以看出,初次查詢的時候,有sql語句,將訂單列表查詢出之後放入了緩存中去;返回來在執行剛纔的查詢動作時,只有下面的輸出,說明第二次沒再查庫,而是直接從redis緩存中去取。
結果列表爲:
以上只是簡單介紹了redis和ssm的整合過程,如果需要完整的代碼,到http://download.csdn.net/detail/donggang1992/9481612 下載。