大白話講解調(diào)用Redis的increment失敗原因及推薦使用詳解
大家在項(xiàng)目中基本都會(huì)接觸到redis,在spring-data-redis-2.*.*.RELEASE.jar中提供了兩個(gè)Helper class,可以讓我們更方便的操作redis中存儲(chǔ)的數(shù)據(jù)。這兩個(gè)Helper class分別是RedisTemplate和StringRedisTemplate,其中StringRedisTemplate是RedisTemplate在存儲(chǔ)String類(lèi)型的時(shí)候的一個(gè)擴(kuò)展子類(lèi)。所以大家在使用redis的時(shí)候:
1、如果操作的是String類(lèi)型,優(yōu)先考慮用StringRedisTemplate;
2、如果是復(fù)雜對(duì)象類(lèi)型,則有限考慮RedisTemplate。
如果大家在使用redis來(lái)進(jìn)行計(jì)數(shù)場(chǎng)景,比如記錄調(diào)用次數(shù)、記錄接口的調(diào)用閾值等,用RedisTemplate出現(xiàn)了以下錯(cuò)誤ERR value is not an integer or out of range,那么先說(shuō)下解決方案,如下:
//類(lèi)中直接注入StringRedisTemplate @Autowired private StringRedisTemplate stringRedisTemplate; //方法體中用以下方式進(jìn)行計(jì)數(shù) stringRedisTemplate.opsForValue().increment("check:incr:str");
即用StringRedisTemplate代替RedisTemplate。
出現(xiàn)上面的原因,就是因?yàn)樾蛄谢瘜?dǎo)致的。我們知道數(shù)據(jù)是以二進(jìn)制形式存儲(chǔ)在redis的,那么就必然涉及到序列化和反向序列化,上面提到的這兩個(gè)Helper class就可以自動(dòng)的幫我們實(shí)現(xiàn)序列化和反向序列,其中二者的主要區(qū)別就是序列化機(jī)制,
1、StringRedisTemplate的序列化機(jī)制是通過(guò)StringRedisSerializer來(lái)實(shí)現(xiàn)的;
2、RedisTemplate的序列化機(jī)制是通過(guò)JdkSerializationRedisSerializer來(lái)實(shí)現(xiàn)的。
increment操作底層就是讀取數(shù)據(jù),然后+1,然后set,只不過(guò)這三個(gè)步驟被redis加了原子操作保證,所以我們從StringRedisTemplate和RedisTemplate的set方法來(lái)分析。先看StringRedisTemplate的源碼如下:
1、stringRedisTemplate.opsForValue().set("check:incr:str", "1");
2、查看第一步的set方法,進(jìn)入DefaultValueOperations類(lèi)的set方法
@Override public void set(K key, V value) { byte[] rawValue = rawValue(value); execute(new ValueDeserializingRedisCallback(key) { @Override protected byte[] inRedis(byte[] rawKey, RedisConnection connection) { connection.set(rawKey, rawValue); return null; } }, true); }
3、查看第二步的處理value的rawValue方法,進(jìn)入AbstractOperations.rawValue方法
@SuppressWarnings("unchecked") byte[] rawValue(Object value) { if (valueSerializer() == null && value instanceof byte[]) { return (byte[]) value; } return valueSerializer().serialize(value); }
4、看到第三步最終調(diào)用了序列化類(lèi)對(duì)value做了序列化處理,我們知道StringRedisTemplate的序列化類(lèi)StringRedisSerializer,
可知第三步的最后一行serialize最后調(diào)用了如下方法
@Override public byte[] serialize(@Nullable String string) { return (string == null ? null : string.getBytes(charset)); }
5、到此我們知道了,value最后存在redis的最底層原理就是第四步的return返回的byte[]。那么就可以這樣認(rèn)為,如果調(diào)用了
stringRedisTemplate.opsForValue().set("check:incr:str", "1");就好比是在redis存儲(chǔ)了"1".getBytes("UTF-8")
public static void main(String[] args) throws UnsupportedEncodingException { byte[] str = "1".getBytes("UTF-8"); for(int i = 0 ; i< str.length; i++) { System.out.println(str[i]); } }
結(jié)論:
上面main方法打印出來(lái)了49,了解了set方法后,可以猜測(cè)調(diào)用increment時(shí)相當(dāng)于對(duì)49直接加1,變成50,get數(shù)據(jù)時(shí)再反向序列化可知對(duì)應(yīng)是數(shù)字2
(注意標(biāo)綠的是我的猜測(cè),可以比較容易理解為什么可以直接increment,事實(shí)是否這樣需要看redis源碼,若有同學(xué)確認(rèn)了,可以回復(fù)下我),
所以可以理解為什么StringRedisTemplate支持increment。
(注意這里并不是說(shuō)RedisTemplate不支持,而是說(shuō)RedisTemplate的默認(rèn)配置的序列化實(shí)現(xiàn)機(jī)制,會(huì)導(dǎo)致RedisTemplate不支持increment)
接下來(lái)以同樣的方式,看RedisTemplate的源碼,如下:
1、根據(jù)redisTemplate.opsForValue().set("check:incr:obj", 1);查看set方法
2、查看第一步的set方法,進(jìn)入DefaultValueOperations類(lèi)的set方法
@Override public void set(K key, V value) { byte[] rawValue = rawValue(value); execute(new ValueDeserializingRedisCallback(key) { @Override protected byte[] inRedis(byte[] rawKey, RedisConnection connection) { connection.set(rawKey, rawValue); return null; } }, true); }
3、查看第二步的處理value的rawValue方法,進(jìn)入AbstractOperations.rawValue方法
@SuppressWarnings("unchecked") byte[] rawValue(Object value) { if (valueSerializer() == null && value instanceof byte[]) { return (byte[]) value; } return valueSerializer().serialize(value); }
4、看到第三步最終調(diào)用了序列化類(lèi)對(duì)value做了序列化處理,我們知道RedisTemplate的序列化類(lèi)是JdkSerializationRedisSerializer,可知第三步的最后一行serialize最后調(diào)用了JdkSerializationRedisSerializer的如下方法
@Override public byte[] serialize(@Nullable Object object) { if (object == null) { return SerializationUtils.EMPTY_ARRAY; } try { return serializer.convert(object); } catch (Exception ex) { throw new SerializationException("Cannot serialize", ex); } }
這里的serializer對(duì)象是SerializingConverter,可以知道實(shí)際調(diào)用的是SerializingConverter.convert(new Integer(1))
5、到此我們知道了,value最后存在redis的最底層原理就是第四步的return返回的byte[]。那么就可以這樣認(rèn)為,如果調(diào)用了
RedisTemplate.opsForValue().set("check:incr:obj", 1);就好比是在redis存儲(chǔ)了下面方法的返回
public static void main(String[] args) throws UnsupportedEncodingException { ByteArrayOutputStream byteStream = new ByteArrayOutputStream(1024); try { ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteStream); objectOutputStream.writeObject(1); objectOutputStream.flush(); byte[] obj = byteStream.toByteArray(); for(int i=0; i<obj.length; i++) { System.out.println(obj[i]); } }catch (Throwable ex) { ex.printStackTrace(); } }
結(jié)論:
打印出來(lái)好多數(shù)據(jù),但是我們存儲(chǔ)的只是一個(gè)整數(shù)1而已,并且根據(jù)序列化過(guò)程中的類(lèi)ObjectOutputStream
的描述(見(jiàn)下)可知序列化后會(huì)包含N多信息
/** * Write the specified object to the ObjectOutputStream. The class of the * object, the signature of the class, and the values of the non-transient * and non-static fields of the class and all of its supertypes are * written.****** */
到此這篇關(guān)于大白話講解調(diào)用Redis的increment失敗原因及推薦使用的文章就介紹到這了,更多相關(guān)Redis increment失敗原因內(nèi)容請(qǐng)搜索本站以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持本站!
版權(quán)聲明:本站文章來(lái)源標(biāo)注為YINGSOO的內(nèi)容版權(quán)均為本站所有,歡迎引用、轉(zhuǎn)載,請(qǐng)保持原文完整并注明來(lái)源及原文鏈接。禁止復(fù)制或仿造本網(wǎng)站,禁止在非www.sddonglingsh.com所屬的服務(wù)器上建立鏡像,否則將依法追究法律責(zé)任。本站部分內(nèi)容來(lái)源于網(wǎng)友推薦、互聯(lián)網(wǎng)收集整理而來(lái),僅供學(xué)習(xí)參考,不代表本站立場(chǎng),如有內(nèi)容涉嫌侵權(quán),請(qǐng)聯(lián)系alex-e#qq.com處理。