高效Redis工具類


一、引言

本篇博客以redis緩存為主。至於什么是redis緩存?還有沒有其它的緩存?哪個緩存的性能會更好?這里就不一一做介紹了!(有興趣的可以自己去百度一下)

在日常的開發中,我們或多或少(必須)的會用到緩存。為了提高系統性能、提升用戶體驗度,用戶體驗是多么的重要;這就要求在軟件設計時,不但要注重可靠性、安全性、可擴展性以及可維護性等等的一些指標,更要注重用戶的體驗,用戶體驗分很多方面,但是有一點非常重要就是對用戶操作的響應一定要快;怎樣提高用戶訪問的響應速度,這就是擺在架構設計中必須要解決的問題;說到提高服務的響應速度就不得不說緩存了;

從系統的層面說,CPU的速度遠遠高於磁盤IO的速度;所以要想提高響應速度,必須減少磁盤IO的操作,但是有很多信息又是存在數據庫當中的,每次查詢數據庫就是一次IO操作;請求響應時間等於網絡響應時間和服務器響應時間;網絡我們控制不了,服務器響應時間包括CPU計算時間和磁盤IO時間,其中CPU計算時間這個有硬件資源決定的,我們盡量減少算法的復雜度來減少它,磁盤IO時間,這個時間是非常慢的,應該盡量減少;

當客戶端調用某個接口獲取信息的時候,執行順序1、2、3、4;由於信息存放在DB中,所以2、3就有一次磁盤IO操作;這個看似非常簡單業務邏輯,但是當你做架構設計的時候往往要考慮最壞的場景,或者當成千上萬的用戶頻繁的調用這個接口應該怎么處理?如果按照上圖這樣的架構處理,這個看似簡單業務的接口會使整個系統變慢,這樣用戶的請求就會長時間得不到響應;這樣的問題怎么解決那,這時候就該緩存登場了;

緩存有很多種、也有很多種緩存策略。這里就不一一就探討了,畢竟這篇博客是講關於的工具類!總之一句話,要想提高系統的性能,盡量減少IO的操作,特別是磁盤IO的操作;使用緩存可以有效的避免這種情況;所以在架構設計過程中,涉及到查詢數據庫的時候,應該考慮一下是不是使用緩存技術來提高系統的性能,並且降低數據庫的壓力。

 

二、無知的痛(用get / set方式使用Redis)

在根據某個需求設計業務的時候,比如說購物車:涉及到添加、查詢、修改、刪除。不說這四個具體實現時候的細節,這不就簡單的對數據庫增、刪、改、查嗎?但是,當成千上萬的用戶頻繁訪問的時候,簡單的事情在高並發的情況下就會變的不簡單!這個時候就需要有個東西來代替數據,而且還要比數據庫更快,來應對可能出現的高並發!所以我們想到了(redis)緩存,作為一個key / value 存在,很多開發者自然的使用set/get 方式來使用 redis  ,實際上這並不是最優化的使用方法。尤其在未啟用VM 情況下,Redis 全部數據需要放入內存,節約內存尤其重要。 假如一個key-value單元需要最小占用512字節,即使只存一個字節也占了512字節(比如set/get,一個key-value對應一條數據)。這時候就有一個設計模式,可以把key復用,幾個key-value作為一個大的value,大的value再作為一個value,set存入一個key中。這樣同樣512字節就會存放10-100倍的容量。 這就是為了節約內存,建議使用hash類型而不是set/get的方式來使用Redis。

 

三、hash類型方式使用redis -- 指定返回類型

在日常開發中,我們會把業務中的所有特性封裝成一個對象,再把這個對象存入到redis緩存中。在使用redis的時候,通常會把redis常用的方法封裝成一個工具類。然后我們調用工具類中的方法,把存入的對象傳到方法中。通常方法會把對象轉換成string存入緩存,需要的時候再把string取出來,然后再轉換成對象!大多數的情況就是如此,但是在我實際的開發中傳入的是個和業務有關的對象,返給我的卻是一個object對象(和業務沒有半毛錢關系)。我想要讓返回的對象和業務有關,還得強轉一下!這返回的是單個對象,如果是個list呢?循環強轉嗎?那如果是map呢?當然肯定都是能轉的,但是這樣就增加了實際業務中的代碼量!在我看來多一行於業務無關的代碼都是罪惡的(縱然它是為了和業務產生關聯),我想要的是 我給你存的是啥!返回的就是啥!別整那些沒用的......

