1. 程式人生 > >使用Spring Cache + Redis + Jackson Serializer快取資料庫查詢結果中序列化問題的解決

使用Spring Cache + Redis + Jackson Serializer快取資料庫查詢結果中序列化問題的解決

應用場景

我們希望通過快取來減少對關係型資料庫的查詢次數,減輕資料庫壓力。在執行DAO類的select***(), query***()方法時,先從Redis中查詢有沒有快取資料,如果有則直接從Redis拿到結果,如果沒有再向資料庫發起查詢請求取資料。

序列化問題

要把domain object做為key-value對儲存在redis中,就必須要解決物件的序列化問題。Spring Data Redis給我們提供了一些現成的方案:

  • JdkSerializationRedisSerializer. 使用JDK提供的序列化功能。 優點是反序列化時不需要提供型別資訊(class),但缺點是序列化後的結果非常龐大,是JSON格式的5倍左右,這樣就會消耗redis伺服器的大量記憶體。
  • Jackson2JsonRedisSerializer. 使用Jackson庫將物件序列化為JSON字串。優點是速度快,序列化後的字串短小精悍。但缺點也非常致命,那就是此類的建構函式中有一個型別引數,必須提供要序列化物件的型別資訊(.class物件)。 通過檢視原始碼,發現其只在反序列化過程中用到了型別資訊。

如果用方案一,就必須付出快取多佔用4倍記憶體的代價,實在承受不起。如果用方案二,則必須給每一種domain物件都配置一個Serializer,即如果我的應用裡有100種domain物件,那就必須在spring配置檔案中配置100個Jackson2JsonRedisSerializer,這顯然是不現實的。

通過google, 發現spring data redis專案中有一個#145 pull request, 而這個提交請求的內容正是解決Jackson必須提供型別資訊的問題。然而不幸的是這個請求還沒有被merge。但我們可以把程式碼copy一下放到自己的專案中:

/**
 * @author Christoph Strobl
 * @since 1.6
 */
public class GenericJackson2JsonRedisSerializer implements RedisSerializer<Object> {

    private final ObjectMapper mapper;

    /**
     * Creates {@link
GenericJackson2JsonRedisSerializer} and configures {@link ObjectMapper} for default typing. */
public GenericJackson2JsonRedisSerializer() { this((String) null); } /** * Creates {@link GenericJackson2JsonRedisSerializer} and configures {@link ObjectMapper} for default typing using the * given {@literal name}. In case of an {@literal empty} or {@literal null} String the default * {@link JsonTypeInfo.Id#CLASS} will be used. * * @param classPropertyTypeName Name of the JSON property holding type information. Can be {@literal null}. */ public GenericJackson2JsonRedisSerializer(String classPropertyTypeName) { this(new ObjectMapper()); if (StringUtils.hasText(classPropertyTypeName)) { mapper.enableDefaultTypingAsProperty(DefaultTyping.NON_FINAL, classPropertyTypeName); } else { mapper.enableDefaultTyping(DefaultTyping.NON_FINAL, As.PROPERTY); } } /** * Setting a custom-configured {@link ObjectMapper} is one way to take further control of the JSON serialization * process. For example, an extended {@link SerializerFactory} can be configured that provides custom serializers for * specific types. * * @param mapper must not be {@literal null}. */ public GenericJackson2JsonRedisSerializer(ObjectMapper mapper) { Assert.notNull(mapper, "ObjectMapper must not be null!"); this.mapper = mapper; } /* * (non-Javadoc) * @see org.springframework.data.redis.serializer.RedisSerializer#serialize(java.lang.Object) */ @Override public byte[] serialize(Object source) throws SerializationException { if (source == null) { return SerializationUtils.EMPTY_ARRAY; } try { return mapper.writeValueAsBytes(source); } catch (JsonProcessingException e) { throw new SerializationException("Could not write JSON: " + e.getMessage(), e); } } /* * (non-Javadoc) * @see org.springframework.data.redis.serializer.RedisSerializer#deserialize(byte[]) */ @Override public Object deserialize(byte[] source) throws SerializationException { return deserialize(source, Object.class); } /** * @param source can be {@literal null}. * @param type must not be {@literal null}. * @return {@literal null} for empty source. * @throws SerializationException */ public <T> T deserialize(byte[] source, Class<T> type) throws SerializationException { Assert.notNull(type, "Deserialization type must not be null! Pleaes provide Object.class to make use of Jackson2 default typing."); if (SerializationUtils.isEmpty(source)) { return null; } try { return mapper.readValue(source, type); } catch (Exception ex) { throw new SerializationException("Could not read JSON: " + ex.getMessage(), ex); } } }

然後在配置檔案中使用這個GenericJackson2JsonRedisSerializer:

<bean id="jacksonSerializer" class="com.fh.taolijie.component.GenericJackson2JsonRedisSerializer">
    </bean>

重新構建部署,我們發現這個serializer可以同時支援多種不同型別的domain物件,問題解決。

相關推薦

使用Spring Cache + Redis + Jackson Serializer快取資料庫查詢結果序列問題的解決

應用場景 我們希望通過快取來減少對關係型資料庫的查詢次數,減輕資料庫壓力。在執行DAO類的select***(), query***()方法時,先從Redis中查詢有沒有快取資料,如果有則直接從Redis拿到結果,如果沒有再向資料庫發起查詢請求取資料。

使用SQL Workbench 進行 SQL Sever 資料庫查詢結果的視覺展現

