Redis高效率原因及數(shù)據(jù)結(jié)構(gòu)分析
1、什么是redis?它主要用來干什么的?
Redis,英文全稱是Remote Dictionary Server(遠程字典服務(wù)),是一個開源的使用ANSI C語言編寫、支持網(wǎng)絡(luò)、可基于內(nèi)存亦可持久化的日志型、Key-Value數(shù)據(jù)庫,并提供多種語言的API。
與MySQL數(shù)據(jù)庫不同的是,Redis的數(shù)據(jù)是存在內(nèi)存中的。它的讀寫速度非???,每秒可以處理超過10萬次讀寫操作。因此redis被廣泛應(yīng)用于緩存,另外,Redis也經(jīng)常用來做分布式鎖。除此之外,Redis支持事務(wù)、持久化、LUA 腳本、LRU 驅(qū)動事件、多種集群方案。
知道redis是什么后,接下來我們來說一說redis為什么這么快。
2、redis為什么這么快?
我們來一個一個說明!
基于內(nèi)存存儲實現(xiàn)
計算機專業(yè)的同學我們都知道內(nèi)存讀寫是要比磁盤快很多的,Redis是基于內(nèi)存實現(xiàn)的數(shù)據(jù)庫,相對于數(shù)據(jù)存在磁盤的mysql等數(shù)據(jù)庫,省去了磁盤I/O的消耗。
高效的數(shù)據(jù)結(jié)構(gòu)
我們都知道,mysql索引為了提高效率,選擇了B+樹的數(shù)據(jù)結(jié)構(gòu),對于一個應(yīng)用場景來說合理的數(shù)據(jù)結(jié)構(gòu)可以讓你的應(yīng)用或者程序更快。我們來看看Redis的數(shù)據(jù)結(jié)構(gòu)–內(nèi)部編碼圖:
String
: 動態(tài)字符串SDS
List
: 雙端鏈表LinkedList+壓縮鏈表ziplist
Hash
: 壓縮鏈表ziplist+字典哈希表hashtable
Set
: hashtable(+inset)
Zset
: 壓縮鏈表ziplist+跳表skiplist
我們來說一說這幾種內(nèi)部編碼:
1、SDS簡單動態(tài)字符串
我們來和C語言中的char[ ]對比下
字符串長度處理: Redis獲取字符串長度,時間復(fù)雜度為O(1),而C語言中,需要從頭遍歷,復(fù)雜度為O(N)。
空間預(yù)分配: 字符串修改越頻繁的話,內(nèi)存分配就越頻繁,就會很消費性能,而SDS修改和空間擴充,會額外分配未使用的空間,減少性能損耗。
惰性空間釋放: SDS縮短時,不是回收多余的內(nèi)存空間,而是free記錄下多余的空間,后續(xù)有變更,直接使用free中記錄的空間,減少分配。
二進制安全: Redis可以存儲一些二進制數(shù)據(jù),在C語言中字符串遇到'/0'會結(jié)束,而SDS中標志字符串結(jié)束的是len屬性。
2、字典
Redis 作為 K-V 型內(nèi)存數(shù)據(jù)庫,所有的鍵值就是用字典來存儲。字典就是哈希表,比如HashMap,通過key就可以直接獲取到對應(yīng)的value。而哈希表的特性,在O(1)時間復(fù)雜度就可以獲得對應(yīng)的值。
3、跳表
跳表是Redis特有的數(shù)據(jù)結(jié)構(gòu),就是在鏈表的基礎(chǔ)上,增加多級索引提升查找效率。
跳表支持平均O(logN),最壞O(N)復(fù)雜度的節(jié)點查找,還可以通過順序性操作。
合理的數(shù)據(jù)編碼
Redis 支持多種數(shù)據(jù)數(shù)據(jù)類型,每種基本類型,可能對多種數(shù)據(jù)結(jié)構(gòu)。什么時候,使用什么樣數(shù)據(jù)結(jié)構(gòu),使用什么樣編碼,是redis設(shè)計者總結(jié)優(yōu)化的結(jié)果。
String: 如果存儲數(shù)字的話,是用int類型的編碼;如果存儲非數(shù)字,小于等于39字節(jié)的字符串,是embstr;大于39個字節(jié),則是raw編碼。
List: 如果列表的元素個數(shù)小于512個,列表每個元素的值都小于64字節(jié)(默認),使用ziplist編碼,否則使用linkedlist編碼
Hash: 哈希類型元素個數(shù)小于512個,所有值小于64字節(jié)的話,使用ziplist編碼,否則使用hashtable編碼。
Set: 如果集合中的元素都是整數(shù)且元素個數(shù)小于512個,使用intset編碼,否則使用hashtable編碼。
Zset: 當有序集合的元素個數(shù)小于128個,每個元素的值小于64字節(jié)時,使用ziplist編碼,否則使用skiplist(跳躍表)編碼。
合理的線程模型
1、I/O多路復(fù)用
多路I/O復(fù)用技術(shù)可以讓單個線程高效的處理多個連接請求,而Redis使用用epoll作為I/O多路復(fù)用技術(shù)的實現(xiàn)。并且,Redis自身的事件處理模型將epoll中的連接、讀寫、關(guān)閉都轉(zhuǎn)換為事件,不在網(wǎng)絡(luò)I/O上浪費過多的時間。
2、什么是I/O多路復(fù)用?
I/O : 網(wǎng)絡(luò) I/O
多路 : 多個網(wǎng)絡(luò)連接
復(fù)用: 復(fù)用同一個線程。
IO多路復(fù)用其實就是一種同步IO模型,它實現(xiàn)了一個線程可以監(jiān)視多個文件句柄;一旦某個文件句柄就緒,就能夠通知應(yīng)用程序進行相應(yīng)的讀寫操作;而沒有文件句柄就緒時,就會阻塞應(yīng)用程序,交出cpu。
3、單線程模型
Redis是單線程模型的,而單線程避免了CPU不必要的上下文切換和競爭鎖的消耗。也正因為是單線程,如果某個命令執(zhí)行過長(如hgetall命令),會造成阻塞。Redis是面向快速執(zhí)行場景的數(shù)據(jù)庫。,所以要慎用如smembers和lrange、hgetall等命令。
Redis 6.0 引入了多線程提速,它的執(zhí)行命令操作內(nèi)存的仍然是個單線程。
虛擬內(nèi)存機制
redis直接自己構(gòu)建了VM機制,不會像一般的系統(tǒng)會調(diào)用系統(tǒng)函數(shù)處理,會浪費一定的時間去移動和請求。
Redis的虛擬內(nèi)存機制是啥呢?
虛擬內(nèi)存機制就是暫時把不經(jīng)常訪問的數(shù)據(jù)(冷數(shù)據(jù))從內(nèi)存交換到磁盤中,從而騰出寶貴的內(nèi)存空間用于其它需要訪問的數(shù)據(jù)(熱數(shù)據(jù))。通過VM功能可以實現(xiàn)冷熱數(shù)據(jù)分離,使熱數(shù)據(jù)仍在內(nèi)存中、冷數(shù)據(jù)保存到磁盤。這樣就可以避免因為內(nèi)存不足而造成訪問速度下降的問題。
以上就是Redis高效原因及數(shù)據(jù)結(jié)構(gòu)分析的詳細內(nèi)容,更多關(guān)于Redis的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
redis使用不當導(dǎo)致應(yīng)用卡死bug的過程解析
本文主要記一次找因redis使用不當導(dǎo)致應(yīng)用卡死bug的過程,文中通過示例代碼介紹的非常詳細,需要的朋友們下面隨著小編來一起學習學習吧2021-07-07聊聊使用RedisTemplat實現(xiàn)簡單的分布式鎖的問題
這篇文章主要介紹了使用RedisTemplat實現(xiàn)簡單的分布式鎖問題,文中給大家介紹在SpringBootTest中編寫測試模塊的詳細代碼,需要的朋友可以參考下2021-11-11高并發(fā)下Redis如何保持數(shù)據(jù)一致性(避免讀后寫)
本文主要介紹了高并發(fā)下Redis如何保持數(shù)據(jù)一致性(避免讀后寫),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-03-03