1. 程式人生 > >分析“備忘使用spring-data-redis中的redistemplate的一個大坑”

分析“備忘使用spring-data-redis中的redistemplate的一個大坑”

前幾天剛剛粗略看了一下spring-data-redis的原始碼 (1.0.1-RELEASE)

又針對這部分分析了下原始碼,總結整理如下

spring-data-redis的各種Operations實現類,如RedisTemplate,DefaultSetOperations,DefaultListOperations等,對Redis命令的封閉都是通過如下結構呼叫的

RedisTemplate中的hasKey

Java程式碼  收藏程式碼
  1. public Boolean hasKey(K key) {  
  2.     final byte[] rawKey = rawKey(key);  
  3.     return execute(new RedisCallback<Boolean>() {  
  4.         public Boolean doInRedis(RedisConnection connection) {  
  5.             return connection.exists(rawKey);  
  6.         }  
  7.     }, true);  
  8. }  

要實現一個指定返回型別的RedisCallback介面,這個介面只有一個函式doInRedis

doInRedis的引數是RedisConnection介面,spring-data-redis針對不的Redis Java Client都有對應的 RedisConnection實現:

JedisConnection

JredisConnection

RjcConnection

SrpConnection

目的就是將不同Client的API統一了一下,可以看成是Adapter吧 。

execute這個是模板函式,主是處理連線的問題。

spring-data-redis 針對不的 Redis Java Client 也都實現了相應的 RedisConnectionFactory,來獲取連線。  

比如 JedisConnectionFactory 內部是通過 JedisPool 來實現連線工廠。

execute模板函式原始碼:

Java程式碼  收藏程式碼
  1. public
     <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) {  
  2.     Assert.notNull(action, "Callback object must not be null");  
  3.     RedisConnectionFactory factory = getConnectionFactory();  
  4.     RedisConnection conn = RedisConnectionUtils.getConnection(factory);  
  5.     boolean existingConnection = TransactionSynchronizationManager.hasResource(factory);  
  6.     preProcessConnection(conn, existingConnection);  
  7.     boolean pipelineStatus = conn.isPipelined();  
  8.     if (pipeline && !pipelineStatus) {  
  9.         conn.openPipeline();  
  10.     }  
  11.     try {  
  12.         RedisConnection connToExpose = (exposeConnection ? conn : createRedisConnectionProxy(conn));  
  13.         T result = action.doInRedis(connToExpose);  
  14.         // close pipeline  
  15.         if (pipeline && !pipelineStatus) {  
  16.             conn.closePipeline();  
  17.         }  
  18.         // TODO: any other connection processing?  
  19.         return postProcessResult(result, conn, existingConnection);  
  20.     } finally {  
  21.         RedisConnectionUtils.releaseConnection(conn, factory);  
  22.     }  
  23. }  

正常情況下RedisCallback中doInRedis獲取的RedisConnection每次都是新的

這也就出現了前面帖子中說的問題了“這時候肯定會有個疑問,既然這個template每次都會生成新連線,那這個multi和exec命令還有個蛋用??

使用RedisTemplate確實會有這個問題,但是作者對官方回答的理解有些出入。

The methods are exposed in case the connection is shared across methods. Currently we don't provide any out of the box support for connection binding but the RedisTemplate supports it - just like with the rest of the templates, one connection could be bound to the running thread and the RT will use it without creating new ones. 

回答中確實承認RedisTemplate 不支援連線繫結,但後半段說的是“在其它的模板類中,連線是可以繫結到當前執行緒,這樣RedisTemplate 就會使用這個連線,而不會重新建立了”。

OK! 下面分析原始碼,看一下這個繫結是怎麼實現的。

org.springframework.data.redis.support.collections.AbstractRedisCollection 是 spring-data-redis 對redis的幾個集合型別的資料結構封裝類的抽象類


其中有 rename 這個函式

Java程式碼  收藏程式碼
  1. public void rename(final String newKey) {  
  2.     CollectionUtils.rename(key, newKey, operations);  
  3.     key = newKey;  
  4. }  

但它的實現並沒有使用內部持有的RedisOperations,而是CollectionUtils.rename,那我們就來看一個這個CollectionUtils.rename

這個rename是通過事務來保證進行rename操作時,原key一定存在

