redis使用事務-踩坑
阿新 • • 發佈:2018-12-19
現象
使用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事務需謹慎,可以使用,但是在使用的過程中獲取資料都是獲取不到的