簡單學習 Redis
Redis
1. NoSQL四大型別
- 鍵值資料庫: Redis
- 列族資料庫: HBase、BigTable
- 文件資料庫: MongoDB(使用者:百度雲資料庫)
- 圖形資料儲存: GraphDB
NoSQL特點:
- 解耦,方便擴充套件(資料之間沒有關係)
- 大資料量高效能(redis一秒寫8萬次,讀取11萬,nosql的快取記錄級,是一種細粒度的快取,效能較高)
- 資料型別是多樣型的(不需事先設計資料庫,隨取隨用)
2. 什麼是Redis?為什麼要用Redis?Redis應用場景、特性
定義:
Redis(Remote Dictionary Server)遠端字典服務
Redis是一個開源的使用ANSI C語言
提高查詢的速度,把熱點資料放在redis中
伺服器啟動時,就把熱點資料直接放在redis中
(伺服器啟動時,沒有往redis中放資料,使用者第一次取資料時,先從redis中取,取不到,再從mysql中取,把mysql中的資料放到redis中。)
原因:
- redis以記憶體作為資料儲存介質,讀取資料的效率極高,遠遠超過一般資料庫。
(redis一秒寫8萬次,讀取11萬) - 儲存在redis的資料是持久化的,斷電或重啟資料也不會丟失。因為redis的儲存分為記憶體儲存、磁碟儲存和log檔案三部分,重啟後,redis可以重新將資料從磁碟載入到記憶體,這樣redis就實現了持久化。
應用場景:
①會話快取(最常用)
②訊息佇列,比如支付
③活動排行榜或計數
④釋出訂閱訊息(訊息通知)
⑤商品列表,評論列表
特性:
- 多樣的資料型別
- 持久化
- 叢集
- 事務
3. Redis支援的五大資料型別
String
- String是redis最基本的型別,能存放字串,整數,浮點數 ,能存json,json能轉成物件、圖片
- String型別是二進位制安全的。意思是redis的string可以包含任何資料。比如jpg圖片或者序列化的物件 。
- String型別最大能儲存512MB。
相關命令:
- keys * 查詢資料
- get key value 獲取值
- set key value 設定值
- exists key 判斷是否存在,如果存在值為1
- del key 刪除
- type key 檢視型別
- expire key 設定key的過期時間
- ttl key 檢視key的有效秒數
List
- list是列表,可以存放字串,整數,浮點數,按照插入順序排序。你可以新增一個元素到列表的頭部(左邊)或者尾部(右邊)。
- list可以重複
- List型別的資料較靈活,可以當做棧使用使其先進後出,也可以當做訊息佇列使用使其從一側入,從另一側出
相關命令:
- rpush 列表名 data 向列表頭部儲存資料(從右邊壓入)
- lpop 列表名 從左邊彈出顯示
- lLen 列表名 取列表長度
- lIndex 列表名 num 按下標num取某個值
- lrange 列表名 0 -1 返回列表中指定區間內的元素
0 表示列表的第一個元素, 1 表示列表的第二個元素,以此類推。或者 -1 表示列表的最後一個元素, -2 表示列表的倒數第二個元素,以此類推。
Set
- Set是String型別的無序集合。
- 無序集合中元素不允許重複。
相關命令:
- sadd 集合名 data1 data2 向集合中新增資料(無序)
- smembers 集合名 讀取集合中的所有資料
Hash
Redis hash是一個string型別的field和value的對映表,hash特別適合用於儲存物件。
- hset myhash filed1 abc
將key為field1,value為abc放入hash名為的myhash中,如果存在filed1則覆蓋,不存在則建立 - hgetall myhash 以鍵值對的形式返回所有的hash
hash更適合於物件的儲存,String更加適合字串儲存
Zset
有序集合
4. 事務
Redis事務特點:
- Redis單條命令要保證原子性,但Redis事務不保證原子性
- Redis事務沒有隔離性,沒有隔離級別的概念
所有的命令在事務中並不會直接被執行,只有發起執行命令的時候才會執行(Exec) - 一次性、順序性、排他性(不允許被幹擾)
Redis事務三個階段:
- 開啟事務(multi)
- 命令入隊
- 執行事務(exec)
取消事務(discard)
注:
- 編譯型異常時(程式碼有問題,命令有錯),事務中所有的命令都不會被執行
- 執行時異常時 ,執行命令的時候,其他命令是可以正常執行的,錯誤命令丟擲異常。
5. Redis和SpringBoot整合
整合(中介軟體Jedis)
Jedis是Redis官方推薦的Java連線開發工具
用jedis去操作redis資料庫
- jedis = new Jedis(ip,port);
- jedis.set() 新增資料
- jedis.get() 獲取資料
- jedis.rpush() 將一個或多個值插入到列表的尾部(最右邊)
- jedis.hmset() 同時將多個 field-value (欄位-值)對設定到雜湊表中
- jedis.del() 刪除某個鍵
- 建立專案,新增依賴
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.6.2</version>
</dependency>
- 寫資料
application.yml
redis:
host: 192.168.216.201
port: 6379
server:
port: 8080
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mall?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8
username: root
password: 123456
mybatis:
mapperLocations: classpath:com.tedu.mapper/*.xml
logging:
path: ./logs
level:
com.tedu.mapper: debug
@RestController
public class RedisController {
@Value("${redis.host}")
String host;
@Value("${redis.port}")
String port;
@RequestMapping("/redis/insert")
public String insert() throws Throwable {
Jedis jedis = new Jedis(host, Integer.parseInt(port));
Item item1 = new Item("1001", "mate10");
Item item2 = new Item("1002", "mate20");
ObjectMapper objectMapper = new ObjectMapper();
String item1Json = objectMapper.writeValueAsString(item1);
String item2Json = objectMapper.writeValueAsString(item2);
HashMap<String, String> hashMap = new HashMap<>();
hashMap.put("1001", item1Json);
hashMap.put("1002", item2Json);
jedis.hmset("itemList", hashMap);
jedis.close();
return "ok";
}
}
3. 讀資料
@RequestMapping("/get")
public Map<String,String> get() {
Jedis jedis=new Jedis(host,Integer.parseInt(port));
Map<String,String> map=jedis.hgetAll("itemList");
jedis.close();
return map;
}
6. Redis.conf配置內容
- 網路
- bind 127.0.0.1 繫結ip
- port 6379 埠
- protected-mode no 保護模式
- 通用
- daemonize yes 守護程序,預設是no,需要設定為yes開啟後臺執行
- 日誌
- logfile “” #日誌的檔案位置名
- databases 16 #預設的資料庫數量
- 快照 rdb
持久化,在規定的時間內,執行了多少次操作,則會持久化到檔案.rdb- save 900 1
如果900秒內至少有1個key修改了一次,進行持久化(儲存) - save 300 10
如果300秒內至少有10個key修改了一次,進行持久化 - save 60 10000
如果60秒內至少有10000個key修改了一次,進行持久化
- save 900 1
- aof配置
- appendonly no
預設不開啟aof模式,預設是rdb方式持久化,大部分情況下rdb夠用了 - appendfsync everysec
每秒執行一次同步 - appendfsync no 不執行同步 (預設)
- appendonly no
7. Redis持久化
7.1 RDB(Redis DataBase)
在指定的時間間隔內將記憶體中的資料集快照寫入磁碟,也就是Snapshot快照,他恢復時將快照檔案直接讀到記憶體裡。
Redis會單獨建立一個子程序(fork) 來進行持久化,會將資料寫入到一個臨時檔案中,持久化過程都結束了,再將這個臨時檔案替換上次持久化好的檔案。整個過程主程序是不進行任何IO操作的,這確保了極高的效能。
- rdb儲存的檔案是dump.rdb
- 配置預設是RDB持久化方式
- RDB方式比AOF方式更加高效
- 優點:
- 適合大規模的資料恢復
- 對資料的完整性不高
- 進行持久化的時候會fork子程序去執行,主程序的任務不受到影響
- 缺點:
- 最後一次持久化的資料可能丟失(!如果redis意外宕機了)
- 需要一定的時間間隔進行操作
- 、fork程序的時候,會佔用一定的記憶體空間
觸發機制:
- save的規則滿足的情況下
- 執行flushall命令
- 退出redis (kill是不會觸發的)
7.2 AOF(Append only File)
以日誌的形式來記錄寫操作,將Redis執行過的所有指令記錄下來(讀操作不記錄),只可追加檔案不可以改寫檔案。
redis啟動之初會讀取該檔案重新構建資料。即redis重啟的話就根據日誌檔案內容將指令從前到後執行一次以完成資料的恢復。
- AOF儲存的是appendonly.aof檔案
- 手動進行配置 appendonly yes
- aof檔案被破壞,redis會拒絕啟動的,需要修復aof檔案
- redis提供了修復工具redis-checj-aof --fix,錯誤資料會被清除
- rdb和aof可以同用,同用時恢復資料預設使用aof,redis預設開啟rdb
- 優點:
- 資料儲存較完整
- 缺點:
- 相對於資料檔案來說,aof遠大於rdb,修復速度也比rdb慢
- AOF執行效率比RDB慢
8. 分散式鎖
redis是單程序單執行緒
- 執行緒鎖:主要用來給方法、程式碼塊加鎖。當某個方法或程式碼使用鎖,在同一時刻僅有一個執行緒執行該方法或該程式碼段。執行緒鎖只在同一JVM中有效果,因為執行緒鎖的實現在根本上是依靠執行緒之間共享記憶體實現的,比如synchronized是共享物件頭,顯示鎖Lock是共享某個變數(state)。
- 程序鎖:為了控制同一作業系統中多個程序訪問某個共享資源,因為程序具有獨立性,各個程序無法訪問其他程序的資源,因此無法通過synchronized等執行緒鎖實現程序鎖。
- 分散式鎖:當多個程序不在同一個系統中,用分散式鎖控制多個程序對資源的訪問。
分散式鎖使用場景:
- 執行緒間併發問題和程序間併發問題都是可以通過分散式鎖解決的,但是強烈不建議這樣做!因為採用分散式鎖解決這些小問題是非常消耗資源的
- 分散式鎖應該用來解決分散式情況下的多程序併發問題才是最合適的。
分散式鎖的實現:
分散式鎖實現的關鍵是在分散式的應用伺服器外,搭建一個儲存伺服器,儲存鎖資訊,這時候我們很容易就想到了Redis。首先我們要搭建一個Redis伺服器,用Redis伺服器來儲存鎖資訊。
- 在實現的時候要注意的幾個關鍵點:
1、鎖資訊必須是會過期超時的,不能讓一個執行緒長期佔有一個鎖而導致死鎖;
2、同一時刻只能有一個執行緒獲取到鎖。
幾個要用到的redis命令:
- setnx(key, value):“set if not exits”,若該key-value不存在,則成功加入快取並且返回1,否則返回0。
- get(key):獲得key對應的value值,若不存在則返回nil。
- getset(key, value):先獲取key對應的value值,若不存在則返回nil,然後將舊的value更新為新的value。
- expire(key, seconds):設定key-value的有效期為seconds秒。
9. Redis快取預熱、快取雪崩、快取穿透
快取預熱
提前將熱點資料寫入redis快取中
解決思路:
- 直接寫個快取重新整理頁面,上線時手工操作下;
- 資料量不大,可以在專案啟動的時候自動進行載入;
- 定時重新整理快取;
快取雪崩
可以簡單的理解為:由於原有快取失效,新快取未到期間。
**原因:**設定快取時採用了相同的過期時間,在同一時刻出現大面積的快取過期。
解決方法:
- 設定不同的過期時間
- 考慮用加鎖或者佇列的方式保證來保證不會有大量的執行緒對資料庫一次性進行讀寫,從而避免失效時大量的併發請求落到底層儲存系統上。
快取穿透
快取和資料庫中查不到
使用者想要查詢一個數據,發現redis記憶體資料庫中沒有,也就是快取沒有命中。於是向持久層資料庫查詢。發現也沒有,於是本次查詢失敗。當用戶很多的時候。快取都沒有命中,於是請求去請求了持久層資料庫。這會給持久層資料庫造成很大的壓力,這就相當於出現了快取穿透。
eg:惡意訪問不存在的資料
解決方法:
- 布隆過濾器:一種資料結構,對所有可能查詢到的引數以hash形式儲存,在控制層先進行校驗,不符合的丟棄,從而避免了對底層儲存系統的查詢壓力。
eg: 在controller中寫一個set集合,裡面存放id,查詢不到時,立即返回,不會再去資料庫中查詢 - nginx限流
10. Redis主從複製
主從複製,是指將一臺Redis伺服器的資料,複製到其他的Redis伺服器。前者為主節點(master/leader),後者為從節點(slave/follower)。
資料的複製是單向的,只能由主節點到從節點。
master以寫為主,slave以讀為主。
作用:
- 資料冗餘:主從複製實現了資料的熱備份,是持久化之外的一種資料冗餘方式。
- 故障恢復:當主節點出現問題時,可以由從節點提供服務,實現快速的故障恢復
- 負載均衡
- 高可用基石
面試題:如何保證redis與mysql中的資料一致
- 更新了mysql中的資料,也要更新redis
- 刪除了mysql中的資料,也要刪除redis中的資料
- update時,先刪除redis中的資料,更新mysql。把查出來的最新資料再放入redis中。
面試題:Redis記憶體滿了,怎麼辦
在redis.conf中配置回收演算法
LRU:最近不訪問的key,自動刪除