Redis?Hash序列化存儲(chǔ)的問(wèn)題及解決方案
這里說(shuō)的是Spring Data Redis(一下簡(jiǎn)稱(chēng)SDR)設(shè)置Hash存儲(chǔ)的序列化。
SDR序列化方式有多種
如:
StringRedisSerializer
JdkSerializationRedisSerializer
Jackson2JsonRedisSerializer
OxmSerializer
- 等等
目前我有個(gè)需求,是將數(shù)據(jù)用hash的形式存到Redis數(shù)據(jù)庫(kù)中,在網(wǎng)上搜了下實(shí)現(xiàn)方式,部分代碼如下:
@Bean public RedisTemplate<String,Object> redisTemplate(){ RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); initDomainRedisTemplate(redisTemplate, redisConnectionFactory); return redisTemplate; } /** * 設(shè)置數(shù)據(jù)存入 redis 的序列化方式 * * @param redisTemplate * @param factory */ private void initDomainRedisTemplate(RedisTemplate<String, Object> redisTemplate, RedisConnectionFactory factory) { redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer()); redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer()); redisTemplate.setConnectionFactory(factory); } /** * 實(shí)例化 HashOperations 對(duì)象,可以使用 Hash 類(lèi)型操作 * * @param redisTemplate * @return */ @Bean public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) { return redisTemplate.opsForHash(); }
對(duì)Redis的存儲(chǔ)設(shè)置是我自己寫(xiě)的
/** * 添加 * * @param key key * @param filed filed * @param domain 對(duì)象 */ public void hset(String key,String filed,Object domain){ System.out.println("開(kāi)始使用filed設(shè)置"); hashOperations.put(key, filed, domain); } /** * 查詢(xún) * * @param key 查詢(xún)的key * @param field 查詢(xún)的field * @return */ public Object hget(String key,String field) { return hashOperations.get(key, field); }
方法:
@RequestMapping("/mytest") public Object myTest() { redisUtils.hset("mykey","myfield","myvalue"); return redisUtils.hget("mykey","myfield"); }
Hash的存儲(chǔ)跟String有些不同,從表面上看Hash多了個(gè)field,這個(gè)自己稍微想下就可以理解了。
執(zhí)行上面的代碼后,用客戶(hù)端查看所存儲(chǔ)的值:
上圖顯示的是亂碼。
用redis-cli查看:
這里顯示的是我存的值myvalue前多了些東西,這是序列化的時(shí)候所加的一些東西。
執(zhí)行方法時(shí)前端得到的值:
這里可見(jiàn)從redis中取出的值是跟我存入的完全一樣的(這是因?yàn)槿〕龅臅r(shí)候Spring有做反序列化處理)。
如果從redis-cli中直接存儲(chǔ):
host:6379> hset mykey2 myfield2 myvalue2 (integer) 1 host:6379> hget mykey2 myfield2 "myvalue2"
查看客戶(hù)端中的值:
在這里存入的hash顯示的是正常的。
所以我猜測(cè)之前redis桌面客戶(hù)端顯示“不正常”的原因應(yīng)該是出在序列化的時(shí)候。
更改序列化方法
改為StringRedisSerializer方式(一般key都是字符串,所以繼續(xù)使用StringRedisSerializer,這里把Hash的value序列化改為StringRedisSerializer):
/** * 設(shè)置數(shù)據(jù)存入 redis 的序列化方式 * * @param redisTemplate * @param factory */ private void initDomainRedisTemplate(RedisTemplate<String, Object> redisTemplate, RedisConnectionFactory factory) { redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer()); redisTemplate.setConnectionFactory(factory); }
查看客戶(hù)端的值:
這時(shí)顯示OK了,redis-cli中顯示的也是OK的。
所以,我們遇到的問(wèn)題貌似解決了。
因?yàn)槲乙鎯?chǔ)的是hash,而hashOperations為我們提供了另外一個(gè)方法putAll,這個(gè)方法支持對(duì)HashMap的操作。
代碼:
/** * 添加 * * @param key key * @param hm 要存入的hash表 */ public void hset(String key, HashMap<String,Object> hm){ System.out.println("開(kāi)始使用hashmap設(shè)置"); hashOperations.putAll(key,hm); }
因?yàn)槲业膆ashmap中要存的值包含時(shí)間,所以就要把值設(shè)為Object,代碼:
@RequestMapping("/hm") public void hmsetTest() { HashMap<String,Object> hm =new HashMap<String,Object>(); hm.put("myFieldKey","myFieldKey"); hm.put("createTime",new Date()); redisUtils.hset("mykey",hm); }
執(zhí)行結(jié)果:
這時(shí)在調(diào)用的時(shí)候直接報(bào)錯(cuò)了,說(shuō)是Date類(lèi)型無(wú)法轉(zhuǎn)String。
回到單個(gè)值存入的方法上:
public void hset(String key,String filed,Object domain){ System.out.println("開(kāi)始使用filed設(shè)置"); hashOperations.put(key, filed, domain); }
用這里執(zhí)行Date的存儲(chǔ),結(jié)果還是包這個(gè)異常。
由此可見(jiàn),使用StringRedisSerializer序列化并不能解決我們的問(wèn)題,而且還有使用的限制。OxmSerializer這個(gè)東西我不太熟悉,所以沒(méi)有測(cè)試。
使用Jackson2JsonRedisSerializer
更改序列化方式
redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<Object>(Object.class));
執(zhí)行單個(gè)日期操作結(jié)果如下:
這里顯示的日期被轉(zhuǎn)成時(shí)間戳形式存儲(chǔ)的。
執(zhí)行hashmap:
結(jié)果顯示執(zhí)行的也是成功的,如果跟StringRedisSerializer比較會(huì)發(fā)現(xiàn),存儲(chǔ)字符串的時(shí)候值得最外層會(huì)被加上“”。
繼續(xù)使用JdkSerializationRedisSerializer
可以正常存儲(chǔ),但是顯示形式一樣不是我們期望的。
對(duì)于這個(gè)問(wèn)題我網(wǎng)上有種解決方法,在redis-cli中查看的時(shí)候使用–raw指令
即啟動(dòng)指令為:redis-cli –raw。這種方式也可以正常的查看中文。但是查看的時(shí)候日期依然有問(wèn)題,而且字符串前邊會(huì)多些東西(t)。
OxmSerializer這個(gè)東西我不熟悉,所以就沒(méi)有測(cè)試,但是網(wǎng)上一般都說(shuō)建議使用JdkSerializationRedisSerializer,而且這種效率是最高的,沒(méi)辦法,畢竟是原生的。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
redis在Linux系統(tǒng)下的環(huán)境配置和redis的全局命令大全
在Linux系統(tǒng)中我們經(jīng)常使用Redis作為高性能的緩存數(shù)據(jù)庫(kù),然而有時(shí)候我們需要在系統(tǒng)中多個(gè)地方使用Redis命令,這就需要將Redis的全局命令設(shè)置好,這篇文章主要給大家介紹了關(guān)于redis在Linux系統(tǒng)下的環(huán)境配置和redis的全局命令大全的相關(guān)資料,需要的朋友可以參考下2024-05-05淺談redis內(nèi)存數(shù)據(jù)的持久化方式
這篇文章主要介紹了淺談redis內(nèi)存數(shù)據(jù)的持久化方式,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-03-03Redis分布式鎖方案設(shè)計(jì)之防止訂單重復(fù)提交或支付
這篇文章主要為大家介紹了Redis分布式鎖之防止訂單重復(fù)提交或支付方案設(shè)計(jì)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09redis keys與scan命令的區(qū)別說(shuō)明
這篇文章主要介紹了redis keys與scan命令的區(qū)別說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-03-03深入解析Redis中常見(jiàn)的應(yīng)用場(chǎng)景
這篇文章主要給大家介紹了關(guān)于Redis中常見(jiàn)的應(yīng)用場(chǎng)景的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-09-09Redis中什么是Big?Key(大key)問(wèn)題?如何解決Big?Key問(wèn)題?
大key并不是指key的值很大,而是key對(duì)應(yīng)的value很大,下面這篇文章主要給大家介紹了Redis中什么是Big?Key(大key)問(wèn)題?如何解決Big?Key問(wèn)題的相關(guān)資料,需要的朋友可以參考下2023-03-03Redis做數(shù)據(jù)持久化的解決方案及底層原理
Redis有兩種方式來(lái)實(shí)現(xiàn)數(shù)據(jù)的持久化,分別是RDB(Redis Database)和AOF(Append Only File),今天通過(guò)本文給大家聊一聊Redis做數(shù)據(jù)持久化的解決方案及底層原理,感興趣的朋友一起看看吧2021-07-07