2019独角兽企业重金招聘Python工程师标准>>>
Java,操作Redis,标配Jedis,如果加上Spring,自然选用Spring Data Redis(简称“sdr”)。
最近在开发中,使用Redis来实现数据点击量的统计存储功能。为什么使用Redis?点击量之类的功能,需要频繁触发更新操作,而且高并发访问时,还需要考虑操作冲突导致数据不一致的问题。而Redis是内存型存储,相比关系型数据库,操作更快,避免了频繁的文件写操作。更重要的是,Redis中有个INCR和INCRBY命令,都可以实现值递增的原子性操作,方便了解决了高并发时的冲突问题。
Redis手册中的命令说明很详尽,还有Redis中文命令参考的网站可供使用,在此感谢无私的翻译人员。如下:
Redis中文参考手册
sdr中针对redis的命令,一一提供了对应的方法可供使用,结合redis命令参考sdr的api,很容易上手。但唯一不足地是,sdr提供的api太简略了,只提供了函数和参数,参数的说明、返回值的说明、异常情况的说明统统没有,这个只能自己在实践的过程中用代码来认知了。
sdr中提供了一个ValueOperations的接口来针对Redis中的Key-Value的命令操作。通过粗略追踪源码的手段,可以大概了解到,sdr框架操作Redis的实质,是和Redis服务通信,告知Redis服务执行指定的命令。ValueOperations中有increment方法,本质上是向Redis服务发送的INCRBY命令。当然要实现点击量递增的功能,需要使用ValueOperations的increment方法。
Redis采用Key-Value的格式实现了多种结构的数据,例如List、Set等。但Redis中的Key和Value存储的都是字符串,而Java是面向对象的语言,大部分情况下操作数据也都是对象,那么Java与Redis的结合手段自然是序列化了。sdr提供了丰富的序列化策略,所以在配置sdr时,可以显示地指定Key和Value的序列化器,如下代码所示。这部分不了解的同志可以搜索相关的内容去了解,这里不再做过多说明。
如果没有显示指定Key或Value的序列化器,默认采用JdkSerializationRedisSerializer。StringRedisSerializer将数据存储为正常的字符串,而且JdkSerializationRedisSerializer则是将数据存储为一串序列字符串,还需通过反序列化才能得到正常的字符串。
当在使用ValueOperations之类的接口时,值得注意的是,接口是使用泛型的,如ValueOperations
在递增之前,由于使用ValueOperations的set方法为点击量设置了初始值,Key的序列化器采用的是JdkSerializationRedisSerializer,其将初始值变成了序列化字符串存入了Redis,而Redis执行INCRBY命令时是无法识别序列化字符串为整数的,所以会提示错误。
有人提出一个很奇怪的现象,如果不使用set方法设置点击量的初始值,直接调用increment方法,Redis中存入的是正常的数字字符串,没有被序列化!这个现象仔细想想,跟sdr没有半毛钱关系的,更扯不到序列化。看上图中的一句说明,“如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作”。在点击量的Key不存在时,直接调用increment方法,递增量的初始值是由Redis生成的,根本没有走sdr的序列化策略,又何来序列化。
那说了这么多,点击量的递增到底如何来解决呢?我得出的结论是,在使用ValueOperations的increment功能时,设置Value的序列化器为StringRedisSerializer,这样存入Redis的值即为可识别的数字字符串了。可以在配置文件中配置,也可以在代码中动态的指定Value的序列化器,下面贴出配置文件中的配置方式:
参考链接:
http://docs.spring.io/spring-data/redis/docs/1.4.0.M1/reference
http://shift-alt-ctrl.iteye.com/blog/1887370