- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章大白话讲解调用Redis的increment失败原因及推荐使用详解由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
大家在项目中基本都会接触到redis,在spring-data-redis-2.*.*.RELEASE.jar中提供了两个Helper class,可以让我们更方便的操作redis中存储的数据。这两个Helper class分别是RedisTemplate和StringRedisTemplate,其中StringRedisTemplate是RedisTemplate在存储String类型的时候的一个扩展子类。所以大家在使用redis的时候:
1、如果操作的是String类型,优先考虑用StringRedisTemplate; 。
2、如果是复杂对象类型,则有限考虑RedisTemplate.
如果大家在使用redis来进行计数场景,比如记录调用次数、记录接口的调用阈值等,用RedisTemplate出现了以下错误ERR value is not an integer or out of range,那么先说下解决方案,如下:
1
2
3
4
5
6
|
//类中直接注入StringRedisTemplate
@Autowired
private
StringRedisTemplate stringRedisTemplate;
//方法体中用以下方式进行计数
stringRedisTemplate.opsForValue().increment(
"check:incr:str"
);
|
即用StringRedisTemplate代替RedisTemplate.
出现上面的原因,就是因为序列化导致的。我们知道数据是以二进制形式存储在redis的,那么就必然涉及到序列化和反向序列化,上面提到的这两个Helper class就可以自动的帮我们实现序列化和反向序列,其中二者的主要区别就是序列化机制, 。
1、StringRedisTemplate的序列化机制是通过StringRedisSerializer来实现的; 。
2、RedisTemplate的序列化机制是通过JdkSerializationRedisSerializer来实现的.
increment操作底层就是读取数据,然后+1,然后set,只不过这三个步骤被redis加了原子操作保证,所以我们从StringRedisTemplate和RedisTemplate的set方法来分析。先看StringRedisTemplate的源码如下:
1、stringRedisTemplate.opsForValue().set("check:incr:str", "1"),
2、查看第一步的set方法,进入DefaultValueOperations类的set方法 。
1
2
3
4
5
6
7
8
9
10
11
|
@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方法,进入AbstractOperations.rawValue方法 。
1
2
3
4
5
6
7
|
@SuppressWarnings
(
"unchecked"
)
byte
[] rawValue(Object value) {
if
(valueSerializer() ==
null
&& value
instanceof
byte
[]) {
return
(
byte
[]) value;
}
return
valueSerializer().serialize(value);
}
|
4、看到第三步最终调用了序列化类对value做了序列化处理,我们知道StringRedisTemplate的序列化类StringRedisSerializer, 可知第三步的最后一行serialize最后调用了如下方法 。
1
2
3
4
|
@Override
public
byte
[] serialize(
@Nullable
String string) {
return
(string ==
null
?
null
: string.getBytes(charset));
}
|
5、到此我们知道了,value最后存在redis的最底层原理就是第四步的return返回的byte[]。那么就可以这样认为,如果调用了 stringRedisTemplate.opsForValue().set("check:incr:str", "1");就好比是在redis存储了"1".getBytes("UTF-8") 。
1
2
3
4
5
6
|
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]);
}
}
|
结论:
上面main方法打印出来了49,了解了set方法后,可以猜测调用increment时相当于对49直接加1,变成50,get数据时再反向序列化可知对应是数字2 。
(注意标绿的是我的猜测,可以比较容易理解为什么可以直接increment,事实是否这样需要看redis源码,若有同学确认了,可以回复下我), 。
所以可以理解为什么StringRedisTemplate支持increment.
(注意这里并不是说RedisTemplate不支持,而是说RedisTemplate的默认配置的序列化实现机制,会导致RedisTemplate不支持increment) 。
接下来以同样的方式,看RedisTemplate的源码,如下: 。
1、根据redisTemplate.opsForValue().set("check:incr:obj", 1);查看set方法 2、查看第一步的set方法,进入DefaultValueOperations类的set方法 。
1
2
3
4
5
6
7
8
9
10
11
|
@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方法,进入AbstractOperations.rawValue方法 。
1
2
3
4
5
6
7
|
@SuppressWarnings
(
"unchecked"
)
byte
[] rawValue(Object value) {
if
(valueSerializer() ==
null
&& value
instanceof
byte
[]) {
return
(
byte
[]) value;
}
return
valueSerializer().serialize(value);
}
|
4、看到第三步最终调用了序列化类对value做了序列化处理,我们知道RedisTemplate的序列化类是JdkSerializationRedisSerializer,可知第三步的最后一行serialize最后调用了JdkSerializationRedisSerializer的如下方法 。
1
2
3
4
5
6
7
8
9
10
11
|
@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对象是SerializingConverter,可以知道实际调用的是SerializingConverter.convert(new Integer(1)) 。
5、到此我们知道了,value最后存在redis的最底层原理就是第四步的return返回的byte[]。那么就可以这样认为,如果调用了 RedisTemplate.opsForValue().set("check:incr:obj", 1);就好比是在redis存储了下面方法的返回 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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();
}
}
|
结论: 打印出来好多数据,但是我们存储的只是一个整数1而已,并且根据序列化过程中的类ObjectOutputStream 的描述(见下)可知序列化后会包含N多信息 。
1
2
3
4
5
6
|
/**
* 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.******
*/
|
到此这篇关于大白话讲解调用Redis的increment失败原因及推荐使用的文章就介绍到这了,更多相关Redis increment失败原因内容请搜索我以前的文章或继续浏览下面的相关文章希望大家以后多多支持我! 。
原文链接:https://www.cnblogs.com/share2perfect/p/15531505.html 。
最后此篇关于大白话讲解调用Redis的increment失败原因及推荐使用详解的文章就讲到这里了,如果你想了解更多关于大白话讲解调用Redis的increment失败原因及推荐使用详解的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
1、流程控制语句主要有if、ii...else、elseif(有时也可以写成else if)、switch四种。 PHP中语句格式为: if(条件满足) {执行语句} if(条件满足) {执行
目录 DFS初步概念 DFS例题-迷宫游戏 题目描述 输入输出格式 输入输出样例
This question两年前被问到,但它提到的资源要么不是很有帮助(恕我直言),要么链接不再有效。 必须有一些很好的教程才能理解 Phaser .我已经阅读了 javadoc,但我的眼睛呆滞了,因
This question两年前被问到,但它提到的资源要么不是很有帮助(恕我直言),要么链接不再有效。 必须有一些很好的教程才能理解 Phaser .我已经阅读了 javadoc,但我的眼睛呆滞了,因
这个正则出自这个网站 http://www.regexlab.com/zh/regref.htm 正向预搜索:"(?=xxxxx)","(?!xxxxx)"
chr(9)、chr(10)、chr(13)、chr(32)、chr(34) 所有关于 ASCII码的表格:[url]http://www.asciitable.com/[/url] chr(13)
我是一名优秀的程序员,十分优秀!