使用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