Spring集成redis缓存

redis一般使用key--value的形式存值,将内存中的数据保存在磁盘中(in-memory),启动的时候再加载使用。

redis是单进程单线程,支持主从模式。具有会话缓存功能,具有发布/订阅功能。

Redis的LRU策略:

Redis在缓存的内存达到极限时,会优先选择最久未被访问的数据进行回收。具体回收策略通过maxmemory-policy配置项进行配置。

Redis优点:

1.性能好,redis中的数据读写速度快。

2.数据类型丰富,Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型

3.丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性

4.Redis支持数据的备份,即master-slave模式的数据备份

Spring集成Redis:

<!-- 配置redis配置文件 -->
	<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations">
			<list>
				<value>classpath:db.properties</value>
			</list>
		</property>
	</bean>

	<!-- 启用缓存注解功能,这个是必须的,否则注解不会生效,该注解一定要声明在spring主配置文件中才会生效 -->
	<cache:annotation-driven cache-manager="cacheManager" />

	<!-- 配置Spring-data-redis连接redis服务器(jedisPool客户端) -->
	<bean id="jedisConnectionFactory"
		class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
		<property name="hostName" value="${redis.host}" />
		<property name="port" value="${redis.port}" />
		<property name="password" value="${redis.pass}" />
		<property name="timeout" value="${redis.timeout}" />
		<property name="database" value="${redis.default.db}" />
		<property name="poolConfig" ref="jedisPoolConfig" />
	</bean>

	<!-- 配置jedisPool参数(连接池) -->
	<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
		<property name="maxTotal" value="${redis.maxActive}" />
		<property name="maxIdle" value="${redis.maxIdle}" />
		<property name="maxWaitMillis" value="${redis.maxWait}" />
	</bean>

	<!-- 配置redis操作Bean,序列化key -->
	<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
		<property name="connectionFactory" ref="jedisConnectionFactory" />
		<property name="keySerializer">
			<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
		</property>
	</bean>

	<!-- 配置redis操作Bean,序列化key,value -->
	<bean id="redisTemplate_KVString" class="org.springframework.data.redis.core.RedisTemplate">
		<property name="connectionFactory" ref="jedisConnectionFactory" />
		<property name="keySerializer">
			<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
		</property>
		<property name="valueSerializer">
	        <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
	    </property>
	</bean>

	<!-- 配置redis缓存管理Bean,设置默认过期时间 -->
	<bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">
		<constructor-arg ref="redisTemplate"></constructor-arg>
		<!-- 默认过期时间三个小时 -->
		<property name="defaultExpiration" value="10800"/>
	</bean>

在数据库配置文件中打开缓存:

    <settings>  
      <!-- 只设置需要的,其他使用默认值 -->  
      <!-- 开启缓存,默认就是开启的,2层开关,需要在Mapper文件中也指定 cache 标签才会真正使用缓存 -->  
      <setting name="cacheEnabled" value="true"/>  
      <!-- 在null时也调用 setter,适应于返回Map,3.2版本以上可用 -->  
        <setting name="callSettersOnNulls" value="true"/>  
    </settings>  

在MVC配置文件中初始化数据库配置:

	<!-- Redis_Init -->
	<import resource="spring-redis.xml" />
	<!-- Data_Source_Init & MapperScannerConfigurer -->
	<import resource="spring-mybatis.xml" />

Redis分布式锁:

public interface DistributionLock {
	//加锁成功 返回加锁时间
    public Long lock(String lockKey, String threadname);

    //解锁 需要更加加锁时间判断是否有权限
    public void unlock(String lockKey, long lockvalue, String threadname);

}
public class RedisDistributionLock implements DistributionLock{
	@Autowired
	private RedisTemplate<Serializable, Serializable> redisTemplate;
	
	 private static final long LOCK_TIMEOUT = 60 * 1000; //加锁超时时间 单位毫秒  意味着加锁期间内执行完操作 如果未完成会有并发现象

	    private static final Logger LOG = Logger.getLogger(RedisDistributionLock.class); //redis锁日志


