目錄
1.Redis數據庫簡介
-
Redis是一個非常快速的、開源的、使用ANSI C語言編寫、支持網絡、可基於內存亦可持久化的日誌型、非關係類型的、Key-Value數據庫,並提供多種語言的API。
-
Redis 是一個高性能的key-value數據庫。 redis的出現,很大程度補償了memcached這類key/value存儲的不足,在部分場合可以對關係數據庫起到很好的補充作用。它提供了Java,C/C++,C#,PHP,JavaScript,Perl,Object-C,Python,Ruby,Erlang等客戶端,使用很方便。
-
Redis支持主從同步。數據可以從主服務器向任意數量的從服務器上同步,從服務器可以是關聯其他從服務器的主服務器。這使得Redis可執行單層樹複製。存盤可以有意無意的對數據進行寫操作。由於完全實現了發佈/訂閱機制,使得從數據庫在任何地方同步樹時,可訂閱一個頻道並接收主服務器完整的消息發佈記錄。同步對讀取操作的可擴展性和數據冗餘很有幫助。
2.Redis主要特點
- 高級數據結構:爲值提供五種可能的數據類型:字符串,列表,集合,哈希和有序集合。提供了這些數據類型獨有的操作,並且具有記錄良好的時間複雜度(Big O表示法)。
- 高性能:由於其內存特性,項目維護者將複雜性保持在最低限度的承諾以及基於事件的編程模型,Redis 在讀寫操作方面具有出色的性能。
- 沒有依賴關係的輕量級:用ANSI C編寫,沒有外部依賴關係。適用於所有POSIX環境。Windows不受官方支持,但Microsoft提供了實驗性版本。
- 高可用性: 內置支持異步,非阻塞,主/從複製,以確保數據的高可用性。目前有一種名爲Redis Sentinel的高可用性解決方案目前可以使用,但仍被視爲正在進行的工作。、
以上術語來自於PHP中文網
3. Redis數據庫的安裝以及配置
-
Redis數據庫使用的最佳環境是Linux和MAC系統,但是官方在github上也提供了相應的window64位的Redis數據庫安裝壓縮包
-
地址如下,下載Redis數據庫
-
進入GitHub主頁後繼續往下翻,直到看到
-
window系統的選擇第二個壓縮包進行下載,後面兩個都是Redis的底層數據庫源碼,深入學習可以使用。
-
解壓後的文件如圖
-
接下來,打開cmd命令行,進入Redis的下載目錄,如圖輸入
如圖表示安裝成功,接下來來體驗Redis數據庫的基本功能
- 首先上述中開啓Redis服務器的cmd窗口不能關閉,否則就無法訪問服務端了。
- 打開另外一個cmd窗口,進入Redis數據庫的下載目錄,輸入
redis-cli.exe -h 127.0.0.1 -p 6379
- 如圖則是成功下載了Redis數據庫。
以下博客內容總結來源於老錢的Redis深度歷險一書
4. Redis的四種基本的數據結構
- string字符串
- list列表
- set集合
- hash哈希
- zset有序集合
4.1 string字符串
-
string表示的是一個可變的字節數組,可以進行字符串初始化,獲取字符串的長度,獲取字符串的子串,覆蓋字符串內容,追加子串等等等。
-
Redis的字符串是動態字符串,是可以修改的字符串,內部結構實現上類似於Java的ArrayList,採用預分配冗餘空間的方式來減少內存的頻繁分配,如圖中所示,內部爲當前字符串實際分配的空間capacity一般要高於實際字符串長度len。當字符串長度小於1M時,擴容都是加倍現有的空間,如果超過1M,擴容時一次只會多擴1M的空間。需要注意的是字符串最大長度爲512M。
此段話來自這篇博客戳這裏
設置字符串內容
獲取字符串的內容
mset 批量設置字符串的內容
mget 批量獲取字符串的內容
獲取字符串的長度
獲取子串
覆蓋子串
追加子串
還可以對字符串轉化爲整數以及進行加減處理
incr a
等價於incrby a 1
decr a
等價於decrby a 1
字符串轉化爲整數也是有範圍的不可以超過Long.Max,不可以低於Long.Min。
變量的過期和刪除
- 字符串刪除使用del指令主動刪除,使用expire指令設置過期時間,到時間自動刪除。
過十秒後
如何獲取變量的壽命呢?使用ttl就可以
ttl後是6,表示還剩六秒的壽命。
del刪除簡單,直接del 變量名
setex name 5 codehole
等價於set+expire,定義變量同時又設置過期時間。
setnx name codehole
codehole變量如果存在則創建失敗,否則創建成功。
4.2 list列表
-
Redis中的列表的存儲結構是雙向鏈表,利於首尾插入刪除,不利於定位操作
-
可以有負下標,鏈表元素的位置爲0,1,2,3,,,,n-1,還可以表示爲-1,-2,-3,,,,-n。-1表示倒數第一個元素對應第n-1個元素。
-
隊列或者堆棧,鏈表可以從表頭或者表尾插入或者刪除元素,類似與隊列和棧的概念。使用rpush或者rpop或者lpush或者lpop四條,可以將鏈表用作隊列或者堆棧。
右進左出
左進右出
其實還是很好區分的,lpush可以看作left push就是左進入啦。
日常中列表作爲異步隊列使用,將需要延後處理的任務結構體序列化成字符串塞進 Redis 的列表,另一個線程從這個列表中輪詢數據進行處理
列表的長度使用llen指令
隨機讀
- 使用lindex指令訪問指定位置的元素,使用lrange指令獲取鏈表子元素列表,提供start和end下標(包括end的)。
修改元素
- 使用lset指令在指定位置修改元素
插入元素
- 使用linsert指令在列表中插入元素,在linsert指令裏增加了方向參數before/after來顯示指示前置和後置插入。不過讓人意想不到的是linsert指令並不是通過指定位置來插入,而是通過指定具體的值。這是因爲在分佈式環境下,列表的元素總是頻繁變動的,意味着上一時刻計算的元素下標在下一時刻可能就不是你所期望的下標了。
刪除元素
- 列表的刪除操作也不是通過指定下標來確定元素的,你需要指定刪除的最大個數以及元素的值。
定長列表
- 在實際應用場景中,我們有時候會遇到「定長列表」的需求。比如要以走馬燈的形式實時顯示中獎用戶名列表,因爲中獎用戶實在太多,能顯示的數量一般不超過100條,那麼這裏就會使用到定長列表。維持定長列表的指令是ltrim,需要提供兩個參數start和end,表示需要保留列表的下標範圍,範圍之外的所有元素都將被移除。
- 維持定長列表的指令是ltrim,需要提供兩個參數start和end,表示需要保留列表的下標範圍,如果指定參數的end對應的真實下標小於start,其效果等價於del,因爲這樣的參數表示需要保留列表元素的下標範圍爲空。
快速列表
深入一點,Redis 底層存儲的不是一個簡單的 linkedlist,而是稱之爲
快速鏈表 quicklist 的一個結構。首先在列表元素較少的情況下會使用一塊連續的內存存儲,這個結構是 ziplist,也即是壓縮列表。它將所有的元素緊挨着一起存儲,分配的是一塊連續的內存。當數據量比較多的時候纔會改成 quicklist。因爲普通的鏈表需要的附加指針空間太大,會比較浪費空間,而且會加重內存的碎片化。比如這個列表裏存的只是 int 類型的數據,結構上還需要兩個額外的指針 prev 和 next 。所以 Redis 將鏈表和 ziplist 結合起來組成了 quicklist。也就是將多個ziplist 使用雙向指針串起來使用。這樣既滿足了快速的插入刪除性能,又不會出現太大的空間冗餘。
4.3 hash哈希
- 哈希等價於Java語言的HashMap,在實現結構上它使用二維結構,第一維是數組,第二維是鏈表,hash的內容key和value存放在鏈表中,數組裏存放的是鏈表的頭指針。通過key查找元素時,先計算key的hashcode,然後用hashcode對數組的長度進行取模定位到鏈表的表頭,再對鏈表進行遍歷獲取到相應的value值,鏈表的作用就是用來將產生了「hash碰撞」的元素串起來。Java語言開發者會感到非常熟悉,因爲這樣的結構和HashMap是沒有區別的。哈希的第一維數組的長度也是2^n。
增加元素
- 使用hset一次增加一個鍵值對,也可以使用hmset一次增加多個鍵值對
獲取元素
- 可以通過hget定位具體key對應的value,可以通過hmget獲取多個key對應的value,可以使用hgetall獲取所有的鍵值對,可以使用hkeys和hvals分別獲取所有的key列表和value列表,這些操作和java語言的Map接口時類似的。
刪除元素
- 可以使用hdel刪除指定key,hdel支持同時刪除多個key
刪除的是鍵
判斷元素是否存在
- 使用hget獲得key對應的value是否爲空就直到對應的元素是否存在了,不過如果value的字符串長度特別大,通過這種方式來判斷元素存在與否就略顯浪費,這時可以使用hexists指令。
1表示爲存在。
計數器
- hash結構還可以當成計數器來使用,對於內部的每一個key都可以作爲獨立的計數器。如果value值不是整數,調用hincrby指令會出錯。
擴容
- 當hash內部的元素比較擁擠時(hash碰撞比較頻繁),就需要進行擴容。擴容需要申請新的兩倍大小的數組,然後將所有的鍵值對重新分配到新的數組下標對應的鏈表中(rehash)。如果hash結構很大,比如有上百萬個鍵值對,那麼一次完整rehash的過程就會耗時很長。這對於單線程的Redis裏來說有點壓力山大。所以Redis採用了漸進式rehash的方案。它會同時保留兩個新舊hash結構,在後續的定時任務以及hash結構的讀寫指令中將舊結構的元素逐漸遷移到新的結構中。這樣就可以避免因擴容導致的線程卡頓現象。
縮容
- Redis的hash結構不但有擴容還有縮容,從這一點出發,它要比Java的HashMap要厲害一些,Java的HashMap只有擴容。縮容的原理和擴容是一致的,只不過新的數組大小要比舊數組小一倍。
4.4 set集合
- Java程序員都知道HashSet的內部實現使用的是HashMap,只不過所有的value都指向同一個對象。Redis的set結構也是一樣,它的內部也使用hash結構,所有的value都指向同一個內部值。
增加元素
- 可以增加多個元素
讀取元素
- 使用smembers列出所有元素,使用scard獲取集合長度,使用srandmember獲取隨機count個元素,如果不提供count參數,默認爲1。
刪除元素
- 使用srem刪除一個到多個元素,使用spop刪除隨機一個元素
判斷元素是否存在
- 使用sismember指令,只能接受單個元素
4.5 sortedset有序集合
-
SortedSet(zset)是Redis提供的一個非常特別的數據結構,一方面它等價於Java的數據結構Map<String, Double>,可以給每一個元素value賦予一個權重score,另一方面它又類似於TreeSet,內部的元素會按照權重score進行排序,可以得到每個元素的名次,還可以通過score的範圍來獲取元素的列表。
-
zset底層實現使用了兩個數據結構,第一個是hash,第二個是跳躍列表,hash的作用就是關聯元素value和權重score,保障元素value的唯一性,可以通過元素value找到相應的score值。跳躍列表的目的在於給元素value排序,根據score的範圍獲取元素列表。
增加元素
- 通過zadd指令可以增到一到多個value/score對,score放在前面
長度
刪除元素
計數器實現
- 和hash一樣zset也可以作爲計數器使用。
獲取排名和分數
- 通過zscore指令獲取指定元素的權重,通過zrank指令獲取指定元素的正向排名,通過zrevrank指令獲取指定元素的反向排名[倒數第一名]。正向是由小到大,負向是由大到小。
根據排名範圍獲取元素列表
- 通過zrange指令指定排名範圍參數獲取對應的元素列表,攜帶withscores參數可以一併獲取元素的權重。通過zrevrange指令按負向排名獲取元素列表[倒數]。正向是由小到大,負向是由大到小。
根據score範圍獲取列表
- 通過zrangebyscore指令指定score範圍獲取對應的元素列表。通過zrevrangebyscore指令獲取倒排元素列表。正向是由小到大,負向是由大到小。參數-inf表示負無窮,+inf表示正無窮。
根據範圍移除元素列表,可以通過排名範圍,也可以通過score範圍來一次性移除多個元素
跳躍列表
-
zset內部的排序功能是通過「跳躍列表」數據結構來實現的,它的結構非常特殊,也比較複雜。
因爲zset要支持隨機的插入和刪除,所以它不好使用數組來表示。我們先看一個普通的鏈表結構。 -
我們需要這個鏈表按照score值進行排序。這意味着當有新元素需要插入時,需要定位到特定位置的插入點,這樣纔可以繼續保證鏈表是有序的。通常我們會通過二分查找來找到插入點,但是二分查找的對象必須是數組,只有數組纔可以支持快速位置定位,鏈表做不到,那該怎麼辦?
-
想想一個創業公司,剛開始只有幾個人,團隊成員之間人人平等,都是聯合創始人。隨着公司的成長,人數漸漸變多,團隊溝通成本隨之增加。這時候就會引入組長制,對團隊進行劃分。每個團隊會有一個組長。開會的時候分團隊進行,多個組長之間還會有自己的會議安排。公司規模進一步擴展,需要再增加一個層級——部門,每個部門會從組長列表中推選出一個代表來作爲部長。部長們之間還會有自己的高層會議安排。
-
跳躍列表就是類似於這種層級制,最下面一層所有的元素都會串起來。然後每隔幾個元素挑選出一個代表來,再將這幾個代表使用另外一級指針串起來。然後在這些代表裏再挑出二級代表,再串起來。最終就形成了金字塔結構。
-
想想你老家在世界地圖中的位置:亞洲–>中國->安徽省->安慶市->樅陽縣->湯溝鎮->田間村->xxxx號,也是這樣一個類似的結構。
-
「跳躍列表」之所以「跳躍」,是因爲內部的元素可能「身兼數職」,比如上圖中間的這個元素,同時處於L0、L1和L2層,可以快速在不同層次之間進行「跳躍」。
-
定位插入點時,先在頂層進行定位,然後下潛到下一級定位,一直下潛到最底層找到合適的位置,將新元素插進去。你也許會問那新插入的元素如何纔有機會「身兼數職」呢?
-
跳躍列表採取一個隨機策略來決定新元素可以兼職到第幾層,首先L0層肯定是100%了,L1層只有50%的概率,L2層只有25%的概率,L3層只有12.5%的概率,一直隨機到最頂層L31層。絕大多數元素都過不了幾層,只有極少數元素可以深入到頂層。列表中的元素越多,能夠深入的層次就越深,能進入到頂層的概率就會越大。
5. 容器型數據結構
list/set/hash/zset 這四種數據結構是容器型數據結構,它們共享下面兩條通用規則:
1、create if not exists
如果容器不存在,那就創建一個,再進行操作。比如 rpush 操作剛開始是沒有列表的,
2、drop if no elements
如果容器裏元素沒有了,那麼立即刪除元素,釋放內存。這意味着 lpop 操作到最後一個元素,列表就消失了。
6. 過期時間
Redis 所有的數據結構都可以設置過期時間,時間到了,Redis 會自動刪除相應的對象。
需要注意的是過期是以對象爲單位,比如一個 hash 結構的過期是整個 hash 對象的過期,而不是其中的某個子 key,還有一個需要特別注意的地方是如果一個字符串已經設置了過期時間,然後你調用了
set 方法修改了它,它的過期時間會消失。