這一篇是redis使用的總結文章,會簡單的談一下緩存的應用場景,緩存的使用邏輯,redis緩存與資料庫間結合以進行系統優化,當然文章最後也會給出具體的代碼實作,不至于看到文章的你一頭霧水,理論要講,項目代碼也要分享。
作者:13
GitHub:https://github.com/ZHENFENG13
版權聲明:本文為原創文章,未經允許不得轉載。
簡介
這是一篇關于Redis使用的總結類型文章,會先簡單的談一下緩存的應用場景、緩存的使用邏輯及注意事項,然後是Redis緩存與資料庫間結合以進行系統優化,當然文章的最後也會給出具體的代碼實作,不至于看到文章的你一頭霧水,理論要講,項目代碼也要分享,這是我寫部落格的基本出發點。
應用場景
Redis能做什麼呢?
這是個好問題,不同的人可能會給出不同的答案,因為它的應用場景真的很多,作為一個優秀的nosql資料庫可以結合其他産品做很多事情,比如:tomcat叢集的session同步、與nginx和lua結合做限流工具、基于Redis的分布式鎖實作、分布式系統唯一主鍵生成政策、秒殺場景中也會看到它、它還能夠作為一個消息隊列.....
Redis的應用場景很多很多,以上也隻是列舉了一部分而已,由于本文是圍繞我的開源項目perfect-ssm來寫的,是以在本文的場景就是一個緩存中間層,對于讀多寫少的應用場景,我們經常使用緩存來進行優化以提高系統性能。
我曾經寫過一篇《一次線上Mysql資料庫崩潰事故的記錄》的文章,裡面記錄了Web請求是如何毫不留情的摧垮mysql資料庫,進而導緻網站應用無法正常運轉。當時的情況就是資料庫讀請求太多,事故的主要原因也是這個,後續的解決方案也就是在項目中添加緩存層,使得熱點資料得以存入緩存,不會重複的去讀取mysql,将大部分請求壓力轉移至Redis緩存中以減輕mysql的負擔。