Java程式碼  收藏程式碼
  1. static <K> void rename(final K key, final K newKey, RedisOperations<K, ?> operations) {  
  2.     operations.execute(new SessionCallback<Object>() {  
  3.         @SuppressWarnings("unchecked")  
  4.         public Object execute(RedisOperations operations) throws DataAccessException {  
  5.             do {  
  6.                 operations.watch(key);  
  7.                 if (operations.hasKey(key)) {  
  8.                     operations.multi();  
  9.                     operations.rename(key, newKey);  
  10.                 }  
  11.                 else {  
  12.                     operations.multi();  
  13.                 }  
  14.             } while (operations.exec() == null);  
  15.             return null;  
  16.         }  
  17.     });  
  18. }  

 這是一個靜態函式,對Redis的操作是通過傳入的RedisOperations來完成的

rename呼叫了RedisOperations的execute,傳入了一個匿名的回撥介面,實現具體操作,這和前面說的execute模板看起來是一樣的。但這裡的RedisOperations為什麼可以使用watch 和 multi 能? 前面不是說 RedisTemplate 不能直接watch 和 multi 嗎?  (RedisOperations是RedisTemplate的介面,針對不同資料結構的操作類和集合都是從RedisTemplate生成的,也就是說都是通過RedisTemplate操作Redis的)

其實這個execute模板和前面的是不一樣的,這裡的引數是SessionCallback 而前面的是 RedisCallback,從名字應該能看出,這個裡的 execute 呼叫是會保持會話,也就是連線。

看一下這裡的execute原始碼

Java程式碼  收藏程式碼
  1. public <T> T execute(SessionCallback<T> session) {  
  2.     RedisConnectionFactory factory = getConnectionFactory();  
  3.     // bind connection  
  4.     RedisConnectionUtils.bindConnection(factory);  
  5.     try {  
  6.         return session.execute(this);  
  7.     } finally {  
  8.         RedisConnectionUtils.unbindConnection(factory);  
  9.     }  
  10. }  
 

不用多解釋了吧,在呼叫 SessionCallback 的實現進行具體操作前後,對連線進行了繫結和解綁。

然後在session.execute中,會呼叫operations.watch(key); 等著操作,這些操作和前面分析的RedisTemplate中的hasKey的流程是一樣的,會呼叫前面分析的execute進行操作。

回到前面看下原始碼,那裡的RedisConnection 是通過RedisConnectionUtils取到的

Java程式碼  收藏程式碼
  1. RedisConnection conn = RedisConnectionUtils.getConnection(factory);  

連線繫結和解綁也是通過 RedisConnectionUtils 完成的,裡面是通過TransactionSynchronizationManager將連線繫結到當前執行緒的,和spring的DB事務管理是一樣的,這裡就不詳細分析了(主要是使用ThreadLocal)。

這樣一分析,spring-data-redis 是可以使用watch 和 multi 的,關鍵是怎麼使用的問題。 

總結:

RedisTemplate和其它特定型別的操作類,主要是實現了基本的操作功能,也有部分高階功能(如上面的rename和RedisAtomicInteger等高階操作)。

通過 SessionCallback 是可以獲取到繫結連線的操作類的,它上面的操作都是在一個連線上的,這樣可以實現高階功能。

在spring-data-redis中使用watch和multi,可以參照原始碼中的CollectionUtils.rename

相關推薦

分析使用spring-data-redisredistemplate一個大坑

前幾天剛剛粗略看了一下spring-data-redis的原始碼 (1.0.1-RELEASE) 又針對這部分分析了下原始碼,總結整理如下 spring-data-redis的各種Operations實現類,如RedisTemplate,DefaultSetOperations,Defaul

使用spring-data-redisredistemplate一個大坑

在專案開發過程中,想要進行redis的併發控制,這時候,想當然地使用了spring-data-redis庫中template裡面提供的multi()和exec()方法,但是蛋疼地發現,使用了之後,就出現瞭如下異常:org.springframework.dao.Invalid

redis的scan命令代替keys命令,以及在spring-data-redis遇到的問題

有一個 arr 問題 public 條件 列表 position cannot clas 摘要 本文主要是介紹使用redis scan命令遇到的一些問題總結,scan命令本身沒有什麽問題,主要是spring-data-redis的問題。 需求 需要遍歷redis中key,找

Spring Data RedisRedisTemplate的坑

自己搭的一個分散式系統,使用的是springboot2.0.3+springcloud Finchley 當時在user服務中存入一個值,然後在api服務中使用相同的key取的時候發現取不出來,然後各種排除: 1 檢查api服務和user服務中的redis

spring-data-redis使用RedisTemplate模板儲存時鍵值與預設不一致的解決方法

一、背景 最近使用spring-data-redis 和jedis 操作redis時發現儲存在redis中的key不是程式中設定的string值,前面還多出了許多類似\xac\xed\x00\x05t

