Spring boot 論壇專案實戰_04
1. Redis 入門
-
Redis 是一款 基於鍵值對 的NoSQL 資料庫, 它的值支援多種資料結構:
-
字串(Strings)、雜湊(hashes)、列表(lists)、集合(sets)、有序集合(sorted sets)等
-
-
Redis 將所有的資料都存放在記憶體中,所以它的讀寫效能十分驚人。同時,Redis還可以將記憶體中的資料以快照或日誌的形式儲存到硬碟上,以保證資料的安全性
-
Redis 典型的應用場景包括:快取、排行榜、計數器、社交網路、訊息佇列等。
-
參考網站:
-
Redis 常用命令:
-
-
字串(Strings):
-
儲存: set key【字元拼接用“ : ”】 value
-
獲取: get key【字元拼接用“ : ”】
-
數字加:incr key,返回 integer
-
數字減:decr key,返回 integer
-
加減運算只能是 整數型別 integer
-
-
雜湊(hashes):
-
儲存:hset key【字元拼接用“ : ”】 field【hash 的 key】 value
-
獲取:hget key【字元拼接用“ : ”】 field
-
-
列表(lists):【可以理解為一個橫向的陣列】
-
根據其存取 可左可右,可分別實現 佇列 和 棧 的特性
-
進:可左可右
-
左進
-
左存多個數值: lpush key value [value ...]
-
檢視當前列表長度: llen key
-
檢視指定 key 的 list 裡面第 index 個值:lindex key index
-
檢視指定範圍的值:lrange key start stop
-
-
出:可左可右
-
指定右出: rpop key
-
-
集合(sets):
-
存入集合元素:sadd key member [member ...]
-
統計集合元素:scard key
-
隨機彈出元素:spop key 【可用於抽獎】
-
檢視集合所剩元素:smembers key
-
-
有序集合(sorted sets):
-
新增元素:zadd key [NX|XX] [CH] [INCR] score member [score member ...] 【分數 元素名】
-
查詢某一個值的分數:zscore key member
-
返回某個值的排名:zrank key member 【預設由小到大,從 0 開始】
-
取某個範圍的資料:zrange key start stop [WITHSCORES]
-
-
全域性:keys pattern
-
檢視當前所有的 key :keys *
-
檢視所有 test 開頭的 key:keys test*
-
檢視某個 key 的型別:type key
-
檢視某個 key 是否存在:EXISTS key [key ...]
-
刪除某 key:del key [key ...]
-
設定 某 key 的過期時間【到期自動刪除】:expire key seconds
-
-
2. Spring 整合 Redis
-
引入依賴
-
spring-boot-starter-data-redis
-
-
配置Redis
-
Spring 預設配置的 k-v 中 k:Object
實際開放常用的 k: String 型別
所以這裡需要自己再手動配置
-
配置資料庫引數
-
編寫配置類,構造RedisTemplate
-
// 基於 Spring 框架的 import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.RedisSerializer; @Configuration public class RedisConfig { // 接入連線工廠, 才能建立物件 @Bean public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){ // 方法的例項化 RedisTemplate<String,Object> template = new RedisTemplate<String,Object>(); // 給物件設定連線工廠 template.setConnectionFactory(factory); // 設定 key 的序列化方式, 引數是 spring 框架的 redis 下的 // 返回一個能夠序列化字串的序列化器 template.setKeySerializer(RedisSerializer.string()); // 設定 value 的序列化方式 template.setValueSerializer(RedisSerializer.json()); // 設定 hash 的 key 的序列化方式 template.setHashKeySerializer(RedisSerializer.string()); // 設定 hash 的 value 的序列化方式 template.setHashValueSerializer(RedisSerializer.json()); // 觸發設定結束後 生效 template.afterPropertiesSet(); return template; } }
-
-
-
訪問Redis
-
redisTemplate.opsForValue()
-
@Test public void testStrings(){ String redisKey = "test:count"; // String 型別的值 redisTemplate.opsForValue().set(redisKey,1); System.out.println(redisTemplate.opsForValue().get(redisKey)); System.out.println(redisTemplate.opsForValue().increment(redisKey)); System.out.println(redisTemplate.opsForValue().decrement(redisKey)); }
-
-
redisTemplate.opsForHash()
-
// 訪問 hash @Test public void testHashes(){ String redisKey = "test:user"; redisTemplate.opsForHash().put(redisKey,"id",1); redisTemplate.opsForHash().put(redisKey,"username","張三"); System.out.println(redisTemplate.opsForHash().get(redisKey, "id")); System.out.println(redisTemplate.opsForHash().get(redisKey, "username")); }
-
-
redisTemplate.opsForList()
-
@Test public void testLists(){ // 左進列表 String redisKey = "test:ids"; // 放入資料 redisTemplate.opsForList().leftPush(redisKey,101); redisTemplate.opsForList().leftPush(redisKey,102); redisTemplate.opsForList().leftPush(redisKey,103); // 取出資料 // 統計當前元素個數 System.out.println(redisTemplate.opsForList().size(redisKey)); // 獲取指定位置的元素, 獲取某個索引所對應的元素 System.out.println(redisTemplate.opsForList().index(redisKey,0)); // 按照 索引範圍獲取元素 System.out.println(redisTemplate.opsForList().range(redisKey, 0, 2)); // 彈出元素, 左出 System.out.println(redisTemplate.opsForList().leftPop(redisKey)); System.out.println(redisTemplate.opsForList().leftPop(redisKey)); System.out.println(redisTemplate.opsForList().leftPop(redisKey)); }
-
-
redisTemplate.opsForSet()
-
@Test public void testSets(){ String redisKey = "test:teachers"; redisTemplate.opsForSet().add(redisKey,"劉備","關羽","張飛","趙雲","孔明"); // 統計元素個數 System.out.println(redisTemplate.opsForSet().size(redisKey)); // 彈出一個數據, 隨機彈出 System.out.println(redisTemplate.opsForSet().pop(redisKey)); // 統計現在集合中的資料都是什麼, 展示現在集合中所有元素 System.out.println(redisTemplate.opsForSet().members(redisKey)); }
-
-
redisTemplate.opsForZSet()
-
@Test public void testSortedSets(){ String redisKey = "test:students"; // 新增 元素, 及其對應的分數 redisTemplate.opsForZSet().add(redisKey,"唐僧",80); redisTemplate.opsForZSet().add(redisKey,"悟空",90); redisTemplate.opsForZSet().add(redisKey,"八戒",50); redisTemplate.opsForZSet().add(redisKey,"沙僧",70); redisTemplate.opsForZSet().add(redisKey,"龍馬",60); // 統計元素總數 System.out.println(redisTemplate.opsForZSet().zCard(redisKey)); // 查詢某個元素的分數 System.out.println(redisTemplate.opsForZSet().score(redisKey,"八戒")); // 查詢某個元素的排名, 預設由小到大 System.out.println(redisTemplate.opsForZSet().rank(redisKey,"八戒")); // 倒敘排名, 按分數由大到小 System.out.println(redisTemplate.opsForZSet().reverseRank(redisKey,"八戒")); // 從小到大取前三 System.out.println(redisTemplate.opsForZSet().range(redisKey,0,2)); // 從大到小取前三 System.out.println(redisTemplate.opsForZSet().reverseRange(redisKey,0,2)); }
-
-
對 key 的訪問
-
// 訪問公共型別 key @Test public void testKeys(){ // 程式中一般不會用 keys * 這個命令, 這個命令一般是直接查詢 redisTemplate.delete("test:user"); // 判斷 key 是否存在 System.out.println(redisTemplate.hasKey("test:user")); // 設定 key 的過期時間 // TimeUnit. 設定時間 單位: 日, 時, 分, 秒, 毫秒... redisTemplate.expire("test:students",10, TimeUnit.SECONDS); }
-
-
多次訪問同一個key , 可採用繫結 key 的方式,來避免複寫 redisKey:
-
// 多次訪問同一個 key @Test public void testBoundOperations(){ String redisKey = "test:count"; // Bound XXX Operations: XXX : 你具體要訪問的資料型別, 這裡是String // redisTemplate.bound XXX Ops : 繫結具體的資料型別, 這裡是String BoundValueOperations operations = redisTemplate.boundValueOps(redisKey); // 這樣就是綁定了 key 不用每次都寫了. operations.increment(); operations.increment(); operations.increment(); operations.increment(); operations.increment(); System.out.println(operations.get()); }
-
-
Redis 的事務
Redis 是 非關係型資料庫, 所以不用嚴格遵守 ACID
在實際生產環境中, 建議用程式設計式事務實現, 宣告式只能針對方法整體不能 對單獨程式碼行控制
// 程式設計式事務
@Test public void testTransactional() { Object obj = redisTemplate.execute(new SessionCallback() { @Override public Object execute(RedisOperations operations) throws DataAccessException { String redisKey = "test:tx"; // 啟用事務 operations.multi(); operations.opsForSet().add(redisKey,"張三"); operations.opsForSet().add(redisKey,"李四"); operations.opsForSet().add(redisKey,"王五"); // 事務過程是不能允許讀的 System.out.println(operations.opsForSet().members(redisKey)); //operations.exec() : 提交事務 return operations.exec(); } }); System.out.println(obj); }
-
列印結果可以看出:
-
第一次 查詢結構為空, 保證了事務的原子性
-
第二次列印結果: 前面三個 “1”: 每次操作影響的元素個數; 後面就是這個集合內的具體元素
-
3. 點贊
將點贊資料存入 Redis 提升效能, 優於存在記憶體中
非同步請求
-
點贊
-
支援對帖子、評論點贊
-
第1次點贊,第二次取消點贊【雙擊取消操作】
-
-
首頁點贊數量
-
統計帖子的點贊數量
-
-
詳情頁點贊數量
-
統計點贊數量
-
顯示點贊狀態
-
4. 我收到的贊
-
重構點贊功能
-
以使用者為key, 記錄點贊數量
-
increment(key), decrement(key)
-
-
開發個人主頁
-
以使用者為key, 查詢點贊數量
-
5. 關注、取消關注
-
需求
-
開發關注、取消關注功能
-
統計使用者的關注數、粉絲數
-
-
關鍵
-
若 A 關注了 B, 則 A 是 B 的 Follower(粉絲) , B 是 A 的 Followee(目標)。
-
關注的目標可以是使用者、帖子、題目等,在實現時將這些目標抽象為實體。
-
6. 關注列表、粉絲列表
-
業務層
-
查詢某個使用者關注的人,支援分頁。
-
查詢某個使用者的粉絲,支援分頁。
-
-
表現層
-
處理 “查詢關注的人”、“查詢粉絲” 請求。
-
編寫 “查詢關注的人”、“查詢粉絲” 模板。
-
7. 優化登入模組
-
使用Redis 儲存驗證碼
-
驗證碼需要頻繁的訪問與重新整理,對效能要求較高
-
驗證碼不需永久儲存,通常在很短的時間後就會失效
-
設定過期時間
-
-
分散式部署,存在Session 共享的問題
-
-
使用Redis 儲存登入憑證【不刪,保留使用者記錄】
-
處理每次請求時,都要查詢使用者的登入憑證,訪問的效率非常高
-
-
使用Redis 快取使用者資訊【暫存,要刪+】
-