1. redis是什麼html
2. 爲何用redisredis
3. redis 數據結構算法
4. redis中的對象類型數據庫
5. redis都能作什麼?怎麼實現的的?數組
6. redis使用過程當中須要注意什麼緩存
7. 數據持久化安全
8. 集羣是怎麼訪問的服務器
9. redis單線程是什麼鬼網絡
10. 過時策略數據結構
11. 內存淘汰策略
12. 什麼狀況下不適合用redis
13. 運維工具:怎麼樣快速定位問題
14. 同類的產品有哪些
1. redis是什麼
Redis(Remote Dictionary Server)是一個由Salvatore Sanfilippo寫的key-value存儲系統。是一個開源(BSD許可)的,內存中的數據結構存儲系統,它能夠用做數據庫、緩存和消息中間件。 它支持多種類型的數據結構,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 與範圍查詢, bitmaps, hyperloglogs 和 地理空間(geospatial) 索引半徑查詢。 Redis 內置了 複製(replication),LUA腳本(Lua scripting), LRU驅動事件(LRU eviction),事務(transactions) 和不一樣級別的 磁盤持久化(persistence), 並經過 Redis哨兵(Sentinel)和自動 分區(Cluster)提供高可用性(high availability)。
2. 爲何用redis
1)速度快,徹底基於內存,使用C語言實現,網絡層使用epoll解決高併發問題,單線程模型避免了沒必要要的上下文切換及競爭條件;
注意:單線程僅僅是說在網絡請求這一模塊上用一個線程處理客戶端的請求,像持久化它就會重開一個線程/進程去進行處理
2)豐富的數據類型,Redis有8種數據類型,固然經常使用的主要是 String、Hash、List、Set、 SortSet 這5種類型,他們都是基於鍵值的方式組織數據。
每一種數據類型提供了很是豐富的操做命令,能夠知足絕大部分需求,若是有特殊需求還能本身經過 lua 腳本本身建立新的命令(具有原子性)
3)除了提供的豐富的數據類型,Redis還提供了像慢查詢分析、性能測試、Pipeline、事務、Lua自定義命令、Bitmaps、HyperLogLog、發佈/訂閱、
Geo等個性化功能。
4)Redis的代碼開源在GitHub,代碼很是簡單優雅,任何人都可以吃透它的源碼;它的編譯安裝也是很是的簡單,沒有任何的系統依賴;有很是活躍的社區,
各類客戶端的語言支持也是很是完善。
5)支持事務(沒用過)、持久化、主從複製讓高可用、分佈式成爲可能。
3. redis 數據結構:
1)簡單動態字符串
2)鏈表:鏈表提供了高效的節點重排能力,以及順序性的節點訪問方式,而且能夠經過增刪節點來靈活地調整鏈表的長度。
鏈表被普遍用於redis的各類功能,好比列表鍵、發佈與訂閱、慢查詢、監視器等
3)字典:又稱爲符號表,關聯數組或映射,是一種用於保存鍵值對的抽象數據結構。
redis的字典使用哈希表做爲底層實現,一個哈希表裏面能夠有多個哈希表節點,而每一個哈希表節點就保存了字典中的一個鍵值對。
字典被普遍用於實現Redis的各類功能,其中包括數據庫和哈希鍵。
4)跳躍表:跳躍表是以各類有序數據結構,它經過在每一個節點中維持多個指向其餘節點的指針,從而達到快速訪問節點的目的。
redis只有兩個地方用到跳躍表:一個是實現有序集合鍵,另外一個是在集羣節點中用做內部數據結構。
5)整數集合:整數集合是集合鍵的底層實現之一,當一個集合只包含整數值元素,而且這個集合的元素數量很少時,Redis就會使用整數集合做爲集合鍵的底層實現。
整數集合能夠保存的類型爲:int16_t,int32_t,int64_t 的整數值,而且保證集合中不會出現重複元素。
當咱們要將一個新元素添加到整數集合裏面,而且新元素的類型比整數集合現有全部元素的類型都要長時,整數集合須要先進行升級,
而後才能將新元素添加到整數集合裏面。整數集合只支持升級操做,不支持降級操做。
4. redis中的對象類型
Redis並無使用以前介紹的數據結構來實現鍵值對數據庫,而是基於那些數據結構建立了一個對象系統,這個系統包含字符串對象、列表對象、哈希對象、集合對象和有序集合對象這五種類型對象。每種類型的對象至少都有兩種或者以上的編碼方式,不一樣的編碼能夠在不一樣的使用場景上優化對象的使用效率。
Redis會共享值爲0到9999的字符串對象。Redis只對包含整數值的字符串對象進行共享。對象會記錄本身的最後一個被訪問的時間,這個時間能夠用於計算對象的空轉時間,用以判斷回收內存。Redis中的每一個對象都由一個redisObject結構表示,該結構中和保存數據相關的三個屬性分別是type屬性,encoding屬性和ptr(pointer ,指針)屬性。
1)String(字符串):字符串的編碼能夠是int,raw 或者 embstr(短字符串)。在條件知足的狀況下,int和embstr會被轉換成raw編碼的字符串對象。
2)List(列表):列表對象的編碼能夠是ziplist(壓縮列表)或者linkedlist(雙端列表)
3)Hash(哈希):哈希對象的編碼能夠是ziplist(壓縮列表)或者hashtable(字典)
4)Set(集合):集合對象的編碼能夠是intset(整數集合)或者hashtable(字典)
5)ZSet(有序集合):有序集合對象,編碼能夠是ziplist(壓縮列表)或者skiplist(字典+跳躍表,使用兩種結構更高效)
6)Bitmaps(位圖):BitMap 就是經過一個 bit 位來表示某個元素對應的值或者狀態, 其中的 key 就是對應元素自己,實際上底層也是經過對字符串的操做來實現。
bitmaps通常的使用場景:
a. 各類實時分析.
b. 存儲與對象ID關聯的節省空間而且高性能的布爾信息.
7)HyperLogLog:
8)Geo(地理位置信息)
5. redis都能作什麼?怎麼實現的的?
1)緩存
a. 數據和緩存的操做時序,結論是清楚的:先淘汰緩存,再寫數據庫,再淘汰緩存。
緣由:
假設先寫數據庫,再淘汰緩存:第一步寫數據庫操做成功,第二步淘汰緩存失敗,則會出現DB中是新數據,Cache中是舊數據,數據不一致。
假設先淘汰緩存,再寫數據庫:第一步淘汰緩存成功,第二步寫數據庫失敗,則只會引起一次Cache miss。
第二次淘汰緩存的目的是避免寫數據庫期間,有新的緩存添加。固然,若是這種狀況下,二次淘汰緩存失敗,仍然有可能有髒數據……
b. 更新緩存 VS 淘汰緩存
淘汰緩存,避免兩個併發寫操做,致使髒數據
而且淘汰緩存操做簡單,而且帶來的反作用只是增長了一次cache miss,建議做爲通用的處理方式。
參見:緩存架構設計細節二三事
2)分佈式鎖:
建議經過引入Redisson相關jar包使用。
加鎖注意:線程惟一標記(本身加的鎖,只能本身解,只能解本身加的鎖),過時時間(避免系統崩潰後造成死鎖),沒有已存在的鎖才能加鎖成功
解鎖注意:只能解本身的鎖,不能直接使用del,避免鎖過時刪除了別人的鎖(鎖過時致使)。因此通常使用lua腳本執行,保證原子性。
參見:https://my.oschina.net/dengfuwei/blog/1604975
http://www.importnew.com/27477.html
3)排行榜(List/Set):
a. 獲取排名和分數 經過zscore指令獲取指定元素的權重,經過zrank指令獲取指定元素的正向排名,經過zrevrank指令獲取指定元素的反向排名[倒數第一名]。
正向是由小到大,負向是由大到小。
b. 根據排名範圍獲取元素列表 經過zrange指令指定排名範圍參數獲取對應的元素列表,攜帶withscores參數能夠一併獲取元素的權重。經過zrevrange指令
按負向排名獲取元素列表[倒數]。正向是由小到大,負向是由大到小。
c. 根據score範圍獲取列表 經過zrangebyscore指令指定score範圍獲取對應的元素列表。經過zrevrangebyscore指令獲取倒排元素列表。正向是由小到大,
負向是由大到小。參數-inf
表示負無窮,+inf
表示正無窮。
4)計數器/限速器(統計播放數據/瀏覽量/在線人數等):
a. 字符串能夠做爲計數器:INCRBY(整數加法),DECRBY(整數減法)
b. hash結構也能夠當成計數器來使用,對於內部的每個key均可以做爲獨立的計數器。若是value值不是整數,調用hincrby指令會出錯。
利用Redis中原子性的自增操做,咱們能夠統計相似用戶點贊數、用戶訪問數等,這類操做若是用MySQL,頻繁的讀寫會帶來至關大的壓力;限速器比較典型的
使用場景是限制某個用戶訪問某個API的頻率,經常使用的有搶購時,防止用戶瘋狂點擊帶來沒必要要的壓力;
5)好友關係(點贊/共同好友)
利用集合(Set)的一些命令,好比求交集、並集、差集等。能夠方便搞定一些共同好友、共同愛好之類的功能;
6)簡單的訂閱消息(訂閱發佈/阻塞隊列):
利用List來實現一個隊列機制,好比:到貨通知、郵件發送之類的需求,不須要高可靠,可是會帶來很是大的DB壓力,徹底能夠用List來完成異步解耦;
7)用戶是否登陸過:
a. Bitmaps的最大優勢就是存儲信息時能夠節省大量的空間。例如在一個系統中,不一樣的用戶被一個增加的用戶ID表示。
40億(2^32=4*1024*1024*1024≈40億
)用戶只須要512M內存就能記住某種信息
8)Session共享
默認Session是保存在服務器的文件中,若是是集羣服務,同一個用戶過來可能落在不一樣機器上,這就會致使用戶頻繁登錄;採用Redis保存Session後,
不管用戶落在那臺機器上都可以獲取到對應的Session信息。
9)分佈式全局惟一id(string)
能夠每次id加1,獲取一個;也能夠一次加100,批量獲取
10)抽獎活動(set)
sadd key {userId} # 參加抽獎活動
smembers key # 獲取全部抽獎用戶,大輪盤轉起來
spop key count # 抽取count名中獎者,並從抽獎活動中移除
srandmember key count # 抽取count名中獎者,不從抽獎活動中移除
6. redis使用過程當中須要注意什麼
1)緩存須要注意什麼
a. 讀多寫少,更新頻率低的時候才使用緩存
b. 先淘汰緩存,再寫數據庫,再淘汰緩存
2)分佈式鎖須要注意什麼
a. 只能本身解鎖,只能解本身的鎖,加過時時間避免死鎖,解鎖時保持原子性
b. 單節點沒法保證高可用;
主從架構可能從庫複製延遲,引發鎖重複(主庫掛了,從庫沒有複製到新鎖);
集羣一樣可能致使鎖重複。
系統GC致使鎖過時,引起鎖重複又該怎麼辦?
分佈式鎖存在的問題,看大神論戰:http://zhangtielei.com/posts/blog-redlock-reasoning.html
3)怎樣避免雪崩
所謂「緩存雪崩「,是指緩存的機器掛了,或者數據未加載到緩存中,或者緩存同一時間大面積的失效,從而致使全部請求都去查數據庫,致使數據庫
CPU和內存負載太高,甚至宕機。
這種問題的解決策略,通常有如下2個方面:
a. 提升緩存的HA。好比緩存的主從複製。
b. 對DB的訪問實行限流、降級。
c. 緩存過時時間。以redis爲例,將過時設置放到1數據庫,真實數據放到0數據庫,key值相同,假設都爲key1。
應用程序首先判斷1庫這條數據是否失效,當1庫標記數據庫數據失效或過時,在1庫中設置新的過時時間。而後從數據庫取數據更新0數據庫中
的數據。若是判斷1數據庫未失效,從0數據庫取出數據返回。
d. 啓動緩存時,進行數據預熱。或者 緩存重啓時,有以前的持久化文件預熱。
4)避免緩存穿透
所謂「緩存穿透「,就是指某個key,先查cache沒查到,再查db也沒有查到。
這種key的存在,會致使cache一直沒辦法命中,壓力一直打在db上面。若是訪問很高頻,可能會壓垮DB。
解決辦法其實也很簡單:當查詢DB沒查到時,往緩存中寫入一個空值(缺省值),這樣第2次再查,就不會打到DB上了。
7. 數據持久化
Redis有兩種持久化的方式:快照(RDB
文件)和追加式文件(AOF
文件):
a. RDB持久化方式會在一個特定的間隔保存那個時間點的一個數據快照。
b. AOF持久化方式則會記錄每個服務器收到的寫操做。在服務啓動時,這些記錄的操做會逐條執行從而重建出原來的數據。寫操做命令記錄的格式跟Redis協議一致,以追加的方式進行保存。
c. Redis的持久化是能夠禁用的,就是說你可讓數據的生命週期只存在於服務器的運行時間裏。
d. 兩種方式的持久化是能夠同時存在的,可是當Redis重啓時,AOF文件會被優先用於重建數據。
參見:http://www.javashuo.com/article/p-rdghwbid-ch.html
8. 集羣是怎麼訪問的
1)Redis 集羣經過分區(partition)來提供必定程度的可用性(availability): 即便集羣中有一部分節點失效或者沒法進行通信, 集羣也能夠繼續處理命令請求。
Redis 集羣提供瞭如下兩個好處:將數據自動切分(split)到多個節點的能力。當集羣中的一部分節點失效或者沒法進行通信時, 仍然能夠繼續處理命令請求的能力。
2)Redis 集羣使用數據分片(sharding)而非一致性哈希(consistency hashing)來實現: 一個 Redis 集羣包含 16384 個哈希槽(hash slot), 數據庫中的每一個鍵都屬於
這 16384 個哈希槽的其中一個, 集羣使用公式 CRC16(key) % 16384 來計算鍵 key 屬於哪一個槽, 其中 CRC16(key) 語句用於計算鍵 key 的 CRC16 校驗和 。
集羣中的每一個節點負責處理一部分哈希槽。 舉個例子, 一個集羣能夠有三個哈希槽, 其中:
a. 節點 A 負責處理 0 號至 5500 號哈希槽。
b. 節點 B 負責處理 5501 號至 11000 號哈希槽。
c. 節點 C 負責處理 11001 號至 16384 號哈希槽。
3)主從複製模型:咱們爲主節點 B 添加了從節點 B1 , 那麼當主節點 B 下線的時候, 集羣就會將 B1 設置爲新的主節點, 並讓它代替下線的主節點 B , 繼續處理 5501 號至 11000 號的
哈希槽, 這樣集羣就不會由於主節點 B 的下線而沒法正常運做了。不過若是節點 B 和 B1 都下線的話, Redis 集羣仍是會中止運做。
4)開啓集羣須要手動執行命令
5)主節點掛了,從節點會變成主節點。當原主節點重啓後,原主節點會成爲新主節點的從節點。
6)增長和刪除節點,都須要手動移動數據。
參見:http://www.redis.cn/topics/cluster-tutorial.html
https://juejin.im/entry/596343056fb9a06bc340ac15
9. redis單線程是什麼鬼
1)線程安全是指 redis是單線程,將變量拷貝到線程內存中。由於是單線程,因此這個變量是公用的。redis其實是採用了線程封閉的觀念,把任務封閉在一個線程,天然避免了線程安全問題,
不過對於須要依賴多個redis操做的複合操做來講,依然須要鎖,並且有多是分佈式鎖。
2)Redis 對於 I/O 多路複用模塊的設計很是簡潔,經過宏保證了 I/O 多路複用模塊在不一樣平臺上都有着優異的性能,將不一樣的 I/O 多路複用函數封裝成相同的 API 提供給上層使用。
整個模塊使 Redis 能以單進程運行的同時服務成千上萬個文件描述符,避免了因爲多進程應用的引入致使代碼實現複雜度的提高,減小了出錯的可能性。
參見:https://draveness.me/redis-io-multiplexing
10. 過時策略
1)按期刪除:Redis 默認會每秒進行十次過時掃描(100ms一次),過時掃描不會遍歷過時字典中全部的 key,而是採用了一種簡單的貪心策略。
從過時字典中隨機 20 個 key;刪除這 20 個 key 中已通過期的 key;若是過時的 key 比率超過 1/4,那就重複步驟 1;
2)惰性刪除:所謂惰性策略就是在客戶端訪問這個 key 的時候,redis 對 key 的過時時間進行檢查,若是過時了就當即刪除,不會給你返回任何東西。
11. 內存淘汰策略
這個問題可能有小夥伴們遇到過,放到Redis中的數據怎麼沒了?
由於Redis將數據放到內存中,內存是有限的,好比redis就只能用10個G,你要是往裏面寫了20個G的數據,會咋辦?固然會幹掉10個G的數據,而後就保留
10個G的數據了。那幹掉哪些數據?保留哪些數據?固然是幹掉不經常使用的數據,保留經常使用的數據了
Redis提供的內存淘汰策略有以下幾種:
1)noeviction 不會繼續服務寫請求 (DEL 請求能夠繼續服務),讀請求能夠繼續進行。這樣能夠保證不會丟失數據,可是會讓線上的業務不能持續進行。這是默認的淘汰策略。
2)volatile-lru 嘗試淘汰設置了過時時間的 key,最少使用的 key 優先被淘汰。沒有設置過時時間的 key 不會被淘汰,這樣能夠保證須要持久化的數據不會忽然丟失。(這個是使用最多的)
3)volatile-ttl 跟上面同樣,除了淘汰的策略不是 LRU,而是 key 的剩餘壽命 ttl 的值,ttl 越小越優先被淘汰。
4)volatile-random 跟上面同樣,不過淘汰的 key 是過時 key 集合中隨機的 key。
5)allkeys-lru 區別於 volatile-lru,這個策略要淘汰的 key 對象是全體的 key 集合,而不僅是過時的 key 集合。這意味着沒有設置過時時間的 key 也會被淘汰。
6)allkeys-random 跟上面同樣,不過淘汰的策略是隨機的 key。allkeys-random 跟上面同樣,不過淘汰的策略是隨機的 key。
12. 什麼狀況下不適合用redis
1)更新頻率太快
2)數據量太大
13. 運維工具:怎麼樣快速定位問題
1)redis desk manager
2)Medis
14. 同類的產品有哪些
1)memcached
a. 存儲方式:memecache 把數據所有存在內存之中,斷電後會掛掉,數據不能超過內存大小。redis有部份存在硬盤上,這樣能保證數據的持久性,支持數據的持久化
(筆者注:有快照和AOF日誌兩種持久化方式,在實際應用的時候,要特別注意配置文件快照參數,要不就頗有可能服務器頻繁滿載作dump)。
b. 數據支持類型:redis在數據支持上要比memecache多的多。
c. 使用底層模型不一樣:新版本的redis直接本身構建了VM 機制 ,由於通常的系統調用系統函數的話,會浪費必定的時間去移動和請求。
d. 分佈式環境:memcached的分佈式由客戶端實現,經過一致性哈希算法來保證訪問的緩存命中率;Redis的分佈式由服務器端實現,經過服務端配置來實現分佈式;
e. 相對memcached而言,redis的面世時間更晚且具有更多功能,所以開發人員一般將其視爲默認性首選方案。不過有兩類特殊場景仍然是memcached的一家天下。首先就是對小型靜態數據進行緩存處理,
最具表明性的例子就是HTML代碼片斷。memcached的內部內存管理機制雖然不像redis的那樣複雜,但卻更具實際效率——這是由於memcached在處理元數據時所消耗的內存資源相對更少。做爲
memcached所支持的唯一一種數據類型,字符串很是適合用於保存那些只須要進行讀取操做的數據,由於字符串自己無需進行進一步處理。
參見:http://www.javashuo.com/article/p-nyxmwphs-dt.html