现象
使用get请求,incrment请求,返回结果一直为null
业务代码
org.springframework.transaction.support.TransactionTemplate
transactionTemplate.execute(t -> {
...
// 在事务里进行了redis的操作,且需要获取数据
Long incr = redisTemplate.opsForValue().increment(key,1L);
});
输出结果:incr为null。
原因
redis操作在事务之间的时候,全部返回null
源码解析
- 执行redis操作的入口:
org.springframework.data.redis.core.RedisTemplate.class
public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) {
Assert.isTrue(initialized, "template not initialized; call afterPropertiesSet() before using it");
Assert.notNull(action, "Callback object must not be null");
RedisConnectionFactory factory = getConnectionFactory();
RedisConnection conn = null;
try {
// 如果配置支持事务,然后就会执行下面的方法,我的配置文件是支持事务的
if (enableTransactionSupport) {
// only bind resources in case of potential transaction synchronization
conn = RedisConnectionUtils.bindConnection(factory, enableTransactionSupport);
} else {
conn = RedisConnectionUtils.getConnection(factory);
}
boolean existingConnection = TransactionSynchronizationManager.hasResource(factory);
RedisConnection connToUse = preProcessConnection(conn, existingConnection);
boolean pipelineStatus = connToUse.isPipelined();
if (pipeline && !pipelineStatus) {
connToUse.openPipeline();
}
RedisConnection connToExpose = (exposeConnection ? connToUse : createRedisConnectionProxy(connToUse));
T result = action.doInRedis(connToExpose);
// close pipeline
if (pipeline && !pipelineStatus) {
connToUse.closePipeline();
}
// TODO: any other connection processing?
return postProcessResult(result, connToUse, existingConnection);
} finally {
RedisConnectionUtils.releaseConnection(conn, factory);
}
}
- RedisConnectionUtils.bindConnection
public static RedisConnection bindConnection(RedisConnectionFactory factory, boolean enableTranactionSupport) {
return doGetConnection(factory, true, true, enableTranactionSupport);
}
// 获取connect连接
public static RedisConnection doGetConnection(RedisConnectionFactory factory, boolean allowCreate, boolean bind,
boolean enableTransactionSupport) {
Assert.notNull(factory, "No RedisConnectionFactory specified");
RedisConnectionHolder connHolder = (RedisConnectionHolder) TransactionSynchronizationManager.getResource(factory);
if (connHolder != null) {
if (enableTransactionSupport) {
// 可能会进行注册事务同步,主要设置redis事务开启的地方就在这里
potentiallyRegisterTransactionSynchronisation(connHolder, factory);
}
return connHolder.getConnection();
}
if (!allowCreate) {
throw new IllegalArgumentException("No connection found and allowCreate = false");
}
if (log.isDebugEnabled()) {
log.debug("Opening RedisConnection");
}
RedisConnection conn = factory.getConnection();
if (bind) {
RedisConnection connectionToBind = conn;
if (enableTransactionSupport && isActualNonReadonlyTransactionActive()) {
connectionToBind = createConnectionProxy(conn, factory);
}
connHolder = new RedisConnectionHolder(connectionToBind);
TransactionSynchronizationManager.bindResource(factory, connHolder);
if (enableTransactionSupport) {
potentiallyRegisterTransactionSynchronisation(connHolder, factory);
}
return connHolder.getConnection();
}
return conn;
}
// 可能会进行注册事务同步
private static void potentiallyRegisterTransactionSynchronisation(RedisConnectionHolder connHolder,
final RedisConnectionFactory factory) {
// 如果当前事务已开启
if (isActualNonReadonlyTransactionActive()) {
// 当前redis的事务同步还未开始
if (!connHolder.isTransactionSyncronisationActive()) {
// 启动redis事务
connHolder.setTransactionSyncronisationActive(true);
RedisConnection conn = connHolder.getConnection();
// 主要有个操作是给redis的client的isInMulti设置为true
conn.multi();
TransactionSynchronizationManager.registerSynchronization(new RedisTransactionSynchronizer(connHolder, conn,
factory));
}
}
}
- RedisTemplate
// 执行redis操作
T result = action.doInRedis(connToExpose);
- JedisConnection
public byte[] get(byte[] key) {
try {
if (isPipelined()) {
pipeline(new JedisResult(pipeline.get(key)));
return null;
}
// 当事务开启的时候,isQueueing的返回结果为true,所以get的操作会返回null
if (isQueueing()) {
transaction(new JedisResult(transaction.get(key)));
return null;
}
return jedis.get(key);
} catch (Exception ex) {
throw convertJedisAccessException(ex);
}
}
public Long incr(byte[] key) {
try {
if (isPipelined()) {
pipeline(new JedisResult(pipeline.incr(key)));
return null;
}
// 当事务开启的时候,isQueueing的返回结果为true,所以incr的操作会返回null
if (isQueueing()) {
transaction(new JedisResult(transaction.incr(key)));
return null;
}
return jedis.incr(key);
} catch (Exception ex) {
throw convertJedisAccessException(ex);
}
}
总结
使用redis事务需谨慎,可以使用,但是在使用的过程中获取数据都是获取不到的