您好,登錄后才能下訂單哦!
這篇文章給大家介紹怎么設置Redis的有效時間,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
一、有效時間設置:
redis對存儲值的過期處理實際上是針對該值的鍵(key)處理的,即時間的設置也是設置key的有效時間。Expires字典保存了所有鍵的過期時間,Expires也被稱為過期字段。
四種處理策略
EXPIRE 將key的生存時間設置為ttl秒
PEXPIRE 將key的生成時間設置為ttl毫秒
EXPIREAT 將key的過期時間設置為timestamp所代表的的秒數的時間戳
PEXPIREAT 將key的過期時間設置為timestamp所代表的的毫秒數的時間戳
其實以上幾種處理方式都是根據PEXPIREAT來實現的,設置生存時間的時候是redis內部計算好時間之后在內存處理的,最終的處理都會轉向PEXPIREAT。
1、2兩種方式是設置一個過期的時間段,就是咱們處理驗證碼最常用的策略,設置三分鐘或五分鐘后失效,把分鐘數轉換成秒或毫秒存儲到redis中。
3、4兩種方式是指定一個過期的時間 ,比如優惠券的過期時間是某年某月某日,只是單位不一樣。
二、過期處理
過期鍵的處理就是把過期鍵刪除,這里的操作主要是針對過期字段處理的。
Redis中有三種處理策略:定時刪除、惰性刪除和定期刪除。
定時刪除:在設置鍵的過期時間的時候創建一個定時器,當過期時間到的時候立馬執行刪除操作。不過這種處理方式是即時的,不管這個時間內有多少過期鍵,不管服務器現在的運行狀況,都會立馬執行,所以對CPU不是很友好。
惰性刪除:惰性刪除策略不會在鍵過期的時候立馬刪除,而是當外部指令獲取這個鍵的時候才會主動刪除。處理過程為:接收get執行、判斷是否過期(這里按過期判斷)、執行刪除操作、返回nil(空)。
定期刪除:定期刪除是設置一個時間間隔,每個時間段都會檢測是否有過期鍵,如果有執行刪除操作。這個概念應該很好理解。
看完上面三種策略后可以得出以下結論:
4. 1、3為主動刪除,2為被動刪除。
5. 1是實時執行的,對CPU不是很友好,但是這在最大程度上釋放了內存,所以這種方式算是一種內存優先優化策略。
6. 2、3為被動刪除,所以過期鍵應該會存在一定的時間,這樣就使得過期鍵不會被立馬刪除,仍然占用著內存。但是惰性刪除的時候一般是單個刪除,相對來說對CPU是友好的。
7. 定期鍵這種刪除策略是一種讓人很蛋疼的策略,它既有避免1、2兩種策略劣勢的可能,也有同時發生1、2兩種策略劣勢的可能。如果定期刪除執行的過于頻繁就可能會演變成定時刪除,如果執行的過少就有可能造成過多過期鍵未被刪除而占用過多內存,如果時間的設置不是太好,既可能占用過多內存又同時對CPU產生不好的影響。所以。使用定期刪除的時候一定要把握好這個刪除的時間點。存在即為合理,既然開發的時候有這種策略,就說明定期刪除還是有他的優勢的,具體大家可以自己琢磨。
三、主從服務器刪除過期鍵處理
參考書上說的有三種:RDB持久化、AOF持久化和復制功能。
RDB:
1. 主服務器模式運行在載入RDB文件時,程序會檢查文件中的鍵,只會加載未過期的,過期的會被忽略,所以RDB模式下過期鍵不會對主服務器產生影響。
2. 從服務器運行載入RDB文件時,會載入所有鍵,包括過期和未過期。當主服務器進行數據同步的時候,從服務器的數據會被清空,所以RDB文件的過期鍵一般不會對從服務器產生影響。
AOF:
AOF文件不會受過期鍵的影響。如果有過期鍵未被刪除,會執行以下動作:
客戶端請求時(過期鍵):
1.從數據庫充刪除被訪問的過期鍵;
2.追加一條DEL 命令到AOF文件;
3.向執行請求的客戶端回復nil(空)。
復制:
1.主服務器刪除過期鍵之后,向從服務器發送一條DEL指令,告知刪除該過期鍵。
2.從服務器接收到get指令的時候不會對過期鍵進行處理,只會當做未過期鍵一樣返回。(為了保持主從服務器數據的一致性)
3.從服務器只有接到主服務器發送的DEL指令后才會刪除過期鍵。
參考書籍:《Redis設計與實現》黃健宏著
補充知識:redis緩存數據需要指定緩存有效時間范圍段的多個解決方案 Calendar+quartz
在實現積分項目業務中,對不同場景設置了不同的key-value緩存到了redis中。但是因為對不同業務的key需要緩存的時間不盡相同,這里自定義工具類來實現。
設置redis緩存key,截取部分代碼:
try{ //cacheManager就相當從redis鏈接池獲取一個連接,具體工廠類獲取在后面備注 cacheManager = (RedisCacheManager) CacheManagerFactory.getCacheManager(); totalMonCount = Float.parseFloat(cacheManager.getString(monthKey)) + centCount; if (centLimitByMonth != -1){ if (totalMonCount > centLimitByMonth) { // 超出月上限不再累計 logger.error("exceeds the month limit cents! [" + totalMonCount + "] code:[" + code + "]"); return null; } } //當未超出月額度,此時要對日額度進行判斷;只需判斷其是否超出日上限積分 if (dayKey != null){ //累積積分;因為簽到其實是沒有每日積分的,是按次數規則累積的;但為了統一,直接用centCount代替(都是簽一次1分) totalDayCount = Float.parseFloat(cacheManager.getString(dayKey)) + centCount; if (centLimitByDay != -1){ if (totalDayCount > centLimitByDay){ logger.info("[ERROR]teacher everyday assign cents > month limit! total: ["+totalDayCount+"]"); return null; } } cacheManager.set(dayKey,totalDayCount.toString(),DateUtil.getSecsToEndOfCurrentDay()); } //對月限制key進行積分累加 //每月1號凌晨1點啟動腳本刪除,同時設置了保存到月底緩存時間雙重保障 cacheManager.set(monthKey, totalMonCount.toString(), DateUtil.getSecsToEndOfCurrentDay()); logger.info("==monthkey:"+monthKey+"---value:"+totalMonCount); } ... }catch(Exception e){ logger.error("===cache redis fail!"); e.printStackTrace(); }finally { if (cacheManager != null){ cacheManager.close(); } } //工廠類獲取redis鏈接 public class CacheManagerFactory { private static ICacheManager cacheManager; private CacheManagerFactory(){ }; public static ICacheManager getCacheManager(){ if(cacheManager == null){ synchronized (CacheManagerFactory.class) { if(cacheManager == null){ JedisPooler jedisPooler = RedisPoolerFactory.getJedisPooler(); cacheManager = new RedisCacheManager(jedisPooler); } } } return cacheManager; } } //redis鏈接池工廠類獲取鏈接 public class RedisPoolerFactory { private static JedisPooler jedisPooler; private RedisPoolerFactory(){ }; public static JedisPooler getJedisPooler(){ if(jedisPooler == null){ synchronized (RedisPoolerFactory.class) { if(jedisPooler == null){ jedisPooler = new JedisPooler(); } } } return jedisPooler; } } /** * * Redis 連接池實例 * * @author Ethan.Lam * @createTime 2011-12-3 * */ public class JedisPooler { private JedisPool pool; private String REDIS_HOST; private String REDIS_PSW; private int REDIS_PORT; private int REDIS_MaxActive; private int REDIS_MaxIdle; private int REDIS_MaxWait; public JedisPooler(String config) { __init(config); } public JedisPooler() { __init("/jedisPool.properties"); } private void __init(String conf) { // 完成初始化工作 Properties prop = new Properties(); try { InputStream _file = loadConfig(conf); prop.load(_file); REDIS_HOST = prop.getProperty("REDIS.HOST"); REDIS_PSW = prop.getProperty("REDIS.PSW"); REDIS_PORT = Integer.parseInt(prop.getProperty("REDIS.PORT").trim()); REDIS_MaxActive = Integer.parseInt(prop.getProperty("REDIS.MaxActive").trim()); REDIS_MaxIdle = Integer.parseInt(prop.getProperty("REDIS.MaxIdle").trim()); REDIS_MaxWait = Integer.parseInt(prop.getProperty("REDIS.MaxWait").trim()); } catch (Exception e) { e.printStackTrace(); REDIS_HOST = "localhost"; throw new NullPointerException(conf + " is not found !"); } JedisPoolConfig config = new JedisPoolConfig(); config.setMaxActive(REDIS_MaxActive); config.setMaxIdle(REDIS_MaxIdle); config.setMaxWait(REDIS_MaxWait); config.setTestOnBorrow(true); System.out.println("REDIS Cache服務信息: 當前連接的服務IP為:" + REDIS_HOST + ":" + REDIS_PORT); if (null == REDIS_PSW || "".equals(REDIS_PSW.trim())){ pool = new JedisPool(config, REDIS_HOST, REDIS_PORT, 5000); } else{ pool = new JedisPool(config, REDIS_HOST, REDIS_PORT, 5000, REDIS_PSW); } } public Jedis getJedis() { return pool.getResource(); } public void returnResource(Jedis jedis) { pool.returnResource(jedis); } public JedisPool getPool() { return pool; } InputStream loadConfig(String configPath) throws Exception { InputStream _file = null; try { String file = JedisPooler.class.getResource(configPath).getFile(); file = URLDecoder.decode(file); _file = new FileInputStream(file); } catch (Exception e) { System.out.println("讀取jar中的配置文件...."); String currentJarPath = URLDecoder.decode(JedisPooler.class.getProtectionDomain() .getCodeSource().getLocation().getFile(), "UTF-8"); // 獲取當前Jar文件名 System.out.println("currentJarPath:" + currentJarPath); java.util.jar.JarFile currentJar = new java.util.jar.JarFile(currentJarPath); java.util.jar.JarEntry dbEntry = currentJar.getJarEntry("jedisPool.properties"); InputStream in = currentJar.getInputStream(dbEntry); _file = in; } return _file; } }
可以看到,這里cacheManager.set(monthKey, totalMonCount.toString(), DateUtil.getSecsToEndOfCurrentDay()); 就用到了工具類獲取了指定的時間范圍。
對于redis這種希望指定緩存有效時間,現在提供3種方案:
1、自定義確切時間:
public static final long LoginCentTimeByDay = 86400;//s 未認證失效時間 1天
public static final long LoginCentTimeByMonth = 86400*30; //s 時效時間 30天
直接指定:
cacheManager.set(monthKey, totalMonCount.toString(),LoginCentTimeByDay)
2、自定義工具類,獲取當前時間到第二天的零點、下個月1號零點的時間差(s):
cacheManager.set(monthKey, totalMonCount.toString(), DateUtil.getSecsToEndOfCurrentDay()); public class DateUtil { /** *獲取每月最后一天時間 * @param sDate1 * @return */ public static Date getLastDayOfMonth(Date sDate1) { Calendar cDay1 = Calendar.getInstance(); cDay1.setTime(sDate1); final int lastDay = cDay1.getActualMaximum(Calendar.DAY_OF_MONTH); Date lastDate = cDay1.getTime(); lastDate.setDate(lastDay); return lastDate; } /* 獲取下一個月第一天凌晨1點 */ public static Date nextMonthFirstDate() { Calendar calendar = Calendar.getInstance(); calendar.set(Calendar.HOUR_OF_DAY, 1); //設置為每月凌晨1點 calendar.set(Calendar.DAY_OF_MONTH, 1); //設置為每月1號 calendar.add(Calendar.MONTH, 1); // 月份加一,得到下個月的一號 // calendar.add(Calendar.DATE, -1); 下一個月減一為本月最后一天 return calendar.getTime(); } /** * 獲取第二天凌晨0點毫秒數 * @return */ public static Date nextDayFirstDate() throws ParseException { Calendar cal = Calendar.getInstance(); cal.setTime(new Date()); cal.add(Calendar.DAY_OF_YEAR, 1); cal.set(Calendar.HOUR_OF_DAY, 00); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); return cal.getTime(); } //********* /** * 獲取當前時間到下個月凌晨1點相差秒數 * @return */ public static Long getSecsToEndOfCurrentMonth(){ Long secsOfNextMonth = nextMonthFirstDate().getTime(); //將當前時間轉為毫秒數 Long secsOfCurrentTime = new Date().getTime(); //將時間轉為秒數 Long distance = (secsOfNextMonth - secsOfCurrentTime)/1000; if (distance > 0 && distance != null){ return distance; } return new Long(0); } /** * 獲取當前時間到明天凌晨0點相差秒數 * @return */ public static Long getSecsToEndOfCurrentDay() throws ParseException { Long secsOfNextDay = nextDayFirstDate().getTime(); //將當前時間轉為毫秒數 Long secsOfCurrentTime = new Date().getTime(); //將時間轉為秒數 Long distance = (secsOfNextDay - secsOfCurrentTime)/1000; if (distance > 0 && distance != null){ return distance; } return new Long(0); } }
3、使用定時任務定時清空redis緩存;避免出現定時任務異常,我的業務代碼里都保障了兩種方案都適用。
定時任務保證,到指定時間直接調用代碼進行操作;代碼里直接調用shell腳本直接刪掉相關redis的緩存數據。
quartz定時任務就需要注意定義相應的cron時間:
我的定時任務的配置文件quartz.xml中定義:
<!--定時任務1--> <!-- 每天12點將當天用戶積分行為緩存清掉 --> <bean id="deleteRedisCacheDayUsersJob" class="cn.qtone.xxt.cent.quartz.delRedisCacheCentUsers" /> <bean id="deleteRedisCacheDayUsersTask" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject" ref="deleteRedisCacheDayUsersJob" /> <property name="targetMethod" value="delCurrentDayCacheUsersByDay" /><!-- 定時執行 doItem 方法 --> <property name="concurrent" value="false" /> </bean> <bean id="deleteRedisCacheDayUsersTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean"> <property name="jobDetail" ref="deleteRedisCacheDayUsersTask" /> <property name="cronExpression" value="59 59 23 * * ?" /><!-- 每天凌晨23:59:59 點執行 --> <!--<property name="cronExpression" value="0 */1 * * * ?" /><!– 每隔1min執行一次 –>--> </bean> <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref bean="deleteRedisCacheDayUsersTrigger" /> <ref bean="deleteRedisCacheMonthUsersTrigger" /> <!--暫時不用--> <!--<ref bean="centUpdateByMonthTrigger" />--> </list> </property> </bean>
關于怎么設置Redis的有效時間就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。