概述
Spring Boot 1.4之前,Redis依赖的名称为:spring-boot-starter-redis
,1.4后改名为spring-boot-starter-data-redis
,成为Spring Data一员,而Spring Data项目定位为spring提供统一的数据仓库接口。
本文的源码基于如下3.2.4
版本:
Spring Data Redis底层还是使用Jedis来间接操作Redis,摒弃Jedis中不好的设计,对Jedis中大量API进行归类封装,将同一类型操作封装为Operations接口;而连接池由Spring Data Redis自动管理(基于Apache Commons Pool2),提供高度封装的RedisTemplate类。
在Spring Boot 2.0后不再使用Jedis,因为Jedis采用直连方式存在流阻塞(BIO);改为Lettuce驱动Redis,同时引入Netty、Reactor等技术栈。
自动配置
即RedisAutoConfiguration:
默认提供两个Bean,RedisTemplate和StringRedisTemplate,后者是前者的子类。两者方法基本一致,不同在于操作的数据类型不同,前者是一个泛型类,两个泛型都是Object,Key和Value都可以是对象。后者指明父类的两个泛型都是String,即Key和Value都只能是字符串。
入门使用
- 引入依赖
- 配置Redis连接
resources/application.yml
本机启动单点Redis即可,使用Redis的0号库作为默认库(默认有16个库)。
使用非常简单:
在生产环境下,则需搭建Redis Sentinel或Redis Cluster两种模式保证Redis集群的高可用。
RedisTemplate
RedisTemplate,模板方法设计模式,源码是这样分类的:
- execute类:execute有6个重载方法,executePipelined有4个,还有1个executeWithStickyConnection
- Redis键:支持原生Redis指令的命令,如:
- hasKey:判断是否存在某个Key
- delete:删除Key
- type:查询Key的类型
- unlink:将键从键空间中解除链接。与delete不同,此命令的内存回收是异步发生的
- randomKey:随机返回一个Key
- rename:重命名Key
- expire/expireAt:为给定Key设置TTL时间,Time To Live
- getExpire:返回给定Key的TTL时间
- 排序:有5个重载方法
- 事务
- Redis Server
- Operations
execute
execute方法有2类共6个:
- 用于提交并执行Lua脚本的2个;
- 用于指定RedisCallback、SessionCallback的4个;
RedisCallback:让RedisTemplate进行回调,通过他们可以在同一条连接中执行多个Redis命令;
SessionCallback:相比RedisCallback的优势在于SessionCallback提供良好的封装。
RedisCallback和SessionCallback都是在一个连接里,防止每执行一条命令创建一次连接。
事务
Redis事务相关命令:
Redis Server
相关命令:
Operations
提供各种Operations操作,这些最终转化为RedisCallback来执行的。也就是说通过使用RedisCallback可以实现更强的功能。通常不直接操作键值,而是通过opsForXxx()
访问;实现RedisOperations接口,这个接口定义一系列与Redis相关的基础数据操作接口,数据类型分别与下列API对应:
若以bound开头,则意味着在操作之初就会绑定一个Key,后续的所有操作便默认是对该Key的操作。
CAS操作
CAS,Compare and Set,通常有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。CAS也通常与并发,乐观锁,非阻塞,机器指令等关键词放到一起讲解。
通过redisTemplate.opsForValue()
或redisTemplate.boundValueOps()
可得到一个ValueOperations或BoundValueOperations接口(以值为字符串的操作接口为例)。这些xxxOperations
都是接口,提供基础操作外,还提供一系列CAS操作,几乎都有重载方法:
- setIfAbsent:将Key的值设为Value,当且仅当Key不存在时,设置成功返回1,设置失败返回0;
- getAndSet:将给定Key的值设为Value,并返回旧值(Old Value);
- increment:将Key所储存的值加上增量delta(如果方法没有delta,则加1)。如果Key不存在,则Key的值会先被初始化为0再执行
发布订阅
Redis内置channel机制,可以用于实现分布式的队列和广播。RedisTemplate.convertAndSend()
用于发送消息,与RedisMessageListenerContainer配合接收,可以实现一个简易的发布订阅。
Lua脚本
Redis内置Lua的解析器,RedisTemplate中包含一个Lua执行器ScriptExecutor,可执行Lua脚本完成原子性操作。
完成对Lua脚本的调用。Redis+Lua脚本实现分布式的应用限流。
序列化
RedisTemplate类里声明的一系列序列化器:
在afterPropertiesSet
方法中可看到默认的序列化方案:
默认的方案是使用JdkSerializationRedisSerializer,使用Redis Desktop Manager等工具查看时不太友好。字符串和使用JDK序列化之后的字符串是两个概念。
查看set方法的源码:
最终与Redis交互使用的是原生connection,键值则全部是字节数组,意味着所有的序列化都依赖于应用层完成,Redis只认字节!
StringRedisSerializer
StringRedisTemplate继承自RedisTemplate,提供StringRedisSerializer的实现:
即只能存取字符串。使用什么样的序列化器序列化,就必须使用同样的序列化器反序列化。
RedisSerializer
接口源码如下:
其实现类有:
- JdkSerializationRedisSerializer:默认使用的序列化方案
- StringRedisSerializer:StringRedisTemplate使用
- GenericToStringSerializer:依赖于内部的ConversionService,将所有的类型转存为字符串
- GenericJackson2JsonRedisSerializer:以JSON的形式序列化对象
- Jackson2JsonRedisSerializer:以JSON的形式序列化对象
- OxmSerializer:以XML的形式序列化对象
可以将全局的RedisTemplate覆盖,也可以在使用时在局部实例化一个RedisTemplate替换(不依赖于IOC容器)需要根据实际的情况选择替换的方式,以Jackson2JsonRedisSerializer为例:
Kryo
也可以考虑根据自己项目和需求的特点,扩展序列化器。如为了追求性能,可能考虑使用Kryo序列化器替换缓慢的JDK序列化器:
Kyro线程不安全,使用一个ThreadLocal来维护,也可以挑选其他高性能的序列化方案如Hessian,Protobuf。
属性配置
项目开发里,存在个性化的属性配置,参考RedisProperties源码。
RedisCallback
TODO
SessionCallback
TODO