接入緩存後的處理邏輯
請求過來後,首先判斷Redis裡面有沒有,有資料則直接傳回Redis中的資料給使用者,沒有則查詢資料庫,如果資料庫中也沒有則傳回空或者提醒語句即可。
當然,針對不同的操作,對于Redis和mysql的操作也是不同的:
添加操作
如果是需要放入緩存的資料,那麼在向mysql資料庫中插入成功後,生成對應的key至,并存入Redis中。
修改操作
向mysql資料庫中修改成功後,修改Redis中的資料,但是Redis并沒有更新語句,是以隻能先删除,再添加完成更新操作。
需要注意的是,考慮到程式對于Redis的操作可能會失敗,這時mysql中的資料已經修改,但是Redis中的資料依然是上一次的資料,導緻資料不一緻的問題,是以是先操作Redis還是先操作mysql需要慎重考慮。
删除操作
與修改操作相同,先删除資料,再更新緩存,但是同樣會有出現資料不一緻問題的可能性需要注意,如果資料庫中的資料删除了,但是Redis中的資料沒删除,又會出現業務問題。
查詢操作
首先通過Redis查詢,如果緩存中已經存在資料則直接傳回即可,此時就不再需要通過mysql資料庫來擷取資料,減少對mysql的請求,如果緩存中不存在資料,則依然通過mysql資料庫查詢,查詢到資料後,存入Redis緩存中。
本項目中的代碼是先操作mysql,再操作Redis,有機率會出現上文中提到的資料庫與緩存資料不一緻的情況,是以需要注意,本文的代碼隻做參考,用到實際項目中還是需要根據具體的業務邏輯進行合理的修改。
使用緩存的建議
緩存存儲政策:
可以緩存的資料的特征基本上是以下幾點:
- 熱點資料
- 實時性要求不高的資料
- 業務邏輯簡單的資料
至于什麼資料,不同的系統、不同的項目要求肯定不同,這裡不做過多讨論,隻簡單的說一下自己的想法,結合以上的特征總結如下:
- 1.首頁資料、分類資料這些資料屬于熱點資料,首頁資料更是熱得發燙,而且這類資料一般實時性不高,不會頻繁的去操作,比較适合放入緩存。
- 2.詳情資料,比如文章詳情、商品詳情、廣告詳情、個人資訊詳情,這些資料庫中單條的的資料可以以其id生成不同的key儲存到Redis,操作比較簡單明了,在更新或者删除的時候需要同步更新Redis中的資料,這類資料也适合放入緩存中。
- 3.清單資料不是特别推薦,除非是實時性和改變頻率真的很低的情況下,因為清單往往牽涉的資料和操作很多,處理起來比較複雜,如果對實時性要求低的話、或者部分字段更新頻率低的話,可以換成這部分資料。
緩存存儲政策的制定說難也難,說容易也容易,主要是根據具體的業務場景合理的操作即可,以上隻是做了一個簡單的總結。
緩存失效政策:
失效政策一定要做好,血的教訓。
- 定時删除
含義:在設定key的過期時間的同時,為該key建立一個定時器,讓定時器在key的過期時間來臨時,對key進行删除
優點:保證記憶體被盡快釋放
缺點:
若過期key很多,删除這些key會占用很多的CPU時間,在CPU時間緊張的情況下,CPU不能把所有的時間用來做要緊的事兒,還需要去花時間删除這些key
定時器的建立耗時,若為每一個設定過期時間的key建立一個定時器(将會有大量的定時器産生),性能影響嚴重
- 惰性删除
含義:key過期的時候不删除,每次從資料庫擷取key的時候去檢查是否過期,若過期則删除,傳回null。
優點:删除操作隻發生在從資料庫取出key的時候發生,而且隻删除目前key,是以對CPU時間的占用是比較少的,而且此時的删除是已經到了非做不可的地步(如果此時還不删除的話,我們就會擷取到了已經過期的key了)
缺點:若大量的key在超出逾時時間後,很久一段時間内,都沒有被擷取過,那麼可能發生記憶體洩露(無用的垃圾占用了大量的記憶體)
- 定期删除
含義:每隔一段時間執行一次删除過期key操作
優點:
通過限制删除操作的時長和頻率,來減少删除操作對CPU時間的占用--處理"定時删除"的缺點
定期删除過期key--處理"惰性删除"的缺點
缺點
在記憶體友好方面,不如"定時删除"
在CPU時間友好方面,不如"惰性删除"
難點
合理設定删除操作的執行時長(每次删除執行多長時間)和執行頻率(每隔多長時間做一次删除)(這個要根據伺服器運作情況來定了)
參考《Redis設計與實作》
緩存操作順序政策:
在上文中已經講到了操作順序的問題,是先操作mysql呢?還是先操作Redis呢?這個需要根據自己的業務邏輯來考量,盡量選擇影響較小且結合友好的方案來做。
代碼實作:
這裡隻貼出主要的邏輯代碼,想要完整實作的可以到代碼倉庫去取。
//添加
@Override
public int addArticle(Article article) {
if (articleDao.insertArticle(article) > 0) {
log.info("insert article success,save article to Redis");
RedisUtil.put(Constants.ARTICLE_CACHE_KEY + article.getId(), article);
return 1;
}
return 0;
}
//修改
@Override
public int updateArticle(Article article) {
if (article.getArticleTitle() == null || article.getArticleContent() == null || getTotalArticle(null) > 90 || article.getArticleContent().length() > 50000) {
return 0;
}
if (articleDao.updArticle(article) > 0) {
log.info("update article success,delete article in Redis and save again");
RedisUtil.del(Constants.ARTICLE_CACHE_KEY + article.getId());
RedisUtil.put(Constants.ARTICLE_CACHE_KEY + article.getId(), article);
return 1;
}
return 0;
}
//删除
@Override
public int deleteArticle(String id) {
RedisUtil.del(Constants.ARTICLE_CACHE_KEY + id);
return articleDao.delArticle(id);
}
//查詢
@Override
public Article findById(String id) {
log.info("get article by id:" + id);
Article article = (Article) RedisUtil.get(Constants.ARTICLE_CACHE_KEY + id, Article.class);
if (article != null) {
log.info("article in Redis");
return article;
}
Article articleFromMysql = articleDao.getArticleById(id);
if (articleFromMysql != null) {
log.info("get article from mysql and save article to Redis");
RedisUtil.put(Constants.ARTICLE_CACHE_KEY + articleFromMysql.getId(), articleFromMysql);
return articleFromMysql;
}
return null;
}
結語
首發于我的個人部落格,新的項目示範位址:perfect-ssm,登入賬号:admin,密碼:123456
如果有問題或者有一些好的創意,歡迎給我留言,也感謝向我指出項目中存在問題的朋友。
如果你想繼續了解該項目可以檢視整個系列文章Spring+SpringMVC+MyBatis+easyUI整合系列文章,也可以到我的GitHub倉庫或者開源中國代碼倉庫中檢視源碼及項目文檔。
我曾七次鄙視自己的靈魂:
第一次,當它本可進取時,卻故作謙卑;
第二次,當它空虛時,用愛欲來填充;
第三次,在困難和容易之間,它選擇了容易;
第四次,它犯了錯,卻借由别人也會犯錯來寬慰自己;
第五次,它自由軟弱,卻把它認為是生命的堅韌;
第六次,當它鄙夷一張醜惡的嘴臉時,卻不知那正是自己面具中的一副;
第七次,它側身于生活的污泥中雖不甘心,卻又畏首畏尾。