继续码字
5.10.Redis Transactions(Redis的事务)
Redis通过命令multi,exec和discard对事务的支持。这些操作命令在RedisTemplate也有对应的方法。然而,RedisTemplate它不能保证一个事务的所有操作都在相同连接中完成。
Spring Data Redis 为用户提供SessionCallback接口,它能保证一个事务的所有操作都在相同的连接中完成。例如:
//execute a transaction
List<Object> txResults = redisTemplate.execute(new SessionCallback<List<Object>>() {
public List<Object> execute(RedisOperations operations) throws DataAccessException {
operations.multi();
operations.opsForSet().add("key", "value1");
// This will contain the results of all ops in the transaction
return operations.exec();
}
});
System.out.println("Number of items added to set: " + txResults.get(0));
提示:
在版本1.1的RedisConnection和RedisTemplate的方法exec最大的改变是,在先前的方法直接返回执行事务的结果。这就意味着这个数据类型经常不同于RedisConnection返回的结果。举个例子,zAdd返回一个boolean值表明这个元素已经添加到有序集合中。但是大多数连接器返回时一个long型的值,Spring Data Redis需要进行转换。其他公共不同的是对于set操作大多数连接器返回一个状态作为响应(通常是字符串“OK”)。这些返回是Spring Data Redis典型废弃的形式。在1.1版本之前。这些转换都不会在exec执行。也就是Redis不会反序列这个结果。因为这里经常包含原生的字节数组。如果这个行为改变你的应用,你可以在RedisConnectionFactory设置convertPipelineAndTxResults为false来禁止这种行为。
5.10.1.@Transactional Support(注解支持)
事务支持在默认情况的是关闭的,你需要为每个RedisTemplate显式开启,通过设置setEnableTransactionSupport(true).这样将会强制绑定到当前的RedisConnection,它对应的线程会触发MULTI。如果事务执行过程没有错误,EXEC将会被调用。否则DISCARD被调用。一旦在MULTI中,RedisConnection队列化所有写的操作和所有读的操作,例如KEYS将会更新到RedisConnection中。
/** Sample Configuration **/
@Configuration
public class RedisTxContextConfiguration {
@Bean
public StringRedisTemplate redisTemplate() {
StringRedisTemplate template = new StringRedisTemplate(redisConnectionFactory());
// explicitly enable transaction support
template.setEnableTransactionSupport(true);
return template;
}
@Bean
public PlatformTransactionManager transactionManager() throws SQLException {
return new DataSourceTransactionManager(dataSource());
}
@Bean
public RedisConnectionFactory redisConnectionFactory( // jedis, lettuce, srp,... );
@Bean
public DataSource dataSource() throws SQLException { // ... }
}
/** Usage Constrainsts **/
// executed on thread bound connection
template.opsForValue().set("foo", "bar");
// read operation executed on a free (not tx-aware)
connection template.keys("*");
// returns null as values set within transaction are not visible
template.opsForValue().get("foo");
5.11.Pipelining(管道线)
Redis支持pipelining,它涉及发送多个命令到服务器不需要每次等待回复。最后一步读取返回数据。
Pipelining 单行发送多个命令来提高性能,例如添加多个元素到相同的List集合中。
Spring Data Redis提供多个RedisTemplate方法去执行pipeline命令。如果你不关心pipeline的执行结果,你可以使用标准的execute方法,在为pipeline参数传入true。这行executePipelined方法将会提供一个RedisCallback或者SessionCallback回调,然后返回结果,例如:
//pop a specified number of items from a queue
List<Object> results = stringRedisTemplate.executePipelined(
new RedisCallback<Object>() {
public Object doInRedis(RedisConnection connection) throws DataAccessException {
StringRedisConnection stringRedisConn = (StringRedisConnection)connection;
for(int i=0; i< batchSize; i++) {
stringRedisConn.rPop("myqueue");
}
return null;
}
});
以上例子展示的pipeline队列批量左边出栈。这个结果list集合包含所有的出栈选项。在返回之前RedisTemplate使用它值,hash key,和hash value序列化器去返回序列化所有结果,所以在上述案例中返回的是字符串类型。除此之外,executePipelined方法允许你为pipeline结果执行自定义序列号器。
注意 当这个值被遗弃,取而代之的是pipeline命令执行的结果,这个RedisCallback返回值的结果要求为null,
5.12.Redis Scripting(Redis 脚本)
Redis 2.6及以上版本支持Lua脚本,它需要使用的命令是eval和evalsha。Spring Data Redis 为执行脚本提供高级抽象,它可以处理序列化和自动使用Redis脚本缓存。
RedisTemplate的execute方法可以执行脚本。RedisTemplate使用一个配置ScriptExecutor去执行提供的脚本。默认情况下。这个ScriptExecutor关心的序列化提供的keys和参数,以及反序列化脚本的结果。这些工作都是RedisTemplate 的key和value序列化器所在做。有一个额外的execute方法是允许你为脚本参数和结果自定义序列化器。
这个默认的ScriptExecutor通过SHA1加密脚本和尝试第一次去运行evalsha命令来优化性能,如果Redis 脚本缓存不存在,它将会回调eval命令。
这里有一个例子使用Luau脚本来执行一个公共的“check-and-set”场景。这是一个理想使用案例。它要求执行一系列的命令是原子性。一个命令的行为将会被另个结果所影响。
@Bean
public RedisScript<Boolean> script() {
DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<Boolean>();
redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("META-INF/scripts/checkandset.lua")));
redisScript.setResultType(Boolean.class);
}
public class Example {
@Autowired
RedisScript<Boolean> script;
public boolean checkAndSet(String expectedValue, String newValue) {
return redisTemplate.execute(script, Collections.singletonList("key"), expectedValue, newValue);
}
}
-- checkandset.lua local
current = redis.call('GET', KEYS[1])
if current == ARGV[1]
then redis.call('SET', KEYS[1], ARGV[2])
return true
end
return false
以上xml配置一个DefaultRedisScript指向了一个文件名为checkandset.lua,它将期望返回一个boolean值,这个脚本的结果类型应该Long型、Boolean、List、或者反序列化值类型。如果这个脚本返回抛出的状态(例如“OK”),它的返回类型将会为null。一个理想在你的应用上下文配置一个单个实例化DefaultRedisScript对象,在每次脚本执行的时候就可以避免再次计算SHA1。
以上CheckAndSet方法将会在一个SessionCallback作为事务或pipeline一个部分执行脚本,详情参考RedisTransations和Pipelining。
这个脚本执行允许你制定定时任务,关于定时任务参考Spring框架文档。
5.13.Support Classes(支持类)
在包下org.springframework.data.redis.support 提供众多可重复利用的组件,它依赖于Redis作为存储端。当前包下包含众多基于JDK-based接口(实现Redis最顶层,例如原子计数)和JDK集合。
原子性计数使得包装Redis key更加容易,集合将会很容易管理Redis keys,只要使用最小的存储暴露或API接口泄露:在特定RedisSet 和RedisZSet接口提供容易获取set操作,例如交集、并集。当RedisList实现了List,Queue和Deque关联最高Redis,暴露的存储 FIFO(先进先出)LIFO(后进先出)或者固定集合,如下最小配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="queue" class="org.springframework.data.redis.support.collections.DefaultRedisList">
<constructor-arg ref="redisTemplate"/>
<constructor-arg value="queue-key"/>
</bean>
</beans>
public class AnotherExample {
// injected
private Deque<String> queue;
public void addTag(String tag) {
queue.push(tag);
}
}
5.13.1. Support for Spring Cache Abstraction(支持Spring缓存抽象)
Spring Redis 提供为缓存抽象化的实现,在包org.springframework.data.redis.cache下。为了使用Redis 作为一个支持的实现。在你配置文件中简单添加RedisCacheManager:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
<!-- turn on declarative caching -->
<cache:annotation-driven />
<!-- declare Redis Cache Manager -->
<bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager" c:template-ref="redisTemplate"/>
</beans>
默认情况下无论Cache是否必须要,RedisCacheManager将会延迟初始化RedisCache。这是可以通过预定义一个包含名称Set集合来进行改变。
提示2:
默认情况RedisCacheManager不会支持事务,可以通过setTransactionAware去开启事务支持
提示3:
在默认情况下RedisCacheManager不会为缓存区域添加前缀key,它将会导致一个ZSET意外的增长。所以强烈推荐使用为缓存区域提供前缀key来避免意外的增长和潜在key冲突。
提示4:
在默认情况下RedisCache不会缓存一个value 为null的key。然而你可以通过显示确保null值被缓存。也就是RedisCacheManager去存储org.springframework.cache.support.NullValue作为占位符。