1.根據key和fieid返回hash中指定存儲位置的值,指定返回類型

  1. /**
  2. * 返回hash中指定存儲位置的值
  3. * @param key
  4. * @param fieid
  5. * @param obj
  6. * @param <T>
  7. * @return
  8. */
  9. public<T> T hget(String key,String fieid,T obj) {
  10. Jedis jedis = null;
  11. try {
  12. logger.info( "hget >> key+fieid:{}",key +"+"+ fieid);
  13. jedis = jedisPool.getResource();
  14. String hget = jedis.hget(key, fieid);
  15. ObjectMapper om = new ObjectMapper();
  16. T t =(T) om.readValue(hget, obj.getClass());
  17. return t;
  18. } catch (Exception e) {
  19. logger.error( "Jedis hget 異常: " + e.getMessage());
  20. return null;
  21. } finally {
  22. closeRedis(jedis);
  23. }
  24. }

調用:

  1. //調用hget方法
  2. DemoDTO demoDTO = jedis.hget( "key", "fieid", new DemoDTO());

 

2.hvals()方法,獲取hash中value的集合,指定返回的集合類型

  1. /**
  2. * 獲取hash中value的集合,指定返回的集合類型
  3. * @param key
  4. * @param obj
  5. * @param <T>
  6. * @return
  7. */
  8. public<T> List<T> hvals(String key,T obj) {
  9. Jedis jedis = null;
  10. try {
  11. logger.info( "hvals >> key:{}",key);
  12. jedis = jedisPool.getResource();
  13. List<String> hvals = jedis.hvals(key);
  14. Iterator<String> iterator = hvals.iterator();
  15. List<T> returnList = new ArrayList<>();
  16. ObjectMapper om = new ObjectMapper();
  17. while (iterator.hasNext()) {
  18. String next = iterator.next();
  19. T t =(T) om.readValue(next, obj.getClass());
  20. returnList.add(t);
  21. }
  22. return returnList;
  23. } catch (Exception e) {
  24. logger.error( "Jedis hvals fail >> e:{}",e.getMessage());
  25. return null;
  26. } finally {
  27. closeRedis(jedis);
  28. }
  29. }

調用:

  1. //調用hvals方法
  2. List<DemoDTO> demoListDTOS = jedis.hvals("key", new DemoDTO());

 

3.以Map的形式返回hash中的存儲和值,指定map中的value類型

  1. /**
  2. * 以Map的形式返回hash中的存儲和值
  3. * @param key
  4. * @param obj
  5. * @param <T>
  6. * @return
  7. */
  8. public<T> Map<String,T> hgetAll(String key,T obj) {
  9. Jedis jedis = null;
  10. try {
  11. logger.info( "hget >> key+obj:{}",key+"+"+obj);
  12. jedis = jedisPool.getResource();
  13. Map<String, String> map = jedis.hgetAll(key);
  14. Map<String, T> returnMap = new HashMap<>();
  15. ObjectMapper om = new ObjectMapper();
  16. for (Map.Entry<String, String> e : map.entrySet()) {
  17. T t =(T) om.readValue(e.getValue(), obj.getClass());
  18. returnMap.put(e.getKey(),t);
  19. }
  20. return returnMap;
  21. } catch (Exception e) {
  22. logger.error( "Jedis hget fail >> e:{}" + e.getMessage());
  23. return null;
  24. } finally {
  25. closeRedis(jedis);
  26. }
  27. }

調用:

  1. //調用hgetAll方法
  2. Map<String,DemoDTO> stringDemoDTOMap = jedis.hgetAll("key", new DemoDTO());

 

4.關閉jedis連接,將jedis連接歸還redis連接池

  1. /**
  2. * 關閉jedis連接池
  3. * @param jedis
  4. */
  5. private void closeRedis(Jedis jedis) {
  6. if (jedis != null){
  7. try{
  8. jedis.close();
  9. } catch (Exception e){
  10. logger.error( "Jedis關閉異常" + e.getMessage());
  11. }
  12. }
  13. }

closeRedis 釋放資源 防止達到最大連接數出現異常 如 :maxTotal=300

如果每次獲取完redis連接,不歸還redis連接池的話,當超過redis的最大連接數。然后redis就雪崩了!注意!注意!!!

 

四、結尾

其實指定redis返回類型,沒什么高大上的!就是用了ObjectMapper類,這個類是Jackson庫的主要類。但是,我百度查過網上很多關於redis的連接工具類,發現卻沒有一個做過這方面的處理!其實這個也不難,在寫業務的時候隨手就能寫出來。我也就是在用redis的時候偶爾蹦出來了這個想法,每都要多那么兩三行於業務無關的代碼讓人非常的抓狂


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM