Redis學習總結第一章--Redis 基礎數據結構

Redis學習總結第一章--Redis 基礎數據結構

Redis 有 5 種基礎數據結構,分別爲:string (字符串)、list (列表)、set (集合)、hash (哈希) 和 zset (有序集合)。

 

string (字符串)

字符串 string 是 Redis 最簡單的數據結構。Redis 所有的數據結構都是以唯一的 key 字符串作爲名稱,然後通過這個唯一 key 值來獲取相應的 value 數據。不同類型的數據結構的差異就在於 value 的結構不一樣。字符串結構使用非常廣泛,一個常見的用途就是緩存用戶信息。我們將用戶信息結構體使用 JSON 序列化成字符串,然後將序列化後的字符串塞進 Redis 來緩存。同樣,取用戶信息會經過一次反序列化的過程。

 

鍵值對

 

批量鍵值對:可以批量對多個字符串進行讀寫,節省網絡耗時開銷

 

過期和 set 命令擴展:可以對 key 設置過期時間,到點自動刪除,這個功能常用來控制緩存的失效時間

 

原子計數:如果 value 值是一個整數,還可以對它進行自增操作。自增是有範圍的,它的範圍是 signed long 的最大最小值,超過了這個值,Redis 會報錯

 

list (列表)

Redis 的列表相當於 Java 語言裏面的 LinkedList,注意它是鏈表而不是數組。這意味着 list 的插入和刪除操作非常快,時間複雜度爲 O(1),但是索引定位很慢,時間複雜度爲 O(n),這點讓人非常意外。 當列表彈出了最後一個元素之後,該數據結構自動被刪除,內存被回收。

Redis 的列表結構常用來做異步隊列使用。將需要延後處理的任務結構體序列化成字符串塞進 Redis 的列表,另一個線程從這個列表中輪詢數據進行處理。

 

 

右邊進左邊出:隊列

 

右邊進右邊出:棧

 

 

 

hash (字典)

Redis 的字典相當於 Java 語言裏面的 HashMap,它是無序字典。內部實現結構上同 Java 的 HashMap 也是一致的,同樣的數組 + 鏈表二維結構。第一維 hash 的數組位置碰撞時,就會將碰撞的元素使用鏈表串接起來。

hash 結構也可以用來存儲用戶信息,不同於字符串一次性需要全部序列化整個對象,hash 可以對 用戶結構中的每個字段單獨存儲。這樣當我們需要獲取用戶信息時可以進行部分獲取。而以整個字符串的形式去保存用戶信息的話就只能一次性全部讀取,這樣就會比較浪費網絡流量。 hash 也有缺點,hash 結構的存儲消耗要高於單個字符串,到底該使用 hash 還是字符串,需要根據實際情況再三權衡。

 

set (集合)

Redis 的集合相當於 Java 語言裏面的 HashSet,它內部的鍵值對是無序的唯一的。它的內部實現相當於一個特殊的字典,字典中所有的 value 都是一個值NULL。 當集合中最後一個元素移除之後,數據結構自動刪除,內存被回收。

 

 

zset (有序集合)

zset 似於 Java 的 SortedSet 和 HashMap 的結合體,一方面它是一個 set,保證了內部 value 的唯一性,另一方面它可以給每個 value 賦予一個 score,代表這個 value 的排序權重。

zset 可以用來存粉絲列表,value 值是粉絲的用戶 ID,score 是關注時間。我們可以對粉絲列表按關注時間進行排序。

zset 還可以用來存儲學生的成績,value 值是學生的 ID,score 是他的考試成績。我們可以對成績按分數進行排序就可以得到他的名次。

 

 

其他高級命令

keys:全量遍歷鍵用來列出所有滿足特定正則字符串規則的key,當redis數據量比較大時,性能比較差,要避免使用

 

scan:漸進式遍歷鍵,scan 參數提供了三個參數,第一個是 cursor 整數值,第二個是 key 的正則模式,第三個是遍歷的 limit hint。第一次遍歷時,cursor 值爲 0,然後將返回結果中第一個整數值作爲下一次遍歷的 cursor。一直遍歷到返回的 cursor 值爲 0 時結束。

Redis存儲鍵值對實際使用的是hashtable的數據結構

 

 

Info:查看redis服務運行信息,分爲 9 大塊,每個塊都有非常多的參數,這 9 個塊分別是:

Server 服務器運行的環境參數

Clients 客戶端相關信息

Memory 服務器運行內存統計數據

Persistence 持久化信息

Stats 通用統計數據

Replication 主從複製相關信息

CPU CPU 使用情況

Cluster 集羣信息

KeySpace 鍵值對統計數量信息

 

 

核心原理

Redis的單線程和高性能

Redis 單線程爲什麼還能這麼快?

因爲它所有的數據都在內存中,所有的運算都是內存級別的運算,而且單線程避免了多線程的切換性能損耗問題。正因爲 Redis 是單線程,所以要小心使用 Redis 指令,對於那些耗時的指令(比如keys),一定要謹慎使用,一不小心就可能會導致 Redis 卡頓。

Redis 單線程如何處理那麼多的併發客戶端連接?

Redis的IO多路複用:redis利用epoll來實現IO多路複用,將連接信息和事件放到隊列中,依次放到文件事件分派器,事件分派器將事件分發給事件處理器。

Nginx也是採用IO多路複用原理解決C10K問題

 

持久化

RDB快照(snapshot)

在默認情況下, Redis 將內存數據庫快照保存在名字爲 dump.rdb 的二進制文件中。

