Spring Data Redis -----笔记4

继续码字

5.10.Redis Transactions(Redis的事务)

Redis通过命令multi,execdiscard对事务的支持。这些操作命令在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));


在返回之前RedisTemplate将会使用它值、hash键、hash值序列化工具去反序列化exec执行的结果,这个额外的exec执行方法允许你去自定义序列化工具去反序列化结果。


提示:

在版本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脚本,它需要使用的命令是evalevalsha。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);
  }
}


正如上例子所示。实现代码从实际存储中解耦-事实上这没表明任何关于Redis隐含的操作。这使得开发人员从开发到生成环境更加透明迁移,提供可测试性(Redis实现同样可以是内存中的一个替代)

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>


提示1:

默认情况下无论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作为占位符。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值