使用SQL Workbench 進行 SQL Sever 資料庫查詢結果的視覺化展現   首先你的機器必須有java執行環境。   1. 下載SQL Workbench (下載地址:http://www.sql-workbench.eu/Workbench-Bui

SPRING CACHE REDIS 註解式實現快取策略

為了解決資料庫查詢效率瓶頸,提升併發系統能力,快取的應用已經非常普遍和必要了。剛接觸REDIS時,如何使SPRING框架與REDIS更高效地整合,困擾了我很長時間。 先說下不使用SPRING CACHE時的兩種快取應用模式: 1.使用redis作為持久層的二級快

spring cache redis 高併發下返回null

在使用springdata操作快取中,當訪問量比較大時,有可能返回null導致資料不準確,發生機率在0.01%或以下,雖然已經低於壓測標準,但是還是會影響部分使用者,經過一番篩查,發現原因如下: RedisCache 類中 有get方法,存在明顯的邏輯錯誤 “先判斷是否存在,再去get”,程

ssm專案 已純註解的方式 引入spring cache+ redis

通過純註解的方式引入redis,這樣可以簡單的通過 @Cacheable、@CacheEvict、@CachePut 來操作快取 在pom.xml 檔案中引入相關jar 包 <dependency> <groupId

Spring+ehcache+redis兩級快取

https://www.cnblogs.com/wchxj/p/8159609.html 問題描述 場景:我們的應用系統是分散式叢集的,可橫向擴充套件的。應用中某個介面操作滿足以下一個或多個條件: 1. 介面運行復雜代價大, 2. 介面返回資料量大, 3. 介

利用redis快取mysql查詢結果,關於快取命名

問題: 在對 mysql 查詢結果快取時,我一直都有這樣的疑惑。那就是當更新一條記錄或者刪除一條記錄時候,那麼對應的主鍵所有的查詢結果必須同時更新,那麼在就是說在 update table set field = value where id = XXX

使用spring-data-redis選用特定的資料庫

redis中,預設的資料庫個數為16。索引為0-15 在使用spring-data-redis時,選擇某個資料庫,設定連線工廠的dbIndex即可。 例如,使用lettuce選擇某個資料庫,連線池和

Spring整合Redis實現資料快取

一、什麼是RedisRedis 是一個開源(BSD許可)的,記憶體中的資料結構儲存系統,它可以用作資料庫、快取和訊息中介軟體。 它支援多種型別的資料結構,如 字串(strings), 雜湊(hashes), 列表(lists), 集合(sets), 有序集合(sorted s

Spring+ehcache+redis兩級快取--快取實戰篇(1)

在上篇《效能優化-快取篇》中已經從理論上介紹了快取,其實,快取簡單易用,更多精力關注的是根據實際業務的選擇快取策略。 本文主要介紹為什麼要構建ehcache+redis兩級快取?以及在實戰中如何實現?思考如何配置快取策略更合適?這樣的方案可能遺留什麼問題?JU

用python指令碼匯出mysql資料庫查詢結果到Excel表

最近需要導資料的情況太多,總用跳板機上的navicat工具有點效率低,也覺得挺麻煩的(由於跳板機無法連通外網 所以匯出資料檔案還得通過sftp傳到本機)anyway 還是寫個指令碼好了。之前寫過一個shell指令碼做的定時匯出任務,現在試試用python寫下 主要用到的庫有: pymysql -- 連資料庫

python pymysql連結資料庫查詢結果轉為Dataframe

import pymysql import pandas as pd def con_sql(db,sql): # 建立連線 db = pymysql.connect(host='127.0.0.1', port=3308, user='name', passwd='passwo

MySQL資料庫查詢結果過大解決記憶體溢位的解決方案

正常來說,一般是不會出現這種情況的,但也不能保證,偶爾有這種情況發生,解決方案如下: 1.使用分頁查詢語句。    因為分頁查詢每次只會查詢少量資料,所以不會佔用太多記憶體,而且資料量很大的時候,分頁

將GridView和資料庫查詢結果繫結起來後,點選查詢出了結果。但是點選第二面或者其他的,就直接變空白了。(已經解決)

public partial class Location_BJ_Location : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { if(!Page.IsPostB

JPA查詢List ID相同導致 返回List與資料庫查詢結果不一致 的問題

1.統計資料的時候  查詢只返回了需要的欄位 select     sum(result) result,     name , from     table_test group by name having class != '100001' 資料庫返回結

Python oracle資料庫查詢結果以字典形式儲存,取多條結果記錄數的實現

方法: def executeSQL(self,sql,params): conn = self.connectDB() conn = self.cursor try: self.r = conn.execute(sql,params) s

資料庫查詢結果快速生成markdown格式表格

在寫資料庫相關博文時,經常需要把結果集展示到頁面上。用圖片真的是太麻煩了,一篇文章都沒寫完我就已經忍無可忍了。 於是寫了一段程式碼來生成基礎的markdown格式表格程式碼。 import ja

關於資料庫查詢語句的where 1=1條件的應用解析

where 1=1有什麼用?在SQL語言中,寫這麼一句話就跟沒寫一樣。 select * from table1 where 1=1與select * from table1完全沒有區別,其目的就只有一個,where 的條件為永真,得到的結果就是未加約束條件的。 其他的寫法

JavaSE__原始的JDBC連線資料庫,查詢結果集 (基礎篇)

展示原生的JDBC連線資料庫,以及存在的問題。 import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql

laravel 資料庫查詢結果自動轉陣列

    今天在專案中再次碰見laravel中從資料庫中查詢到的結果是物件,用toArray也不好用,之前改過一次,印象中是在/confing/database.php,    'fetch' => PDO::FETCH_OBJ,這行改為'fetch' =>FETC