你可以對 Redis 進行設置, 讓它在“ N 秒內數據集至少有 M 個改動”這一條件被滿足時, 自動保存一次數據集。

比如說, 以下設置會讓 Redis 在滿足“ 60 秒內有至少有 1000 個鍵被改動”這一條件時, 自動保存一次數據集:

# save 60 1000

AOF(append-only file)

快照功能並不是非常耐久(durable): 如果 Redis 因爲某些原因而造成故障停機, 那麼服務器將丟失最近寫入、且仍未保存到快照中的那些數據。從 1.1 版本開始, Redis 增加了一種完全耐久的持久化方式: AOF 持久化,將修改的每一條指令記錄進文件

你可以通過修改配置文件來打開 AOF 功能:

# appendonly yes

從現在開始, 每當 Redis 執行一個改變數據集的命令時(比如 SET), 這個命令就會被追加到 AOF 文件的末尾。

這樣的話, 當 Redis 重新啓時, 程序就可以通過重新執行 AOF 文件中的命令來達到重建數據集的目的。

你可以配置 Redis 多久纔將數據 fsync 到磁盤一次。

有三個選項:

  • AOF 文件時就執行一次 fsync :非常慢,也非常安全。
  •  fsync 一次:足夠快(和使用 RDB 持久化差不多),並且在故障時只會丟失 1 秒鐘的數據。
  •  fsync :將數據交給操作系統來處理。更快,也更不安全的選擇。

推薦(並且也是默認)的措施爲每秒 fsync 一次, 這種 fsync 策略可以兼顧速度和安全性。

RDB 和 AOF ,我應該用哪一個?

如果你非常關心你的數據, 但仍然可以承受數分鐘以內的數據丟失, 那麼你可以只使用 RDB 持久化。

有很多用戶都只使用 AOF 持久化, 但我們並不推薦這種方式: 因爲定時生成 RDB 快照(snapshot)非常便於進行數據庫備份, 並且 RDB 恢復數據集的速度也要比 AOF 恢復的速度要快。

Redis 4.0 混合持久化

重啓 Redis 時,我們很少使用 rdb 來恢復內存狀態,因爲會丟失大量數據。我們通常使用 AOF 日誌重放,但是重放 AOF 日誌性能相對 rdb 來說要慢很多,這樣在 Redis 實例很大的情況下,啓動需要花費很長的時間。 Redis 4.0 爲了解決這個問題,帶來了一個新的持久化選項——混合持久化。AOF在重寫(aof文件裏可能有太多沒用指令,所以aof會定期根據內存的最新數據生成aof文件)時將重寫這一刻之前的內存rdb快照文件的內容和增量的 AOF修改內存數據的命令日誌文件存在一起,都寫入新的aof文件,新的文件一開始不叫appendonly.aof,等到重寫完新的AOF文件纔會進行改名,原子的覆蓋原有的AOF文件,完成新舊兩個AOF文件的替換;

AOF根據配置規則在後臺自動重寫,也可以人爲執行命令bgrewriteaof重寫AOF。 於是在 Redis 重啓的時候,可以先加載 rdb 的內容,然後再重放增量 AOF 日誌就可以完全替代之前的 AOF 全量文件重放,重啓效率因此大幅得到提升。

開啓混合持久化:

# aof-use-rdb-preamble yes    

混合持久化aof文件結構

 

緩存淘汰策略

當 Redis 內存超出物理內存限制時,內存的數據會開始和磁盤產生頻繁的交換 (swap)。交換會讓 Redis 的性能急劇下降,對於訪問量比較頻繁的 Redis 來說,這樣龜速的存取效率基本上等於不可用。

在生產環境中我們是不允許 Redis 出現交換行爲的,爲了限制最大使用內存,Redis 提供了配置參數 maxmemory 來限制內存超出期望大小。

當實際內存超出 maxmemory 時,Redis 提供了幾種可選策略 (maxmemory-policy) 來讓用戶自己決定該如何騰出新的空間以繼續提供讀寫服務。

noeviction 不會繼續服務寫請求 (DEL 請求可以繼續服務),讀請求可以繼續進行。這樣可以保證不會丟失數據,但是會讓線上的業務不能持續進行。這是默認的淘汰策略。

volatile-lru 嘗試淘汰設置了過期時間的 key,最少使用的 key 優先被淘汰。沒有設置過期時間的 key 不會被淘汰,這樣可以保證需要持久化的數據不會突然丟失。

volatile-ttl 跟上面一樣,除了淘汰的策略不是 LRU,而是 key 的剩餘壽命 ttl 的值,ttl 越小越優先被淘汰。

volatile-random 跟上面一樣,不過淘汰的 key 是過期 key 集合中隨機的 key。

allkeys-lru 區別於 volatile-lru,這個策略要淘汰的 key 對象是全體的 key 集合,而不只是過期的 key 集合。這意味着沒有設置過期時間的 key 也會被淘汰。

allkeys-random 跟上面一樣,不過淘汰的策略是隨機的 key。

volatile-xxx 策略只會針對帶過期時間的 key 進行淘汰,allkeys-xxx 策略會對所有的 key 進行淘汰。如果你只是拿 Redis 做緩存,那應該使用 allkeys-xxx,客戶端寫緩存時不必攜帶過期時間。如果你還想同時使用 Redis 的持久化功能,那就使用 volatile-xxx 策略,這樣可以保留沒有設置過期時間的 key,它們是永久的 key 不會被 LRU 算法淘汰。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章