1. Redis 簡介
Redis(Remote Dictionary Server)是一個開源的內存數據庫,遵守 BSD 協議,它提供了一個高性能的鍵值(key-value)存儲系統,常用于緩存、消息隊列、會話存儲等應用場景。
1.1 特征
-
豐富的數據類型:Redis 不僅僅支持簡單的 key-value 類型的數據,還提供了 list、set、zset(有序集合)、hash 等數據結構的存儲。這些數據類型可以更好地滿足特定的業務需求,使得 Redis 可以用于更廣泛的應用場景。
-
高性能的讀寫能力:Redis 能讀的速度是 110000次/s,寫的速度是 81000次/s。這種高性能主要得益于 Redis 將數據存儲在內存中,從而顯著提高了數據的訪問速度。
-
原子性操作:Redis 的所有操作都是原子性的,這意味著操作要么完全執行,要么完全不執行。這種特性對于確保數據的一致性和完整性非常重要。
-
持久化機制:Redis 支持數據的持久化,可以將內存中的數據保存在磁盤中,以便在系統重啟后能夠再次加載使用。這為 Redis 提供了數據安全性,確保數據不會因為系統故障而丟失。
-
豐富的特性集:Redis 還支持 publish/subscribe(發布/訂閱)模式、通知、key 過期等高級特性。這些特性使得 Redis 可以用于消息隊列、實時數據分析等復雜的應用場景。
-
主從復制和高可用性:Redis 支持 master-slave 模式的數據備份,提供了數據的備份和主從復制功能,增強了數據的可用性和容錯性。
-
支持 Lua 腳本:Redis 支持使用 Lua 腳本來編寫復雜的操作,這些腳本可以在服務器端執行,提供了更多的靈活性和強大的功能。
-
單線程模型:盡管 Redis 是單線程的,但它通過高效的事件驅動模型來處理并發請求,確保了高性能和低延遲。
1.2 SQL 和 NOSQL 區別
Redis 屬于 NOSQL 類型
- 結構化:數據庫表的字段的類型即長度都是限定好的。
- SQL查詢:SQL 查詢語句格式是固定的無論 MySql 還是 Oracle
- ACID:原子性,一致性,隔離性,持久性
- BASE:不全部滿足 ACID
- 關聯:數據表中的數據可以通過外鍵關聯在一起
2. Redis 命令行客戶端
Redis 安裝完成后就自帶了命令行客戶端:redis-cli,使用方式如下:
// 先輸入
redis-cli -h 127.0.0.1 -p 6379
// 回車后輸入
auth 123456
其中常見的 options 有:
-h 127.0.0.1
:指定要連接的 redis 節點的 IP 地址,默認是 127.0.0.1 (本機),也可以填寫本機的 IP 地址-p 6379
:指定要連接的 redis 節點的端口,默認是 6379-a 123456
:指定 redis 的訪問密碼 (一般不這個,使用auth
命令來輸入密碼)
3. Redis 數據結構介紹
4. Redis 命令
對于這些命令 Redis 都提供了幫助文檔只要在你要使用的命令前面加入 help 即可
4.1 通用命令
-
KEYS
查看符合模板的所有 key,不建議在生成環境設備上使用
KEYS pattern
-
DEL
刪除一個指定的 Key
DEL key [key ...]
-
EXISTS
判斷 Key 是否存在
EXISTS key [key ...]
-
EXPIRE
給一個 Key 設置有效期,有效期到了該 Key 自動刪除
EXPIRE key seconds
-
TTL
查看一個 Key 剩余的有效期
TTL key
-
hlep
查看某個命令的使用方法
help del
4.2 String類型命令
4.2.1 String類型分類
- 字符串
- 整形(Int)
- 浮點型(Float)
4.2.2 常用命令
-
SET
添加或者修改,指定鍵不存在則新增,否則修改
set name jack
-
GET
根據 key 獲取 String 類型的 Value
get name
-
MSET
批量添加 String 類型的鍵值對
mset key1 value1 key2 value2
-
MGET
根據多個 Key 獲取多個 Value
mget key1 key2
-
INCR
讓一個整形的 key 的值自增 1
incr age
-
INCRBY
讓一個整形 key 自增并指定步長
incrby num 2
-
INCRBYFLOAT
讓一個浮點類型的值自增并指定步長
incrbyfloat num 0.2
-
SETNX
添加一個 String 類型的鍵值對前提是這個 Key 不存在,否則不執行
setnx name jack
-
SETEX
添加一個鍵值對,并指定有效日期。
setex name 10 jack
4.3 Key的層級格式
在 Redis 沒有類似于 MySQL 中 Table 的概念,我們如何區分不同類型 Key 呢?
比如我們需要存儲商品信息和用戶信息到 Redis 當中,此時他們的 Id 恰好都為 1。這樣當我們要取數據時發現無法區分。此時我們可以給 Key 加上一個前綴加以區分。當然這前綴按規范要具有一定層次結構
4.4 Hash類型命令
該結構類似于 Java 的 HashMap
4.4.1 哈希類型結構
4.4.2 常見命令
-
HSET key field value
添加或者修改 Hash 類型,鍵為 key ,值的鍵為 field 的值為 Value
hset user name zhansan
-
HGET key field
根據 key 獲取 Hash 類型,鍵為 key ,值的鍵為 field 的值
hget user name
-
HMSET
批量添加 Hash 類型 Key 的 field 的值
hset user name zhansan age 18
-
HMGET
批量獲取 Hash 類型,鍵為 key 的多個 field 的值
hmget user name age
-
HGETALL
獲取一個 Hash 類型,鍵為 key 所有的鍵值對
hgetall user
-
HKEYS
獲取一個 Hash 類型,鍵為 key 所有的鍵
hkeys user
-
HVALS
獲取一個 Hash 類型,鍵為 key 所有的值
hvals user
-
HINCRBY
讓一個 Hash 類型的 key 的 field 的值自增長指定步長
hincrby user age 2
-
HSETNX
添加一個 Hash 類型的 key 的 field 的值,前提是這個 field 不存在,否則不執行
hsetnx user age 100
4.5 List類型
類似雙端隊列,查詢慢,刪除快,該數據類型可以根據方法,模擬棧,隊列,阻塞隊列的數據結構
4.5.1 常見命令
-
LPUSH key element
向列表左側插入一個或者多個元素
lpush goods car food
-
RPUSH key element
向列表右側插入一個或者多個元素
rpush goods car food
-
RPOP KEY [COUNT]
將列表左側元素彈出一個或者多個,沒有則返回 nil
lpop goods 2
-
RPOP KEY
將列表右側元素彈出一個或者多個,沒有則返回 nil
rpop goods 2
-
BLPOP 和 BRPOP [TimeOut]
和 LPOP 和 RPOP 類似,只不過在當列表中沒有元素的時候會等待指定時間,而不是返回nil,后面該功能會被充當緩沖隊列
Brpop user 10
-
LRANGE KEY STAR END
返回一段角標內的所有元素
4.6 Set類型
無序,不重復,查詢快,類似于 C++ 的 Unordered_Set
4.6.1 常見命令
-
SADD key member
向指定 Key 的集合中插入一個或者多個元素
-
SREM key member
將指定 Key 的集合中指定元素刪除
-
SCARD key
統計集合中的元素
-
SISMEMBER key member
查詢某個元素是否在集合當中
-
SMEMBERS key:
獲取指定Key的集合中的所有元素
-
SINTER key1 key2
獲取鍵為 key1 和 key2 兩個集合的交集
-
SDIFF key1 key2
獲取鍵為 key1 和 key2 兩個集合的差集
-
SUNION key1 key2
獲取鍵為 key1 和 key2 兩個集合的并集
4.7 SortedSet類型
可排序集合和 Java 中的 TreeSet 類似,但底層差距很大。SortedSet中的每個元素都帶有一個score屬性,可以基于 score 的值對集合進行排序,底層實現是 跳表 + hash表 。常被用于實現排行榜的業務。
4.7.1 常用命令
-
ZADD key score member
添加一個或者多個元素,score 是加入元素的分數(排序依據),member 表示要加入的元素
-
ZREM key member
刪除指定元素
-
ZSCORE key member
獲取指定元素的分數
-
ZRANK key member
獲取指定元素的排名(默認升序排名,可以用 ZREVRANK 獲取降序排名)
-
ZCARD key
獲取集合中的元素數量
-
ZCOUNT key min max
統計 Score 值在 min 和 max 之間的元素個數
-
ZINCRBY key increment member
讓集合指定元素 member 自增,步長為指定的 increment 的值
-
ZRANGE key min max
按照 Score 排序后,獲取指定排名范圍內的元素
-
ZRANGEBYSCORE key min max
按照 Score 排序后,獲取指定分數范圍內的元素
-
ZDIFF,ZINTER,ZUNION
求差集,求交集,求并集。
5. Java客戶端
5.1 簡介
Java的Redis客戶端(如Jedis、Lettuce)通過封裝Redis協議,允許Java應用與Redis服務器交互。它們提供直觀的API執行數據操作,支持連接池、集群、哨兵等部署模式。其中Jedis輕量高效但線程不安全,需配合連接池使用;Lettuce基于Netty實現異步與線程安全;Redisson專注分布式服務。開發者可根據性能、線程模型和功能需求靈活選用。
5.2 Jedis
5.2.1 簡介
Jedis 是一款輕量級、同步阻塞的 Java Redis 客戶端,通過直連模式與 Redis 服務器通信。其 API 直接映射 Redis 原生命令(如
set()
對應SET
指令),學習成本低且穩定性強;但原生線程不安全(多線程需依賴連接池),適用于并發要求不高的傳統項目或小型應用,是 Java 生態中最經典的 Redis 集成方案之一。
5.2.2 特點
-
優點:
Jedis 設計小巧,實現簡潔,穩定性高。其最大優勢在于 API 與 Redis 原生命令高度一致:方法名和參數直接對應官方文檔指令。這使得開發者遇到不熟悉的操作時,無需額外學習客戶端封裝,直接查閱 Redis 命令文檔即可快速上手,顯著降低了學習成本。相比之下,某些客戶端重命名 API 的做法雖意圖簡化,實則增加了額外的記憶負擔。 -
缺點:
Jedis 采用直連模式且非線程安全。其核心問題在于:單個Jedis
實例內的RedisOutputStream
(寫流) 和RedisInputStream
(讀流) 是共享的全局資源。當多個線程并發操作同一實例時,會爭搶這些流資源,導致讀寫數據混亂(如數據錯位、混合),而非 Redis 服務本身的數據安全問題。
5.2.3 Jedis 的使用
- 引用依賴
<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>3.7.0</version>
</dependency>
- 建立連接
private Jedis jedis;
@Before // 該注解,表示在所有你創鍵的測試方法前先執行,這里用于建立連接
public void setUp() throws Exception {jedis = new Jedis("192.168.195.132", 6379); //建立連接jedis.auth("123456"); //密碼jedis.select(0); //選擇庫
}
- 使用 Jedis
@Test
public void testString() {//命令名和 Redis 的一致 String s = jedis.set("name", "tom");String name = jedis.get("name");
}
- 釋放資源
@After // 該注解,表示在所有你創鍵的測試方法后執行,這里用于關閉連接
public void tearDown() throws Exception {// 如果在建立連接的時候拋出了異常,那么運行到這的時就會有空指針異常的風險if(jedis != null) {jedis.close();}
}
5.2.4 Jedis連接池
Jedis本身是線程不安全的,并且頻繁的創建和銷毀連接會有性能損耗。所以建議使用 Jedis 的連接池來代替 Jedis的直連方式。
public class JedisConnectionFactory {private static JedisPool jedisPool;static {//配置連接池JedisPoolConfig poolConfig = new JedisPoolConfig();//最大連接量,表示最多建立8個連接poolConfig.setMaxTotal(8); //最大空閑連接,表示即便沒有線程訪問我這個池子,也會最多預備8個連接poolConfig.setMaxIdle(8); //最小空閑連接,表示空閑連接存放一段時間后,會被自動釋放,直到預備連接小于等于2個poolConfig.setMinIdle(2); //最長等待時間,表示當連接池內沒有空閑連接時,線程對多等多久后放棄等待poolConfig.setMaxWaitMillis(1000); //建立連接jedisPool = new JedisPool(poolConfig,"192.168.195.132", 6379, 1000, "123456");}// 獲取連接public static Jedis getJedis() {return jedisPool.getResource();}
}
5.3 SpringDataRedis
5.3.1 簡介
- Spring Data Redis 是 Spring 生態的 Redis 集成框架,內部整合來 Jedis 和 Lettuce。
- 提供了
RedisTemplate
統一 API 來操作 Redis。- 簡化數據操作(自動處理序列化/連接管理)
- 支持事務、發布訂閱、集群與哨兵模式,并天然適配 Spring 事務管理,顯著降低 Java 應用訪問 Redis 的復雜度,適用于需與 Spring 深度整合的中大型項目。
5.3.2 SpringDataRedis的使用
**SpringBoot 已經整合 SpringDataRedis,并且實現了自動裝配,使用起來非常方便,**所以下面我們基于SpringBoot 來使用 SpringDataRedis
- 引入依賴
<!--redis起步依賴-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency><!--連接池依賴-->
<!--無論是 Jedis 還是 Luttce,都是基于 commons-pool 來實現連接池的效果 -->
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId>
</dependency>
- 連接和連接池配置
spring:data:redis:host: 192.168.195.132 #連接的虛擬機的 IP 地址port: 6379 #端口號database: 0 #連接的數據庫password: 123456 #密碼lettuce:pool: #連接池的配置max-active: 8 #最大連接數max-idle: 8 #最大空閑連接數min-idle: 0 #最小空閑連接數max-wait: 100ms #最大等待時間#具體解釋可以看 Jedis 配置連接池那一節
- 測試代碼
@SpringBootTest
class RedisDemoApplicationTests {@Autowiredprivate RedisTemplate redisTemplate;@Testvoid stringTest() {redisTemplate.opsForValue().set("name", "虎哥");Object name = redisTemplate.opsForValue().get("name");System.out.println(name);}
}
5.3.3 RedisTemplated的缺點
RedisTemplate 可以存儲任意類型的數據,原因是在存儲之前會先把存儲的數據序列化字節形式,默認采用的 JDK 序列化。但是得到的結果,可讀性差,并且占用了更多的內存
5.3.3 解決方式
RedisTemplate 的缺點產生原因在于他默認的序列化方式不太合適,所以我們可以更改他的序列化方式即可解決這個問題。
存在的小問題以及解決辦法
- 但是這種方式還是存在一點點問題就是,他 Value 使用的是 Json 序列化器,當存入和獲取的數據類型是對象是會自動序列化和反序列化,但是要實現自動反序列化,存入 Redis 的數據數據需要加一段額外的信息來記錄該 Json 數據屬于哪個對象,這樣就造成了,內存的額外開銷。
- 解決方式也很簡單,就是再一次跟換 Value 的序列化方式,采用和 Key 一樣的 String 序列化器,但是后果就是,存入數據類型只能是 String 類型。但是沒關系,只要當我們要存入對象時,就先將他轉為 Json 字符串格式
- 但是幸運的是我們不要自己再去修改,RedisTemplate 的 Value 的序列化方式了。這個別人已經寫好了。Spring 提供了一個 StringRedisTemplate 類,他的 Key 和 Value 默認的序列化方式就是 String 序列化,省去了我們自己定義 RedisTemplate。
5.3.4 StringRedisTemplate
5.3.4.1 簡介
為了節省空間我們并不會使用 Json 的序列化器來處理 value,而是統一的使用 String 的序列化器,要求只能存儲String 類型的 key 和 value。當需要存儲 Java 對象的時候,需手動完成對象序列化和反序列化。
5.3.4.2 使用步驟
至于序列化工具,使用自己喜歡的就好,我這里使用的 FastJson
- 導入依賴
<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.7</version>
</dependency>
- 代碼演示
@Autowired
private StringRedisTemplate stringRedisTemplate;@Test
void objectTest() {User user = new User("name", "虎哥");//手動序列化(object -> Json)String s = JSON.toJSONString(user);stringRedisTemplate.opsForValue().set("user:100", s);String s1 = stringRedisTemplate.opsForValue().get("user:100");//手動反序列化(Json -> object)User user1 = JSON.parseObject(s1, User.class);System.out.println(user1);
}