redis-redis為什么這么快
Redis為什么這么快?
作者:w08e
?? NOTE
redis熱門問題 沖沖沖
答題思路
核心圍繞存儲方式、架構設計與使用方式回答:

回答話術
Redis 官方早前發布過一套基準測試,在 Redis 服務連接數小于 1 萬時,并發數量每秒可以達到 10-12 萬左右。連接數在 3-6 萬 時,也能支持每秒 5-6 萬的并發。
我覺得 Redis 之所以操作這么快,主要有以下幾方面原因:
- 從存儲方式上看:Redis 是基于內存的數據庫,而直接訪問內存的速度要比訪問磁盤高上幾個數量級。這是 Redis 快最主要的原因。
- 從設計上看:Redis 在架構上采用了 IO 多路復用提高了資源利用率,通過多線程非阻塞式 IO 提高請求的處理效率 ,使用單線程執行大部分命令以避免上下文切換,部分重命令則允許異步執行,并且在設計上針對最底層的數據結構進行了精細的優化,以保證任何操作都具備盡可能低的復雜度。
- 從使用方式上看:Redis 的功能非常純粹,用戶直接面向經過精心設計的數據結構進行操作,因此效率極高,此外,用戶還可以根據自己的業務場景采用最合適的數據結構,這也間接提高了操作效率。
問題詳解
1. 基于內存操作
Redis 是基于內存操作的數據庫,這是它快的最根本原因。
一般情況下,計算機訪問一次 SSD 硬盤大概需要 50 ~ 150 納秒,如果是傳統的硬盤時間則需要 1 ~ 10 毫秒,而訪問一次內存僅需要 120 微秒。可見,磁盤和內存訪問的速度最少差了一千倍。
除了少數一些需要需要跟磁盤打交道的時候(比如持久化),大部分時候 Redis 都只在內存中進行讀寫,因此它的效率極高。
2. 合理的線程模型
我們一般說“Redis”是單線程的,實際上是指 Redis 使用單個主線程來執行大部分的命令。這個設計使得 Redis 可以避免因頻繁的上下文切換以及各種同步機制帶來的性能開銷,同上也避免的了為了保證數據結構支持并發操作而引入的代碼復雜度。
不過,Redis 也并非真的就是單線程的,從 4.0 開始,Redis 就引入了 UNLINK 這類命令,用于異步執行刪除等重操作,并在 6.0 以后引入了專門的 IO 線程,實現了多線程的非阻塞式 IO,它們也進一步的提升了 Redis 的執行效率。
這里我們要順帶強調一下,雖然 Redis 的單個主線程模型確實帶來的不少的好處,但是這個設計更多的還是在性能與設計之間取得的一個平衡。實際上不少市面上開源或者大公司內部自研的 KV 數據庫 —— 比如 KeyDB 或者 Dragonfly —— 都是基于多線程模型實現的,它們以單機模式運行在多核機器上時也確實表現出了比 Redis 更高的性能。
3. 高效的 IO 模型
3.1. IO 多路復用

為了提高資源利用率,提高服務吞吐量,Redis 在內部實現了一套網絡事件庫,它支持基于 Solaris 中的 evport、Linux 中的 epoll、Mac OS/FreeBSD 中的 kQueue ……等操作系統函數實現高效的 IO 多路復用。
在這個模型中,它將會來自客戶端的網絡請求作為一個事件發布到隊列中,然后線程將同步的獲取事件并派發到不同的處理器,而處理器處理完畢后又會再發布另一個事件……整個主流程都由 Redis 的主線程在一個不間斷的循環中完成,這就是事件循環。
熟悉 Netty 的同學可能會覺得有點既視感,因為兩者都可以認為基于反應器模式實現的 IO 模型,不過 Netty 可以有多個事件循環,并且還可以劃分為 Boss 和 Worker 兩類事件循環組,而 Redis 只有一個事件循環,并且在早期版本只有一個 IO 線程(也就是主線程本身)。
3.2. 多線程非阻塞式 IO
隨著請求規模的擴大,單個線程在網絡 IO 上消耗的 CPU 時間越來越多,它逐漸成為了 Redis 的性能瓶頸。因此在 6.0 版及以上版本,Redis 正式引入了多線程來處理網絡 IO。
在新的版本中,Redis 依然使用單個主線程來執行命令,但是使用多個線程來處理 IO 請求,主線程不再負責包括建立連接、讀取數據和回寫數據這些事情,而只是專注于執行命令。

這個做法在保證單注線程設計的原有優點的情況下,又進一步提高了網絡 IO 的處理效率。
4. 數據結構
Redis 的高性能很大程度上依賴于它豐富而高效的數據結構,而它們在底層實現上,都針對不同的使用場景進行了精心的設計和優化。
Redis 共有 5 種基本數據類型:String(字符串)、List(列表)、Set(集合)、Hash(散列)、Zset(有序集合)。
這 5 種數據類型是直接提供給用戶使用的,是數據的保存形式,其底層實現主要依賴這 8 種數據結構:簡單動態字符串(SDS)、LinkedList(雙向鏈表)、Dict(哈希表/字典)、SkipList(跳躍表)、Intset(整數集合)、ZipList(壓縮列表)、QuickList(快速列表)。
| 數據類型 | 底層數據結構 | 應用場景 |
|---|---|---|
| String | SDS | 它可以存儲任何數據 - 字符串、整數、浮點值、JPEG 圖像、序列化的 Ruby 對象或您希望它承載的任何其他內容 |
| List 列表 | LinkedList/ZipList/QuickList | Redis Lists 保存按插入順序排序的字符串元素集合。從兩端推送或彈出項目、根據偏移量進行修剪、讀取單個或多個項目,或按值和位置查找或刪除項目。您還可以對異步消息傳輸進行阻塞調用。 |
| Set 集 | Dict(哈希表/字典)、Intset | Redis Sets 數據結構存儲一組唯一的成員。使用 Set,您可以添加、獲取或刪除成員、檢查成員資格或檢索隨機成員。使用排序算法,您還可以執行集合運算(例如交集、并集和集合差集)并計算集合基數 |
| Sorted Set | ZipList(壓縮列表)、SkipList(跳表) | Redis 有序集包含一組按浮點分數排序的唯一>成員。與集合一樣,您可以添加、獲取或刪除單個成員,并執行集合運算,例如并集、交集、集合差集和計算基數。此外,您還可以根據分數或成員值查詢集合,聚合、過濾和排序結果。 |
| Hash 散 列 | Dict(哈希表/字典)、ZipList(跳表) | Redis 哈希是一種數據類型,表示字符串字段和字符串值之間的映射。Redis 哈希結構存儲一組字段值對,不會占用太多空間,因此非常適合表示數據對象。它提供了添加、獲取或刪除單個項目、獲取整個哈希或使用哈希中的一個或多個字段作為計數器的功能。 |
| Stream | redis流 | 用于管理高速數據流(如消息隊列)。憑借現成的分區、復制和持久性,它可以以亞毫秒級的延遲每秒捕獲和處理數百萬個數據點。 |
| Bitmap | 位圖 | 它提供命令來獲取和設置給定位置的位值,并在多個位圖鍵之間執行 AND、OR、XOR 和 NOT 運算 |
| Bitfield | 位域 | 它允許在給定位置增加和減少計數器,并且當計數器達到其上限時會標記溢出。 |
| Geospatial | 地理空間索引 | 附近的人 |
| HyperLogLog | 基數統計,熱門網站活躍ip統計 |

浙公網安備 33010602011771號