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);
	}
}






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