redis整合Spring並使用
Redis簡介:
Redis是一個開源的使用ANSIC語言編寫、支援網路、可基於記憶體亦可持久化的日誌型、Key-Value資料庫,並提供多種語言的API。redis是一個key-value儲存系統。和Memcached類似,它支援儲存的value型別相對更多,包括string(字串)、list(連結串列)、set(集合)、zset(sorted set --有序集合)和hash(雜湊型別)。這些資料型別都支援push/pop、add/remove及取交集並集和差集及更豐富的操作,而且這些操作都是原子性的。在此基礎上,redis支援各種不同方式的排序。與memcached一樣,為了保證效率,資料都是快取在記憶體中。區別的是
效能:
下面是官方的bench-mark資料:測試完成了50個併發執行100000個請求。設定和獲取的值是一個256位元組字串。Linux box是執行Linux 2.6,這是X3320 Xeon 2.5 ghz。
文字執行使用loopback介面(127.0.0.1)。結果:讀的速度是110000次/s,寫的速度是81000次/s 。
資料結構:
redis提供五種資料型別:string,hash,list,set及zset(sortedset)。
string
string是最簡單的型別,你可以理解成與Memcached一模一樣的型別,一個key對應一個value,其上支援的操作與Memcached的操作類似。但它的功能更豐富。redis採用結構sdshdr和sds封裝了字串。
list(雙向連結串列)
list是一個連結串列結構,主要功能是push、pop、獲取一個範圍的所有值等等。操作中key理解為連結串列的名字。
dict(hash表)
set是集合,和我們數學中的集合概念相似,對集合的操作有新增刪除元素,有對多個集合求交併差等操作。操作中key理解為集合的名字。
dict中table為dictEntry
zset(排序set)
zset是set的一個升級版本,他在set的基礎上增加了一個順序屬性,這一屬性在新增修改元素的時候可以指定,每次指定後,zset會自動重新按新的值調整順序。可以理解了有兩列的mysql表,一列存value,一列存順序。操作中key理解為zset的名字。
zset利用dict維護key ->value的對映關係,用zsl(zskiplist)儲存value的有序關係。zsl實際是叉數,不穩定的多叉樹,每條鏈上的元素從根節點到葉子節點保持升序排序。
儲存:
redis使用了兩種檔案格式:全量資料和增量請求。
全量資料格式是把記憶體中的資料寫入磁碟,便於下次讀取檔案進行載入;
增量請求檔案則是把記憶體中的資料序列化為操作請求,用於讀取檔案進行replay得到資料,序列化的操作包括SET、RPUSH、SADD、ZADD。
redis的儲存分為記憶體儲存、磁碟儲存和log檔案三部分,配置檔案中有三個引數對其進行配置。
save secondsupdates,save配置,指出在多長時間內,有多少次更新操作,就將資料同步到資料檔案。這個可以多個條件配合,比如預設配置檔案中的設定,就設定了三個條件。
appendonlyyes/no ,appendonly配置,指出是否在每次更新操作後進行日誌記錄,如果不開啟,可能會在斷電時導致一段時間內的資料丟失。因為redis本身同步資料檔案是按上面的save條件來同步的,所以有的資料會在一段時間內只存在於記憶體中。
appendfsyncno/always/everysec ,appendfsync配置,no表示等作業系統進行資料快取同步到磁碟,always表示每次更新操作後手動呼叫fsync()將資料寫到磁碟,everysec表示每秒同步一次。
redis集成於spring框架
1、 需要匯入maven包。
<!--redis快取相關依賴-->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.6.2.RELEASE</version>
</dependency>
<!-- redis客戶端jar -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>${redis.clients.version}</version>
</dependency>
<redis.clients.version>2.8.2</redis.clients.version>
2、 建立redis的配置檔案redis.properties
#訪問地址
redis.host=127.0.0.1
#訪問埠
redis.port=6379
#注意,如果沒有password,此處不設定值,但這一項要保留,預設foobared
redis.password=
#客戶端超時時間單位是毫秒 預設是2000
redis.timeout=10000
#最大空閒數
redis.maxIdle=300
#連線池的最大資料庫連線數。設為0表示無限制,如果是jedis 2.4以後用redis.maxTotal
#redis.maxActive=600
#控制一個pool可分配多少個jedis例項,用來替換上面的redis.maxActive,如果是jedis 2.4以後用該屬性
redis.maxTotal=1000
#最大建立連線等待時間。如果超過此時間將接到異常。設為-1表示無限制。
redis.maxWaitMillis=1000
#連線的最小空閒時間 預設1800000毫秒(30分鐘)
redis.minEvictableIdleTimeMillis=300000
#每次釋放連線的最大數目,預設3
redis.numTestsPerEvictionRun=1024
#逐出掃描的時間間隔(毫秒) 如果為負數,則不執行逐出執行緒, 預設-1
redis.timeBetweenEvictionRunsMillis=30000
#是否在從池中取出連線前進行檢驗,如果檢驗失敗,則從池中去除連線並嘗試取出另一個
redis.testOnBorrow=true
#在空閒時檢查有效性, 預設false
redis.testWhileIdle=true
3、 spring的配置檔案:
<?xml version="1.0" encoding="UTF-8"?>
<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">
<!-- redis連線池配置-->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig" >
<!--最大空閒數-->
<property name="maxIdle" value="${redis.maxIdle}" />
<!--連線池的最大資料庫連線數 -->
<property name="maxTotal" value="${redis.maxTotal}" />
<!--最大建立連線等待時間-->
<property name="maxWaitMillis" value="${redis.maxWaitMillis}" />
<!--逐出連線的最小空閒時間 預設1800000毫秒(30分鐘)-->
<property name="minEvictableIdleTimeMillis" value="${redis.minEvictableIdleTimeMillis}" />
<!--每次逐出檢查時 逐出的最大數目 如果為負數就是 : 1/abs(n), 預設3-->
<property name="numTestsPerEvictionRun" value="${redis.numTestsPerEvictionRun}" />
<!--逐出掃描的時間間隔(毫秒) 如果為負數,則不執行逐出執行緒, 預設-1-->
<property name="timeBetweenEvictionRunsMillis" value="${redis.timeBetweenEvictionRunsMillis}" />
<!--是否在從池中取出連線前進行檢驗,如果檢驗失敗,則從池中去除連線並嘗試取出另一個-->
<property name="testOnBorrow" value="${redis.testOnBorrow}" />
<!--在空閒時檢查有效性, 預設false -->
<property name="testWhileIdle" value="${redis.testWhileIdle}" />
</bean>
<!--redis連線工廠 -->
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" destroy-method="destroy">
<property name="poolConfig" ref="jedisPoolConfig"></property>
<!--IP地址 -->
<property name="hostName" value="${redis.host}"></property>
<!--埠號 -->
<property name="port" value="${redis.port}"></property>
<!--如果Redis設定有密碼 -->
<property name="password" value="${redis.password}" />
<!--客戶端超時時間單位是毫秒 -->
<property name="timeout" value="${redis.timeout}"></property>
</bean>
<!-- 配置RedisTemplate -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="jedisConnectionFactory" />
<!--如果不配置Serializer,那麼儲存的時候預設使用String,如果用User型別儲存,那麼會提示錯誤User can't cast to String!! -->
<property name="keySerializer" >
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="valueSerializer" >
<bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer" />
</property>
<property name="hashKeySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
</property>
<property name="hashValueSerializer">
<bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>
</property>
<!--開啟事務 -->
<property name="enableTransactionSupport" value="true"></property>
</bean>
<!--自定義redis工具類,在需要快取的地方注入此類 -->
<bean id="redisService" class="com.hikvision.common.cache.RedisService">
<property name="redisTemplate" ref="redisTemplate" />
</bean>
</beans>
4、 redis的工具類:
package com.hikvision.common.cache;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* redis快取服務service
*
* @author wanjiadong
* @description
* @date Create in 16:35 2018/5/10
*/
public class RedisService {
private RedisTemplate<String, Object> redisTemplate;
public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
//=============================common============================
/**
* 指定快取失效時間
* @param key 鍵
* @param time 時間(秒)
* @return
*/
public boolean expire(String key,long time){
try {
if(time>0){
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
return false;
}
}
/**
* 根據key 獲取過期時間
* @param key 鍵 不能為null
* @return 時間(秒) 返回0代表為永久有效
*/
public long getExpire(String key){
return redisTemplate.getExpire(key,TimeUnit.SECONDS);
}
/**
* 判斷key是否存在
* @param key 鍵
* @return true 存在 false不存在
*/
public boolean hasKey(String key){
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
return false;
}
}
/**
* 刪除快取
* @param key 可以傳一個值 或多個
*/
@SuppressWarnings("unchecked")
public void del(String ... key){
if(key!=null&&key.length>0){
if(key.length==1){
redisTemplate.delete(key[0]);
}else{
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}
//============================String=============================
/**
* 普通快取獲取
* @param key 鍵
* @return 值
*/
public Object get(String key){
return key==null?null:redisTemplate.opsForValue().get(key);
}
/**
* 普通快取放入
* @param key 鍵
* @param value 值
* @return true成功 false失敗
*/
public boolean set(String key,Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
return false;
}
}
/**
* 普通快取放入並設定時間
* @param key 鍵
* @param value 值
* @param time 時間(秒) time要大於0 如果time小於等於0 將設定無限期
* @return true成功 false 失敗
*/
public boolean set(String key,Object value,long time){
try {
if(time>0){
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
}else{
set(key, value);
}
return true;
} catch (Exception e) {
return false;
}
}
/**
* 遞增
* @param key 鍵
* @return
*/
public long incr(String key, long delta){
if(delta<=0){
throw new RuntimeException("遞增因子必須大於0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 遞減
* @param key 鍵
* @return
*/
public long decr(String key, long delta){
if(delta<=0){
throw new RuntimeException("遞減因子必須大於0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
//================================Map=================================
/**
* HashGet
* @param key 鍵 不能為null
* @param item 項 不能為null
* @return 值
*/
public Object hget(String key,String item){
return redisTemplate.opsForHash().get(key, item);
}
/**
* 獲取hashKey對應的所有鍵值
* @param key 鍵
* @return 對應的多個鍵值
*/
public Map<Object,Object> hmget(String key){
return redisTemplate.opsForHash().entries(key);
}
/**
* HashSet
* @param key 鍵
* @param map 對應多個鍵值
* @return true 成功 false 失敗
*/
public boolean hmset(String key, Map<String,Object> map){
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
return false;
}
}
/**
* HashSet 並設定時間
* @param key 鍵
* @param map 對應多個鍵值
* @param time 時間(秒)
* @return true成功 false失敗
*/
public boolean hmset(String key, Map<String,Object> map, long time){
try {
redisTemplate.opsForHash().putAll(key, map);
if(time>0){
expire(key, time);
}
return true;
} catch (Exception e) {
return false;
}
}
/**
* 向一張hash表中放入資料,如果不存在將建立
* @param key 鍵
* @param item 項
* @param value 值
* @return true 成功 false失敗
*/
public boolean hset(String key,String item,Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
return false;
}
}
/**
* 向一張hash表中放入資料,如果不存在將建立
* @param key 鍵
* @param item 項
* @param value 值
* @param time 時間(秒) 注意:如果已存在的hash表有時間,這裡將會替換原有的時間
* @return true 成功 false失敗
*/
public boolean hset(String key,String item,Object value,long time) {
try {
redisTemplate.opsForHash().put(key, item, value);
if(time>0){
expire(key, time);
}
return true;
} catch (Exception e) {
return false;
}
}
/**
* 刪除hash表中的值
* @param key 鍵 不能為null
* @param item 項 可以使多個 不能為null
*/
public void hdel(String key, Object... item){
redisTemplate.opsForHash().delete(key,item);
}
/**
* 判斷hash表中是否有該項的值
* @param key 鍵 不能為null
* @param item 項 不能為null
* @return true 存在 false不存在
*/
public boolean hHasKey(String key, String item){
return redisTemplate.opsForHash().hasKey(key, item);
}
/**
* hash遞增 如果不存在,就會建立一個 並把新增後的值返回
* @param key 鍵
* @param item 項
* @param by 要增加幾(大於0)
* @return
*/
public double hincr(String key, String item,double by){
return redisTemplate.opsForHash().increment(key, item, by);
}
/**
* hash遞減
* @param key 鍵
* @param item 項
* @param by 要減少記(小於0)
* @return
*/
public double hdecr(String key, String item,double by){
return redisTemplate.opsForHash().increment(key, item,-by);
}
//============================set=============================
/**
* 根據key獲取Set中的所有值
* @param key 鍵
* @return
*/
public Set<Object> sGet(String key){
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
return null;
}
}
/**
* 根據value從一個set中查詢,是否存在
* @param key 鍵
* @param value 值
* @return true 存在 false不存在
*/
public boolean sHasKey(String key,Object value){
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
return false;
}
}
/**
* 將資料放入set快取
* @param key 鍵
* @param values 值 可以是多個
* @return 成功個數
*/
public long sSet(String key, Object...values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
return 0;
}
}
/**
* 將set資料放入快取
* @param key 鍵
* @param time 時間(秒)
* @param values 值 可以是多個
* @return 成功個數
*/
public long sSetAndTime(String key,long time,Object...values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if(time>0) expire(key, time);
return count;
} catch (Exception e) {
return 0;
}
}
/**
* 獲取set快取的長度
* @param key 鍵
* @return
*/
public long sGetSetSize(String key){
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
return 0;
}
}
/**
* 移除值為value的
* @param key 鍵
* @param values 值 可以是多個
* @return 移除的個數
*/
public long setRemove(String key, Object ...values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
return 0;
}
}
//================================zSet================================
/**
* 將資料放入set快取
* @param key 鍵
* @param value 值
* @param score 排序分數
*/
public <T>boolean zSet(String key, T value ,double score) {
try {
return redisTemplate.opsForZSet().add(key, value,score);
} catch (Exception e) {
return false;
}
}
/**
*
* 返回鍵k的從start-end之間的資料。(從大到小排序從0開始)
* @param
* @return
* @auther yaotenghui
* @date 2018/5/17 15:22
*/
public <T>Set<T>reverseRange(String k,long start,long end){
try {
return (Set<T>) redisTemplate.opsForZSet().reverseRange(k,start,end);
}catch (Exception e){
return null;
}
}
//===============================list=================================
/**
* 獲取list快取的內容
* @param key 鍵
* @param start 開始
* @param end 結束 0 到 -1代表所有值
* @return
*/
public List<Object> lGet(String key, long start, long end){
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
return null;
}
}
/**
* 獲取list快取的長度
* @param key 鍵
* @return
*/
public long lGetListSize(String key){
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
return 0;
}
}
/**
* 通過索引 獲取list中的值
* @param key 鍵
* @param index 索引 index>=0時, 0 表頭,1 第二個元素,依次類推;index<0時,-1,表尾,-2倒數第二個元素,依次類推
* @return
*/
public Object lGetIndex(String key,long index){
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
return null;
}
}
/**
* 將list放入快取
* @param key 鍵
* @param value 值
* @return
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
return false;
}
}
/**
* 將list放入快取
* @param key 鍵
* @param value 值
* @param time 時間(秒)
* @return
*/
public boolean lSet(String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0) expire(key, time);
return true;
} catch (Exception e) {
return false;
}
}
/**
* 將list放入快取
* @param key 鍵
* @param value 值
* @return
*/
public boolean lSet(String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
return false;
}
}
/**
* 將list放入快取
* @param key 鍵
* @param value 值
* @param time 時間(秒)
* @return
*/
public boolean lSet(String key, List<Object> value, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0) expire(key, time);
return true;
} catch (Exception e) {
return false;
}
}
/**
* 根據索引修改list中的某條資料
* @param key 鍵
* @param index 索引
* @param value 值
* @return
*/
public boolean lUpdateIndex(String key, long index,Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
return false;
}
}
/**
* 移除N個值為value
* @param key 鍵
* @param count 移除多少個
* @param value 值
* @return 移除的個數
*/
public long lRemove(String key,long count,Object value) {
try {
Long remove = redisTemplate.opsForList().remove(key, count, value);
return remove;
} catch (Exception e) {
return 0;
}
}
}