	    /**
	     * 取到锁加锁 取不到锁一直等待直到获得锁
	     */
	    @Override
	    public synchronized Long lock(String lockKey, String threadname) {
	        LOG.info(threadname + "开始执行加锁");
	        while (true) { //循环获取锁
	            Long lock_timeout = System.currentTimeMillis() + LOCK_TIMEOUT + 1; //锁时间
	            if (redisTemplate.execute(new RedisCallback<Boolean>() {

	                @Override
	                public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
	                    JdkSerializationRedisSerializer jdkSerializer = new JdkSerializationRedisSerializer();
	                    byte[] value = jdkSerializer.serialize(lock_timeout);
	                    return connection.setNX(lockKey.getBytes(), value);
	                }
	            })) { //如果加锁成功
	                LOG.info(threadname + "加锁成功+1");
	                redisTemplate.expire(lockKey, LOCK_TIMEOUT, TimeUnit.MILLISECONDS); //设置超时时间,释放内存
	                return lock_timeout;
	            }else {
	                Long currt_lock_timeout_Str = (Long) redisTemplate.opsForValue().get(lockKey); // redis里的时间
	                if (currt_lock_timeout_Str != null && currt_lock_timeout_Str < System.currentTimeMillis()) { //锁已经失效
	                    // 判断是否为空,不为空的情况下,说明已经失效,如果被其他线程设置了值,则第二个条件判断是无法执行

	                    Long old_lock_timeout_Str = (Long) redisTemplate.opsForValue().getAndSet(lockKey, lock_timeout);
	                    // 获取上一个锁到期时间,并设置现在的锁到期时间
	                    if (old_lock_timeout_Str != null && old_lock_timeout_Str.equals(currt_lock_timeout_Str)) {
	                        // 如过这个时候,多个线程恰好都到了这里,但是只有一个线程的设置值和当前值相同,他才有权利获取锁
	                        LOG.info(threadname + "加锁成功+2");
	                        redisTemplate.expire(lockKey, LOCK_TIMEOUT, TimeUnit.MILLISECONDS); //设置超时时间,释放内存
	                        return lock_timeout;//返回加锁时间
	                    }
	                }
	            }

	            try {
	                LOG.info(threadname +  "等待加锁,睡眠100毫秒"); 
	                TimeUnit.MILLISECONDS.sleep(100);//睡眠100毫秒
	            } catch (InterruptedException e) {
	                e.printStackTrace();
	            } 
	        }
	    }

	    @Override
	    public synchronized void unlock(String lockKey, long lockvalue, String threadname) {
	        LOG.info(threadname + "执行解锁==");//正常直接删除 如果异常关闭判断加锁会判断过期时间
	        Long currt_lock_timeout_Str = (Long) redisTemplate.opsForValue().get(lockKey); // redis里的时间

	        if (currt_lock_timeout_Str != null && currt_lock_timeout_Str == lockvalue) {//如果是加锁者 则删除锁 如果不是则等待自动过期 重新竞争加锁
	            redisTemplate.delete(lockKey); //删除键
	            LOG.info(threadname + "解锁成功---");
	        }
	    }

}


RedisTemplate操作:

public class RedisUnitTest extends BaseJunit4 {
	
	/**
	 * 默认采用String序列化策略(key与value)
	 */
	@Resource(name = "redisTemplate_KVString")
	private RedisTemplate<String, String> redisTemplate_KVString;
	
	/**
	 * Redis 可以存储键与5种不同数据结构类型之间的映射,这5种数据结构类型分别为
	 * String(字符串)、List(列表)、Set(集合)、Hash(散列)和 Zset(有序集合)
	 * Key值如无特殊要求,一般为String类型
	 * 
	 */
	public void queryRedisValue(){
		String key = "";
		String value = "";
		String value2 = "";
		Map<String,String> maps = new HashMap<String, String>();
        maps.put("key1","value1");
        maps.put("key2","value2");
        maps.put("key3","value3");
        
        List<String> keys = new ArrayList<String>();
        keys.add("key1");
        keys.add("key2");
        keys.add("key3");
        
		redisTemplate_KVString.opsForValue().set(key, value, 10, TimeUnit.SECONDS);//设置key-value失效时间10s
		redisTemplate_KVString.opsForValue().get(key);//根据key获取value,10秒的时限
		redisTemplate_KVString.opsForValue().setIfAbsent(key, value);//false 已存在key/ true 之前不存在key
		redisTemplate_KVString.opsForValue().multiSet(maps);//同时设置多个 key-value
		redisTemplate_KVString.opsForValue().getAndSet(key, value2);//设置key的value值,并返回旧value
		redisTemplate_KVString.opsForValue().multiGet(keys);//同时取出多个 key对应的value
		redisTemplate_KVString.opsForValue().append(key, value);//key存在则将value追加到末尾,不存在则创建一个key-空串,再加到末尾
		redisTemplate_KVString.opsForValue().get(key, 0, 3);//截取key 所对应的value字符串
		redisTemplate_KVString.opsForValue().size(key);//返回key对应的value长度
		
		redisTemplate_KVString.opsForHash();//操作hash
		redisTemplate_KVString.opsForList();//操作list
		redisTemplate_KVString.opsForSet();//操作set
		redisTemplate_KVString.opsForZSet();//操作有序set
		
		//如需将key在redis服务器中保存为文件夹类型,在文件夹名后加 : 号
		String folder = "key:";
		key = folder + 1;
		redisTemplate_KVString.opsForValue().set(key, value);
	}
}






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章