Redis之坑:spring-data-redis中的Redis事务
Redis之坑:理解Redis事务
Redis之坑:Redis与MySQL中事务的区别
Transaction之坑:数据库事务
Transaction之坑:Spring中配置Transaction与不配置有何区别
Transaction之坑:分析sql执行结果,主动促使事务rollback
Redis
通过multi
, exec
, 或discard
命令来提供事务支持,这些操作在RedisTemplate
中同样是可用的。但是,RedisTemplate
默认使用 RedisCallBack 接口,并不能保证使用同一连接来执行同一事务中的所有操作(此时Transaction
是无效的)。
又但是,Spring Data Redis
提供了 SessionCallback 接口,以便在需要保证同一连接
执行多个操作时使用,比如“需要使用Redis事务
时”。 我们能够看到:
public T execute(SessionCallback session) {
Assert.isTrue(initialized, "template not initialized; call afterPropertiesSet() before using it");
Assert.notNull(session, "Callback object must not be null");
RedisConnectionFactory factory = getConnectionFactory();
// bind connection
RedisConnectionUtils.bindConnection(factory, enableTransactionSupport);//第8行
try {
return session.execute(this);
} finally {
RedisConnectionUtils.unbindConnection(factory);
}
}
RedisTemplate.execute(SessionCallback
方法的session) 第8行
已经做了连接绑定
;
使用方式如下:
//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将使用它的value, hash key和hash value 序列化器来反序列化exec的所有结果。 另外一个额外的exec方法,允许您为事务结果传递自定义序列化器。
上面我们能够看到,可以通过 SessionCallback 绑定连接
,并且实现multi
, exec
,或discard
,从而支持Redis事务
,但是这样就显得很复杂而且Redis操作(opsXXX.X)
执行的位置也变得有局限性(尽管不影响功能)。
然而,Spring
下我们可以更加简单,只需两步:
method
添加注解**@Transactional或者Xml配置**(< tx:method />),注册事务切点。相当于调用了 TransactionSynchronizationManager . setActualTransactionActive ( true );RedisTemplate
实例的事务支持
(默认被禁用)/** Sample Configuration **/
@Configuration
public class RedisTxContextConfiguration {
@Bean
public StringRedisTemplate redisTemplate() {
StringRedisTemplate template = new StringRedisTemplate(redisConnectionFactory());
// explicitly enable transaction support
template.setEnableTransactionSupport(true);
return template;
}
}
redisTemplate实例 默认调用 execute ( RedisCallback action ),方法内容如下:
public
<T> T
execute
(RedisCallback<T> action,
boolean
exposeConnection,
boolean
pipeline){
/**
* 变量声明等操作……
*/
try {
if (enableTransactionSupport) {
//
only
bind
resources
in
case
of
potential
transaction
synchronization
conn = RedisConnectionUtils.
bindConnection
(factory, enableTransactionSupport);
}
else
{
conn = RedisConnectionUtils.
getConnection
(factory);
}
/**
* 其他操作……
*/
}
public
static
RedisConnection
bindConnection
(RedisConnectionFactory factory,
boolean
enableTransactionSupport) {
/**
* 不用管……
*/
RedisConnection conn = factory.
getConnection
();
RedisConnection connectionToBind = conn;
//redisTemplate开启事务支持,同时transactionManager非只读的实际事务被激活
if (enableTransactionSupport &&
isActualNonReadonlyTransactionActive
()) {
connectionToBind =
createConnectionProxy
(conn, factory);
}
/**
* 不用管……
*/
return
conn;
}
可以看到, enableTransactionSupport = true 将会促使当前Thread
尝试绑定RedisConnection
,仅当也 isActualNonReadonlyTransactionActive = true ,连接才会成功绑定。
连接绑定成功,同时将会触发MULTI
。一旦MULTI
被调用:
RedisConnection
将会排队write操作
;readonly操作
,例如KEYS
将会被分发给一个全新的 (非Thread
绑定)的RedisConnection
;EXEC
或DISCARD
将交由 SpringAOP 的动态代理对象去调用:事务构建
过程中没有异常抛出
(默认RuntimeException
及其子类),则EXEC
被调用,执行命令队列;DISCARD
,清除命令队列。开启事务支持后:
/** 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");
上面的样例代码是Spring官网给出的,第三个显然是WATCH
命令开启乐观锁
后的结果。然而至少在本人正在使用的 spring - data - redis -1.8.10. RELEASE . jar 中,
<
dependency
>
<
groupId
>org.springframework.data
groupId
>
<
artifactId
>spring-data-redis
artifactId
>
<
version
>1.8.10.RELEASE
version
>
dependency
>
WATCH
命令并没有被使用,亲测第三种
效果并不存在(你可以根据自己的依赖版本尝试一下),此处亮出代码。
private
static
void
potentiallyRegisterTransactionSynchronisation
(RedisConnectionHolder connHolder,
final
RedisConnectionFactory factory) {
if (
isActualNonReadonlyTransactionActive
()) {
if (!connHolder.
isTransactionSyncronisationActive
()) {
connHolder.
setTransactionSyncronisationActive
(
true
);
RedisConnection conn = connHolder.
getConnection
();
conn.
multi
();//在此之前conn.watch()未被调用
TransactionSynchronizationManager.
registerSynchronization
(new
RedisTransactionSynchronizer
(connHolder, conn,
factory));
}
}
}
两个
RedisTemplate实例两个RedisTemplate
实例?
commands
要么统一执行
,要么都被清除
,维护数据完整性;command
立即执行,即时返回执行结果
并且更高效
;/**
Sample
Configuration
**/
@Configuration
public
class
RedisTxContextConfiguration
{
@Bean
public
StringRedisTemplate
redisTransactionTemplate
() {
StringRedisTemplate template = new
StringRedisTemplate
(
redisConnectionFactory
());
//
explicitly
enable
transaction
support
template.
setEnableTransactionSupport
(
true
);
return
template;
}
@Bean
public
StringRedisTemplate
redisTemplate
() {
StringRedisTemplate template = new
StringRedisTemplate
(
redisConnectionFactory
());
return
template;
}
}