大白话讲解调用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,那么先说下解决方案,如下:
//类中直接注入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方法
@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方法
@SuppressWarnings("unchecked") byte[] rawValue(Object value) { if (valueSerializer() == null value instanceof byte[]) { return (byte[]) value; return valueSerializer().serialize(value);
4、看到第三步最终调用了序列化类对value做了序列化处理,我们知道StringRedisTemplate的序列化类StringRedisSerializer,
可知第三步的最后一行serialize最后调用了如下方法
@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 )
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方法
@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方法
@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的如下方法
@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存储了下面方法的返回
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多信息
* 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失败原因内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!
我想要获取技术服务或软件
服务范围:MySQL、ORACLE、SQLSERVER、MongoDB、PostgreSQL 、程序问题
服务方式:远程服务、电话支持、现场服务,沟通指定方式服务
技术标签:数据恢复、安装配置、数据迁移、集群容灾、异常处理、其它问题
本站部分文章参考或来源于网络,如有侵权请联系站长。
数据库远程运维 大白话讲解调用Redis的increment失败原因及推荐使用详解
相关文章
- 新技术Web项目使用Redis实现高性能(web项目使用redis)
- 使用Redis实现分布式缓存能力(分布式缓存redis)
- 红色呼唤:Redis初探(红丸redis)
- 数据实现极速性能:使用Redis缓存MySQL数据(redis缓存mysql)
- 使用Redis实现自减操作(redis自减)
- 缓存活用Redis实现浏览器缓存升级(redis浏览器)
- 使用Redis存储文件的畅快体验(redis存放文件)
- Redis 重新开始:新的旅程(redis重启了)
- 一步步教你如何安装Redis服务器(怎么安装redis服务器)
- 快速掌握数据库Redis的下载方法(怎么下载数据库redis)
- 使用Redis实现更可靠的延时任务(延时任务redis)
- 一步步移除Redis实例的指南(移除redis实例)
- 实现高效登录系统使用Redis缓存技术(登录系统使用redis)
- 探索Redis数据库容量大小(查看redis数据库容量)
- 探索Redis应用程序的端口号奥秘(查看redis使用端口)
- 处理延迟任务利用Redis解决方案(数据库redis延迟任务)
- 单元测试验证Redis缓存技术的可靠性(单测redis缓存)
- 教程传智播客学习Redis,掌握实战视频教程(传智redis视频)
- 了解多少数据量才需要使用Redis(多少数据量使用redis)
- Redis免费使用还是付费(redis 需要付费吗)
- 利用Redis连接局域网中的IP地址(redis连接区域网ip)
- 行熟悉Redis,学会进入数据库命令行(redis进入数据库命令)
- 端口Redis突破6380端口,拓展新前景(redis进入6380)
- 使用Redis集群可视化工具加速系统开发(redis集群可视化工具)
- 红色卓越Redis缓存的神速(redis缓存速度)
- 使用Redis连接远程主机的技术指南(redis远程主机连接)
- Redis双翼齐飞读写分离哨兵结合实现高效服务(redis读写分离 哨兵)
- 使用Redis缓存系统提升性能配置篇(redis缓存系统配置)
- 一键瞬间更新使用Redis缓存批量Key提高效率(redis缓存批量key)