分析“備忘使用spring-data-redis中的redistemplate的一個大坑”
前幾天剛剛粗略看了一下spring-data-redis的原始碼 (1.0.1-RELEASE)
又針對這部分分析了下原始碼,總結整理如下
spring-data-redis的各種Operations實現類,如RedisTemplate,DefaultSetOperations,DefaultListOperations等,對Redis命令的封閉都是通過如下結構呼叫的
RedisTemplate中的hasKey
Java程式碼- public Boolean hasKey(K key) {
-
final byte[] rawKey = rawKey(key);
- return execute(new RedisCallback<Boolean>() {
- public Boolean doInRedis(RedisConnection connection) {
- return connection.exists(rawKey);
- }
- }, true);
- }
要實現一個指定返回型別的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程式碼-
public
- Assert.notNull(action, "Callback object must not be null");
- RedisConnectionFactory factory = getConnectionFactory();
- RedisConnection conn = RedisConnectionUtils.getConnection(factory);
- boolean existingConnection = TransactionSynchronizationManager.hasResource(factory);
- preProcessConnection(conn, existingConnection);
- boolean pipelineStatus = conn.isPipelined();
- if (pipeline && !pipelineStatus) {
- conn.openPipeline();
- }
- try {
- RedisConnection connToExpose = (exposeConnection ? conn : createRedisConnectionProxy(conn));
- T result = action.doInRedis(connToExpose);
- // close pipeline
- if (pipeline && !pipelineStatus) {
- conn.closePipeline();
- }
- // TODO: any other connection processing?
- return postProcessResult(result, conn, existingConnection);
- } finally {
- RedisConnectionUtils.releaseConnection(conn, factory);
- }
- }
正常情況下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程式碼- public void rename(final String newKey) {
- CollectionUtils.rename(key, newKey, operations);
- key = newKey;
- }
但它的實現並沒有使用內部持有的RedisOperations,而是CollectionUtils.rename,那我們就來看一個這個CollectionUtils.rename
這個rename是通過事務來保證進行rename操作時,原key一定存在
Java程式碼- static <K> void rename(final K key, final K newKey, RedisOperations<K, ?> operations) {
- operations.execute(new SessionCallback<Object>() {
- @SuppressWarnings("unchecked")
- public Object execute(RedisOperations operations) throws DataAccessException {
- do {
- operations.watch(key);
- if (operations.hasKey(key)) {
- operations.multi();
- operations.rename(key, newKey);
- }
- else {
- operations.multi();
- }
- } while (operations.exec() == null);
- return null;
- }
- });
- }
這是一個靜態函式,對Redis的操作是通過傳入的RedisOperations來完成的
rename呼叫了RedisOperations的execute,傳入了一個匿名的回撥介面,實現具體操作,這和前面說的execute模板看起來是一樣的。但這裡的RedisOperations為什麼可以使用watch 和 multi 能? 前面不是說 RedisTemplate 不能直接watch 和 multi 嗎? (RedisOperations是RedisTemplate的介面,針對不同資料結構的操作類和集合都是從RedisTemplate生成的,也就是說都是通過RedisTemplate操作Redis的)
其實這個execute模板和前面的是不一樣的,這裡的引數是SessionCallback 而前面的是 RedisCallback,從名字應該能看出,這個裡的 execute 呼叫是會保持會話,也就是連線。
看一下這裡的execute原始碼
Java程式碼- public <T> T execute(SessionCallback<T> session) {
- RedisConnectionFactory factory = getConnectionFactory();
- // bind connection
- RedisConnectionUtils.bindConnection(factory);
- try {
- return session.execute(this);
- } finally {
- RedisConnectionUtils.unbindConnection(factory);
- }
- }
不用多解釋了吧,在呼叫 SessionCallback 的實現進行具體操作前後,對連線進行了繫結和解綁。
然後在session.execute中,會呼叫operations.watch(key); 等著操作,這些操作和前面分析的RedisTemplate中的hasKey的流程是一樣的,會呼叫前面分析的execute進行操作。
回到前面看下原始碼,那裡的RedisConnection 是通過RedisConnectionUtils取到的
Java程式碼- 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-redis中的redistemplate的一個大坑”
前幾天剛剛粗略看了一下spring-data-redis的原始碼 (1.0.1-RELEASE) 又針對這部分分析了下原始碼,總結整理如下 spring-data-redis的各種Operations實現類,如RedisTemplate,DefaultSetOperations,Defaul
備忘使用spring-data-redis中的redistemplate的一個大坑
在專案開發過程中,想要進行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 Redis的RedisTemplate的坑
自己搭的一個分散式系統,使用的是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-Redis之RedisTemplate的使用
上篇部落格是Spring-Data-Redis的例項,接著上篇的內容,這篇部落格介紹一下RedisTemplate的詳細方法。 功能介紹 大部分的使用者都喜歡用RedisTemplate,它相應的包是org.springframework
Spring中使用RedisTemplate操作Redis(spring-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