1. 初始 Redis
1.1 認識NoSQL
NoSql可以翻譯做Not Only Sql(不僅僅是SQL),或者是No Sql(非Sql的)資料庫。是
相對於傳統關係型資料庫而言,有很大差異的一種特殊的資料庫
,因此也稱之為非關係型資料庫。
1.1.1 結構化與非結構化
1.1.2 關係型與非關係型的差異
-
儲存方式
- 關係型資料庫基於磁碟進行儲存,會有大量的磁碟IO,對效能有一定影響
- 非關係型資料庫,他們的操作更多的是依賴於記憶體來操作,記憶體的讀寫速度會非常快,效能自然會好一些
-
擴充套件性
- 關係型資料庫叢集模式一般是主從,主從資料一致,起到資料備份的作用,稱為垂直擴充套件。
- 非關係型資料庫可以將資料拆分,儲存在不同機器上,可以儲存海量資料,解決記憶體大小有限的問題。稱為水平擴充套件。
- 關係型資料庫因為表之間存在關聯關係,如果做水平擴充套件會給資料查詢帶來很多麻煩
1.2 認識 Redis
Redis誕生於2009年全稱是Remote Dictionary Server 遠端詞典伺服器,是一個基於記憶體的鍵值型NoSQL資料庫。
特徵:
- 鍵值(key-value)型,value支援多種不同資料結構,功能豐富
- 單執行緒,每個命令具備原子性
- 低延遲,速度快(基於記憶體.IO多路複用.良好的編碼)。
- 支援資料持久化
- 支援主從叢集.分片叢集
- 支援多語言客戶端
作者:Antirez
Redis的官方網站地址:https://redis.io/
1.3 安裝Redis
Redis的安裝步驟
2. Redis 的常見命令
2.1 Redis 資料結構介紹
Redis是一個key-value的資料庫,key一般是String型別,不過value的型別多種多樣:
貼心小建議:命令不要死記,學會查詢就好啦
Redis為了方便我們學習,將操作不同資料型別的命令也做了分組,在官網( https://redis.io/commands )可以檢視到不同的命令:
當然我們也可以透過Help命令來幫助我們去檢視命令
2.2 Redis 通用命令
通用指令是部分資料型別的,都可以使用的指令,常見的有:
- KEYS:檢視符合模板的所有key
- DEL:刪除一個指定的key
- EXISTS:判斷key是否存在
- EXPIRE:給一個key設定有效期,有效期到期時該key會被自動刪除
- TTL:檢視一個KEY的剩餘有效期
透過help [command] 可以檢視一個命令的具體用法,例如:
例項程式碼如下:
- KEYS
127.0.0.1:6379> keys *
1) "name"
2) "age"
127.0.0.1:6379>
# 查詢以a開頭的key
127.0.0.1:6379> keys a*
1) "age"
127.0.0.1:6379>
貼心小提示:在生產環境下,不推薦使用keys 命令,因為這個命令在key過多的情況下,效率不高
- DEL
127.0.0.1:6379> help del
DEL key [key ...]
summary: Delete a key
since: 1.0.0
group: generic
127.0.0.1:6379> del name #刪除單個
(integer) 1 #成功刪除1個
127.0.0.1:6379> keys *
1) "age"
127.0.0.1:6379> MSET k1 v1 k2 v2 k3 v3 #批次新增資料
OK
127.0.0.1:6379> keys *
1) "k3"
2) "k2"
3) "k1"
4) "age"
127.0.0.1:6379> del k1 k2 k3 k4
(integer) 3 #此處返回的是成功刪除的key,由於redis中只有k1,k2,k3 所以只成功刪除3個,最終返回
127.0.0.1:6379>
127.0.0.1:6379> keys * #再查詢全部的key
1) "age" #只剩下一個了
127.0.0.1:6379>
貼心小提示:同學們在複製程式碼的時候,只需要複製對應的命令哦~
- EXISTS
127.0.0.1:6379> help EXISTS
EXISTS key [key ...]
summary: Determine if a key exists
since: 1.0.0
group: generic
127.0.0.1:6379> exists age
(integer) 1
127.0.0.1:6379> exists name
(integer) 0
- EXPIRE
貼心小提示:記憶體非常寶貴,對於一些資料,我們應當給他一些過期時間,當過期時間到了之後,他就會自動被刪除~
127.0.0.1:6379> expire age 10
(integer) 1
127.0.0.1:6379> ttl age
(integer) 8
127.0.0.1:6379> ttl age
(integer) 6
127.0.0.1:6379> ttl age
(integer) -2
127.0.0.1:6379> ttl age
(integer) -2 #當這個key過期了,那麼此時查詢出來就是-2
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> set age 10 #如果沒有設定過期時間
OK
127.0.0.1:6379> ttl age
(integer) -1 # ttl的返回值就是-1
2.3 Redis命令-String命令
String型別,也就是字串型別,是Redis中最簡單的儲存型別。
其value是字串,不過根據字串的格式不同,又可以分為3類:
- string:普通字串
- int:整數型別,可以做自增.自減操作
- float:浮點型別,可以做自增.自減操作
String的常見命令有:
- SET:新增或者修改已經存在的一個String型別的鍵值對
- GET:根據key獲取String型別的value
- MSET:批次新增多個String型別的鍵值對
- MGET:根據多個key獲取多個String型別的value
- INCR:讓一個整型的key自增1
- INCRBY:讓一個整型的key自增並指定步長,例如:incrby num 2 讓num值自增2
- INCRBYFLOAT:讓一個浮點型別的數字自增並指定步長
- SETNX:新增一個String型別的鍵值對,前提是這個key不存在,否則不執行
- SETEX:新增一個String型別的鍵值對,並且指定有效期
貼心小提示:以上命令除了INCRBYFLOAT 都是常用命令
-
SET 和GET: 如果key不存在則是新增,如果存在則是修改
-
MSET和MGET : 可以批次增加 , 檢視
-
INCR和INCRBY和DECY
-
SETNX : 新增一個String型別的鍵值對,前提是這個key不存在,否則不執行
-
SETEX : 新增一個String型別的鍵值對,並且指定有效期
2.4 Redis命令-Key的層級結構
Redis沒有類似MySQL中的Table的概念
,我們該如何區分不同型別的key呢?
例如,需要儲存使用者.商品資訊到redis,有一個使用者id是1,有一個商品id恰好也是1,此時如果使用id作為key,那就會衝突了,該怎麼辦?
我們可以透過給key新增字首加以區分,不過這個字首不是隨便加的,有一定的規範:
Redis的key允許有多個單詞形成層級結構,多個單詞之間用':'隔開,格式如下:
專案名:業務名:id
這個格式並非固定,也可以根據自己的需求來刪除或新增詞條。
例如我們的專案名稱叫 heima,有user和product兩種不同型別的資料,我們可以這樣定義key:
- user相關的key:heima:user:1
- product相關的key:heima:product:1
如果Value是一個Java物件,例如一個User物件,則可以將物件序列化為JSON字串後儲存:
KEY | VALUE |
---|---|
heima:user:1 | |
heima:product:1 |
一旦我們向redis採用這樣的方式儲存,那麼在視覺化介面中,redis會以層級結構來進行儲存,形成類似於這樣的結構,更加方便Redis獲取資料
2.5 Redis命令-Hash命令
Hash型別,也叫雜湊,其value是一個無序字典,類似於Java中的HashMap結構。
String結構是將物件序列化為JSON字串後儲存,當需要修改物件某個欄位時很不方便:
Hash結構可以將物件中的每個欄位獨立儲存,可以針對單個欄位做CRUD:
Hash型別的常見命令
- HSET key field value:新增或者修改hash型別key的field的值
- HGET key field:獲取一個hash型別key的field的值
- HMSET:批次新增多個hash型別key的field的值
- HMGET:批次獲取多個hash型別key的field的值
- HGETALL:獲取一個hash型別的key中的所有的field和value
- HKEYS:獲取一個hash型別的key中的所有的field
- HINCRBY:讓一個hash型別key的欄位值自增並指定步長
- HSETNX:新增一個hash型別的key的field值,前提是這個field不存在,否則不執行
貼心小提示:雜湊結構也是我們以後實際開發中常用的命令喲
- HSET和HGET
127.0.0.1:6379> HSET heima:user:3 name Lucy//大key是 heima:user:3 小key是name,小value是Lucy
(integer) 1
127.0.0.1:6379> HSET heima:user:3 age 21// 如果操作不存在的資料,則是新增
(integer) 1
127.0.0.1:6379> HSET heima:user:3 age 17 //如果操作存在的資料,則是修改
(integer) 0
127.0.0.1:6379> HGET heima:user:3 name
"Lucy"
127.0.0.1:6379> HGET heima:user:3 age
"17"
- HMSET和HMGET
127.0.0.1:6379> HMSET heima:user:4 name HanMeiMei
OK
127.0.0.1:6379> HMSET heima:user:4 name LiLei age 20 sex man
OK
127.0.0.1:6379> HMGET heima:user:4 name age sex
1) "LiLei"
2) "20"
3) "man"
- HGETALL
127.0.0.1:6379> HGETALL heima:user:4
1) "name"
2) "LiLei"
3) "age"
4) "20"
5) "sex"
6) "man"
- HKEYS和HVALS
127.0.0.1:6379> HKEYS heima:user:4
1) "name"
2) "age"
3) "sex"
127.0.0.1:6379> HVALS heima:user:4
1) "LiLei"
2) "20"
3) "man"
- HINCRBY
127.0.0.1:6379> HINCRBY heima:user:4 age 2
(integer) 22
127.0.0.1:6379> HVALS heima:user:4
1) "LiLei"
2) "22"
3) "man"
127.0.0.1:6379> HINCRBY heima:user:4 age -2
(integer) 20
- HSETNX
127.0.0.1:6379> HSETNX heima:user4 sex woman
(integer) 1
127.0.0.1:6379> HGETALL heima:user:3
1) "name"
2) "Lucy"
3) "age"
4) "17"
127.0.0.1:6379> HSETNX heima:user:3 sex woman
(integer) 1
127.0.0.1:6379> HGETALL heima:user:3
1) "name"
2) "Lucy"
3) "age"
4) "17"
5) "sex"
6) "woman"
2.6 Redis命令-List命令
redis中的List型別與Java中的LinkedList類似,可以看做是一個雙向連結串列結構。既可以支援正向檢索和也可以支援反向檢索。
特徵也與LinkedList類似:
- 有序
- 元素可以重複
- 插入和刪除快
- 查詢速度一般
常用來儲存一個有序資料,例如:朋友圈點贊列表,評論列表等。
List的常見命令有:
- LPUSH key element ... :向列表左側插入一個或多個元素
- LPOP key:移除並返回列表左側的第一個元素,沒有則返回nil
- RPUSH key element ... :向列表右側插入一個或多個元素
- RPOP key:移除並返回列表右側的第一個元素
- LRANGE key star end:返回一段角標範圍內的所有元素
- BLPOP和BRPOP:與LPOP和RPOP類似,只不過在沒有元素時等待指定時間,而不是直接返回nil
- LPUSH和RPUSH
127.0.0.1:6379> LPUSH users 1 2 3
(integer) 3
127.0.0.1:6379> RPUSH users 4 5 6
(integer) 6
- LPOP和RPOP
127.0.0.1:6379> LPOP users
"3"
127.0.0.1:6379> RPOP users
"6"
- LRANGE
127.0.0.1:6379> LRANGE users 1 2
1) "1"
2) "4"
2.7 Redis命令-Set命令
Redis的Set結構與Java中的HashSet類似,可以看做是一個value為null的HashMap。因為也是一個hash表,因此具備與HashSet類似的特徵:
- 無序
- 元素不可重複
- 查詢快
- 支援交集.並集.差集等功能
Set型別的常見命令
- SADD key member ... :向set中新增一個或多個元素
- SREM key member ... : 移除set中的指定元素
- SCARD key: 返回set中元素的個數
- SISMEMBER key member:判斷一個元素是否存在於set中
- SMEMBERS:獲取set中的所有元素
- SINTER key1 key2 ... :求key1與key2的交集
- SDIFF key1 key2 ... :求key1與key2的差集
- SUNION key1 key2 ..:求key1和key2的並集
例如兩個集合:s1和s2:
求交集:SINTER s1 s2
求s1與s2的不同:SDIFF s1 s2
具體命令
127.0.0.1:6379> sadd s1 a b c
(integer) 3
127.0.0.1:6379> smembers s1
1) "c"
2) "b"
3) "a"
127.0.0.1:6379> srem s1 a
(integer) 1
127.0.0.1:6379> SISMEMBER s1 a
(integer) 0
127.0.0.1:6379> SISMEMBER s1 b
(integer) 1
127.0.0.1:6379> SCARD s1
(integer) 2
案例
- 將下列資料用Redis的Set集合來儲存:
- 張三的好友有:李四.王五.趙六
- 李四的好友有:王五.麻子.二狗
- 利用Set的命令實現下列功能:
- 計算張三的好友有幾人
- 計算張三和李四有哪些共同好友
- 查詢哪些人是張三的好友卻不是李四的好友
- 查詢張三和李四的好友總共有哪些人
- 判斷李四是否是張三的好友
- 判斷張三是否是李四的好友
- 將李四從張三的好友列表中移除
127.0.0.1:6379> SADD zs lisi wangwu zhaoliu
(integer) 3
127.0.0.1:6379> SADD ls wangwu mazi ergou
(integer) 3
127.0.0.1:6379> SCARD zs
(integer) 3
127.0.0.1:6379> SINTER zs ls
1) "wangwu"
127.0.0.1:6379> SDIFF zs ls
1) "zhaoliu"
2) "lisi"
127.0.0.1:6379> SUNION zs ls
1) "wangwu"
2) "zhaoliu"
3) "lisi"
4) "mazi"
5) "ergou"
127.0.0.1:6379> SISMEMBER zs lisi
(integer) 1
127.0.0.1:6379> SISMEMBER ls zhangsan
(integer) 0
127.0.0.1:6379> SREM zs lisi
(integer) 1
127.0.0.1:6379> SMEMBERS zs
1) "zhaoliu"
2) "wangwu"
2.8 Redis命令-SortedSet型別
Redis的SortedSet是一個可排序的set集合,與Java中的TreeSet有些類似,但底層資料結構卻差別很大。SortedSet中的每一個元素都帶有一個score屬性,可以基於score屬性對元素排序,底層的實現是一個跳錶(SkipList)加 hash表。
SortedSet具備下列特性:
- 可排序
- 元素不重複
- 查詢速度快
因為SortedSet的可排序特性,經常被用來實現排行榜這樣的功能。
SortedSet的常見命令有:
- ZADD key score member:新增一個或多個元素到sorted set ,如果已經存在則更新其score值
- ZREM key member:刪除sorted set中的一個指定元素
- ZSCORE key member : 獲取sorted set中的指定元素的score值
- ZRANK key member:獲取sorted set 中的指定元素的排名
- ZCARD key:獲取sorted set中的元素個數
- ZCOUNT key min max:統計score值在給定範圍內的所有元素的個數
- ZINCRBY key increment member:讓sorted set中的指定元素自增,步長為指定的increment值
- ZRANGE key min max:按照score排序後,獲取指定排名範圍內的元素
- ZRANGEBYSCORE key min max:按照score排序後,獲取指定score範圍內的元素
- ZDIFF.ZINTER.ZUNION:求差集.交集.並集
注意:所有的排名預設都是升序,如果要降序則在命令的Z後面新增REV即可,例如:
- 升序獲取sorted set 中的指定元素的排名:ZRANK key member
- 降序獲取sorted set 中的指定元素的排名:ZREVRANK key memeber
3. Redis的Java客戶端-Jedis
在Redis官網中提供了各種語言的客戶端,地址:https://redis.io/docs/clients/
其中Java客戶端也包含很多:
標記為❤的就是推薦使用的java客戶端,包括:
- Jedis和Lettuce:這兩個主要是提供了Redis命令對應的API,方便我們操作Redis,而SpringDataRedis又對這兩種做了抽象和封裝,因此我們後期會直接以SpringDataRedis來學習。
- Redisson:是在Redis基礎上實現了分散式的可伸縮的java資料結構,例如Map.Queue等,而且支援跨程序的同步機制:Lock.Semaphore等待,比較適合用來實現特殊的功能需求。
3.1 快速入門
-
建立Maven工程
-
引入依賴
<!--jedis--> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.7.0</version> </dependency> <!--單元測試--> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.7.0</version> <scope>test</scope> </dependency>
-
建立連線
private Jedis jedis; @BeforeEach void setUp() { // 1. 建立連線 // jedis = new Jedis("192.168.146.129" , 6379); jedis = JedisConnectionFactory.getJedis(); // 2. 設定密碼 // jedis.auth("north"); // 3. 選擇庫 jedis.select(0); }
-
測試
@Test public void testSet() { // 存資料 String result = jedis.set("name", "陳平安"); System.out.println("result = " + result); // 取資料 String name = jedis.get("name"); System.out.println("name = " + name); } @Test public void testHash() { Long hset = jedis.hset("user:1", "name", "石昊"); Long hset1 = jedis.hset("user:1", "age", "19"); Map<String, String> hgetAll = jedis.hgetAll("user:1"); System.out.println("hgetAll = " + hgetAll); }
-
釋放資源
@AfterEach void tearDown() { if (jedis != null) { jedis.close(); } }
3.2 Jedis連線池
Jedis本身是執行緒不安全的,並且頻繁的建立和銷燬連線會有效能損耗,因此我們推薦大家使用Jedis連線池代替Jedis的直連方式
有關池化思想,並不僅僅是這裡會使用,很多地方都有,比如說我們的資料庫連線池,比如我們tomcat中的執行緒池,這些都是池化思想
的體現。
3.2.1 建立Jedis的連線池
public class JedisConnectionFactory {
private static final JedisPool jedisPool;
static {
// 配置連線池
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(8);
poolConfig.setMaxIdle(8);
poolConfig.setMinIdle(0);
poolConfig.setMaxWaitMillis(1000);
// 建立連線池物件
jedisPool = new JedisPool(poolConfig , "192.168.146.129" , 6379 , 1000 , "north");
}
public static Jedis getJedis() {
return jedisPool.getResource();
}
}
程式碼說明:
- 1) JedisConnectionFacotry:工廠設計模式是實際開發中非常常用的一種設計模式,我們可以使用工廠,去降低代的耦合,比如Spring中的Bean的建立,就用到了工廠設計模式
- 2)靜態程式碼塊:隨著類的載入而載入,確保只能執行一次,我們在載入當前工廠類的時候,就可以執行static的操作完成對 連線池的初始化
- 3)最後提供返回連線池中連線的方法.
對於這段程式碼的理解:
這段程式碼是一個用於連線 Redis 資料庫的 Java 類 JedisConnectionFactory
。它使用了 Jedis 庫,這是一個流行的 Redis 客戶端庫,用於在 Java 程式中與 Redis 伺服器進行通訊。
讓我解釋一下這段程式碼的主要部分:
-
public class JedisConnectionFactory
: 這是一個公共類的定義,類名為JedisConnectionFactory
,用於建立 Redis 連線工廠。 -
private static final JedisPool jedisPool;
: 這是一個私有的靜態常量,用於儲存連線到 Redis 伺服器的連線池例項。連線池是一種管理和維護與 Redis 伺服器連線的機制,可以提高連線的複用和效能。 -
static { ... }
: 這是一個靜態程式碼塊,在類被載入時執行。在這個程式碼塊中,你可以看到配置了一個 Jedis 連線池。以下是對配置選項的解釋:-
JedisPoolConfig poolConfig = new JedisPoolConfig();
: 建立一個 Jedis 連線池的配置物件。 -
poolConfig.setMaxTotal(8);
: 設定連線池中最大連線數為 8。 -
poolConfig.setMaxIdle(8);
: 設定連線池中最大空閒連線數為 8。 -
poolConfig.setMinIdle(0);
: 設定連線池中最小空閒連線數為 0。 -
poolConfig.setMaxWaitMillis(1000);
: 設定從連線池獲取連線的最大等待時間為 1000 毫秒。
然後,使用這些配置項建立一個 Jedis 連線池例項:
-
jedisPool = new JedisPool(poolConfig, "192.168.146.129", 6379, 1000, "north");
-
poolConfig
: 使用之前配置的連線池配置物件。 -
"192.168.146.129"
: Redis 伺服器的 IP 地址。 -
6379
: Redis 伺服器的埠號。 -
1000
: 連線超時時間(毫秒)。 -
"north"
: 連線到 Redis 伺服器的密碼(如果有密碼保護)。
-
-
-
public static Jedis getJedis()
: 這是一個公共靜態方法,用於從連線池中獲取一個可用的 Redis 連線(Jedis 物件)。透過呼叫jedisPool.getResource()
來獲取連線。
這段程式碼的作用是建立一個連線到 Redis 資料庫的連線池,以便在應用程式中可以輕鬆地獲取和管理與 Redis 伺服器的連線。透過呼叫 getJedis()
方法,你可以獲取一個可用的 Redis 連線並開始執行對 Redis 的操作。要確保在使用完連線後,將其釋放回連線池以供其他地方使用。
3.2.2 改造原始程式碼
程式碼說明:
1.在我們完成了使用工廠設計模式來完成程式碼的編寫之後,我們在獲得連線時,就可以透過工廠來獲得。
,而不用直接去new物件,降低耦合,並且使用的還是連線池物件。
2.當我們使用了連線池後,當我們關閉連線其實並不是關閉,而是將Jedis還回連線池的。
@BeforeEach
void setUp(){
//建立連線
/*jedis = new Jedis("127.0.0.1",6379);*/
jedis = JedisConnectionFacotry.getJedis();
//選擇庫
jedis.select(0);
}
@AfterEach
void tearDown() {
if (jedis != null) {
jedis.close();
}
}
4. Redis的Java客戶端-SpringDataRedis
SpringData是Spring中資料操作的模組,包含對各種資料庫的整合,其中對Redis的整合模組就叫做SpringDataRedis,官網地址:https://spring.io/projects/spring-data-redis
- 提供了對不同Redis客戶端的整合(Lettuce和Jedis)
- 提供了RedisTemplate統一API來操作Redis
- 支援Redis的釋出訂閱模型
- 支援Redis哨兵和Redis叢集
- 支援基於Lettuce的響應式程式設計
- 支援基於JDK.JSON.字串.Spring物件的資料序列化及反序列化
- 支援基於Redis的JDKCollection實現
SpringDataRedis中提供了RedisTemplate工具類,其中封裝了各種對Redis的操作。並且將不同資料型別的操作API封裝到了不同的型別中:
4.1 快速入門
-
匯入座標
<!--redis依賴--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!--common-pool--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> <!--Jackson依賴--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency>
-
配置檔案
spring: redis: host: 192.168.150.101 port: 6379 password: 123321 lettuce: pool: max-active: 8 #最大連線 max-idle: 8 #最大空閒連線 min-idle: 0 #最小空閒連線 max-wait: 100ms #連線等待時間
-
test
@Autowired private RedisTemplate redisTemplate; @Test void testString() { // 寫入一條String資料 redisTemplate.opsForValue().set("name" , "徐鳳年"); // 取出資料 Object name = redisTemplate.opsForValue().get("name"); System.out.println("name = " + name); } @Test void testSaveUser() { // 寫入資料 redisTemplate.opsForValue().set("user:100" , new User("李七夜" , 19)); // 獲取資料 User o = (User) redisTemplate.opsForValue().get("user:100"); System.out.println("o = " + o); }
貼心小提示:SpringDataJpa使用起來非常簡單,記住如下幾個步驟即可
SpringDataRedis的使用步驟:
- 引入spring-boot-starter-data-redis依賴
- 在application.yml配置Redis資訊
- 注入RedisTemplate
4.2 資料序列化器
RedisTemplate可以接收任意Object作為值寫入Redis:
只不過寫入前會把Object序列化為位元組形式,預設是採用JDK序列化,得到的結果是這樣的:
缺點:
- 可讀性差
- 記憶體佔用較大
我們可以自定義RedisTemplate的序列化方式,程式碼如下:
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory){
// 建立RedisTemplate物件
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 設定連線工廠
template.setConnectionFactory(connectionFactory);
// 建立JSON序列化工具
GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
// 設定Key的序列化
template.setKeySerializer(RedisSerializer.string());
template.setHashKeySerializer(RedisSerializer.string());
// 設定Value的序列化
template.setValueSerializer(jsonRedisSerializer);
template.setHashValueSerializer(jsonRedisSerializer);
// 返回
return template;
}
}
這裡採用了JSON序列化來代替預設的JDK序列化方式。最終結果如圖
{
"@class": "com.north.pojo.User",
"name": "李七夜",
"age": 19
}
整體可讀性有了很大提升,並且能將Java物件自動的序列化為JSON字串,並且查詢時能自動把JSON反序列化為Java物件。不過,其中記錄了序列化時對應的class名稱,目的是為了查詢時實現自動反序列化。這會帶來額外的記憶體開銷。
4.3 StringRedisTemplate
儘管JSON的序列化方式可以滿足我們的需求,但依然存在一些問題,如下:
{
"@class": "com.north.pojo.User",
"name": "李七夜",
"age": 19
}
為了在反序列化時知道物件的型別,JSON序列化器會將類的class型別寫入json結果中,存入Redis,會帶來額外的記憶體開銷
。
為了減少記憶體的消耗,我們可以採用手動序列化的方式
,換句話說,就是不借助預設的序列化器,而是我們自己來控制序列化的動作
,同時,我們只採用String的序列化器,這樣,在儲存value時,我們就不需要在記憶體中就不用多儲存資料,從而節約我們的記憶體空間
這種用法比較普遍,因此SpringDataRedis就提供了RedisTemplate的子類:StringRedisTemplate,它的key和value的序列化方式預設就是String方式。
手動序列化實現:
@Bean
public RedisTemplate<String , Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
// 建立RedisTemplate 物件
RedisTemplate<String , Object> redisTemplate = new RedisTemplate<>();
// 設定連線工廠
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 設定序列化工具
GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
// key 和 hashKey 採用String序列化
redisTemplate.setKeySerializer(jsonRedisSerializer);
redisTemplate.setHashKeySerializer(jsonRedisSerializer);
// value 和 hashValue 採用JSON序列化
redisTemplate.setValueSerializer(jsonRedisSerializer);
redisTemplate.setHashValueSerializer(jsonRedisSerializer);
// 返回
return redisTemplate;
}
此時我們再來看一看儲存的資料,小夥伴們就會發現那個class資料已經不在了,節約了我們的空間~
{
"name": "徐鳳年",
"age": 22
}
測試類
@SpringBootTest
class RedisApplicationTests {
@Autowired
private StringRedisTemplate stringRedisTemplate;
// JSON 工具
private static final ObjectMapper mapper = new ObjectMapper();
@Test
public void testStringTemplate() throws JsonProcessingException {
// 準備資料
User user = new User("徐鳳年" , 22);
// 手動序列化
String json = mapper.writeValueAsString(user);
// 寫入一條資料到redis
stringRedisTemplate.opsForValue().set("user:200" , json);
// 讀取資料
String result = stringRedisTemplate.opsForValue().get("user:200");
User user1 = mapper.readValue(result, User.class);
System.out.println("user1 = " + user1);
}
}
最後小總結:
RedisTemplate的兩種序列化實踐方案:
-
方案一:
- 自定義RedisTemplate
- 修改RedisTemplate的序列化器為GenericJackson2JsonRedisSerializer
-
方案二:
- 使用StringRedisTemplate
- 寫入Redis時,手動把物件序列化為JSON
- 讀取Redis時,手動把讀取到的JSON反序列化為物件
4.4 Hash結構操作
@SpringBootTest
class RedisStringTests {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Test
void testHash() {
stringRedisTemplate.opsForHash().put("user:400", "name", "虎哥");
stringRedisTemplate.opsForHash().put("user:400", "age", "21");
Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries("user:400");
System.out.println("entries = " + entries);
}
}
我的部落格即將同步至騰訊雲開發者社群,邀請大家一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=3vtuwevgbfms4