Spring-Data-RedisRedisTemplate的使用

        上篇部落格是Spring-Data-Redis的例項,接著上篇的內容,這篇部落格介紹一下RedisTemplate的詳細方法。 功能介紹         大部分的使用者都喜歡用RedisTemplate,它相應的包是org.springframework

Spring使用RedisTemplate操作Redisspring-data-redis

Redis 資料結構簡介 Redis可以儲存鍵與5種不同資料結構型別之間的對映,這5種資料結構型別分別為String(字串)、List(列表)、Set(集合)、Hash(雜湊)和 Zset(有序集合)。 下面來對這5種資料結構型別作簡單的介紹: 結構型別 結構儲存

Spring Data Redis入門示例:基於RedisTemplate (三)

gem per 例子 基於 接口 image 安全 redist 工作 使用底層API:RedisConnection操作Redis,需要對數據進行手動轉換(String <---->byte),需要進行多數重復性工作,效率低下;org.springframew

Spring Data Redis 2.x RedisConfiguration 類的新編寫方法

redis 分享 name pub 名稱 per localhost 端口號 vat 在 Spring Data Redis 1.x 的時候,我們可能會在項目中編寫這樣一個RedisConfig類: @Configuration @EnableCaching public

spring-data-redis 使用過程需要注意的一點(序列化選擇)

在專案中需要用到redis做快取,於是採用spring-data-redis,並且打算自己封裝一個redis的靜態工具類。後來在進行單元測試的過程中發現了一個容易出錯的地方,於是打算記錄下來,並分享給各位朋友。 這裡主要說下碰到的問題,首先,採用了spring-

Spring Data Redis 二:RedisTemplate實現事物問題剖析和解決

三、解決方案  只能自己實現RedisCallBack底層,採用RedisTemplate的SesionCallback來完成在同一個Connection中,完成多個操作的方法: SessionCallback<Object> sessionCallback=new SessionCa

Spring Data Redis框架常使用的方法大全

1、Spring Data Redis框架中常使用的方法大全。 stringRedisTemplate.opsForValue().set("test", "100",60*10,TimeUnit.SECONDS);//向redis裡存入資料和設定快取時間 stringRedisTemplate

Spring Data Redis框架opsForValue()方法的使用

1、Spring Data Redis框架中opsForValue()方法的使用。 /** * 根據ID查詢實體 * @param id * @return */ public Article findById(String id) { //從快取中,查詢當前物件 A

Spring Data Redis整合Redis流程原始碼分析

一:版本資訊如下.     1.1 SpringBoot的版本:1.5.10.RELEASE. <parent> <groupId>org.springframework.boot</groupId> <art

spring data jpa使用redis的通用list及entity儲存方法

/** * 從redis中獲取物件。注意:未進行haskey檢測 * * @param e * @param redis * @param KEY * @param KEY_LIST * @param

Spring-Data-Redis叢集配置和RedisTemplate用法

使用SpringData更加方便我們對關係型資料庫和非關係型資料庫更好的操作,封裝了通用的程式碼,使得操作更加快捷簡單。 一、使用Spring-data-redis Jar包準備 spring-data-redis 需要在1.7 版本以上,他會依賴一些包,

Spring Data Redis簡介以及專案Demo,RedisTemplate和 Serializer詳解

一、概念簡介: Redis:   Redis是一款開源的Key-Value資料庫,執行在記憶體中,由ANSI C編寫,詳細的資訊在Redis官網上面有,因為我自己通過google等各種渠道去學習Redis,走了不少彎路,所以總結一條我認為不錯的學習路徑給大

spring-data-redis 使用過程踩過的坑

spring-data-redis簡介 Spring-data-redis是spring大家族的一部分,提供了在srping應用中通過簡單的配置訪問redis服務,對reids底層開發包(Jedis, JRedis, and RJC)進行了高度封裝,RedisTemplate提供了redis各種操作、異常處

Spring整合Redis(spring-data-redis)

nds 獲取 可能 div 普通 工具 long red 等待 歷經幾天看了大量的博客資料,差不多算是搞定了,目前只是針對單個數據源,集群暫時沒研究 maven依賴 <properties> <!-- redis 版本 --> &l

Spring集成Redis方案(spring-data-redis)(基於Jedis的單機模式)(待實踐)

packaging 基於 .cn @override time Coding 很好 -o -i 繼上一篇文章http://www.cnblogs.com/EasonJim/p/7625738.html中提到的幾款客戶端,它們基本都能和Spring集成。 下面介紹的是基於S