2019年java中高級java面試題(一)Redis

1 、redis的數據結構

(1) Redis數據結構之String

字符串類型是Redis中最爲基礎的數據存儲類型,它在Redis中是二進制安全的,這便意味着該類型可以接受任何格式的數據,如JPEG圖像數據或Json對象描述信息等。在Redis中字符串類型的Value最多可以容納的數據長度是512M。

SET key value      
設定該Key持有指定的字符串Value,如果該Key已經存在,則覆蓋其原有值。返回值:總是返回"OK"

 

GET key        
獲取指定Key的Value。如果與該Key關聯的Value不是string類型,Redis將返回錯誤信息,因爲GET命令只能用於獲取string Value。

返回值:與該Key相關的Value,如果該Key不存在,則返回nil。

爲何redis字符串是二進制安全?

在 C 語言中,字符串可以用一個 \0 結尾的 char 數組來表示。

比如說, hello world 在 C 語言中就可以表示爲 "hello world\0" 。

這種簡單的字符串表示,在大多數情況下都能滿足要求,但是,它並不能高效地支持長度計算和追加(append)這兩種操作:

  • 每次計算字符串長度(strlen(s))的複雜度爲 O(N)。
  • 對字符串進行 N 次追加,必定需要對字符串進行 N 次內存重分配(realloc)。

而redis除了要處理c語言字符串之外,還需要處理redis的服務器協議等等。所以,redis實現的sds(簡單動態字符串),是二進制安全的。

數據結構的定義如下:

struct sdshdr {  
  
    // buf 已佔用長度  
    int len;  
  
    // buf 剩餘可用長度  
    int free;  
  
    // 實際保存字符串數據的地方  
    char buf[];  
};
  • 因爲有了對字符串長度定義len, 所以在處理字符串時候不會以零值字節(\0)爲字符串結尾標誌.
  • 二進制安全就是輸入任何字節都能正確處理, 即使包含零值字節.

 

(2)Redis數據結構之List

  在Redis中,List類型是按照插入順序排序的字符串鏈表。和數據結構中的普通鏈表一樣,我們可以在其頭部(left)和尾部(right)添加新的元素。在插入時,如果該鍵並不存在,Redis將爲該鍵創建一個新的鏈表。與此相反,如果鏈表中所有的元素均被移除,那麼該鍵也將會被從數據庫中刪除。List中可以包含的最大元素數量是4294967295。

(3)Redis數據結構之Hash

Redis中的Hashes類型可以看成具有String Key和String Value的map容器。所以該類型非常適合於存儲值對象的信息。如用戶信息:Username、Password和Age等。每一個Hash可以存儲4294967295個鍵值對。

hset user01 username zhangsan

(4)Redis數據結構之Set

在Redis中,我們可以將Set類型看作爲沒有排序的字符串集合。Set可包含的最大元素數量是4294967295。

      Set類型在功能上還存在着一個非常重要的特性,即在服務器端完成多個Sets之間的聚合計算操作,如unions、intersections和differences。由於這些操作均在服務端完成,因此效率極高,而且也節省了大量的網絡IO開銷。

 

(5)Redis數據結構之SortedSet

Sorted-Sets和Sets類型極爲相似,它們都是字符串的集合,都不允許重複的成員出現在一個Set中。它們之間的主要差別是Sorted-Sets中的每一個成員都會有一個分數(score)與之關聯,Redis正是通過分數來爲集合中的成員進行從小到大的排序。然而需要額外指出的是,儘管Sorted-Sets中的成員必須是唯一的,但是分數(score)卻是可以重複的

2、redis的持久化機制


Redis是一個開源的高性能鍵值對數據庫

是NoSQL技術陣營中的一員

它通過提供多種鍵值數據類型來適應不同場景下的存儲需求

藉助一些高層級的接口使其可以勝任,如緩存、隊列系統的不同角色

可用作緩存、隊列、消息訂閱/發佈

   1). RDB持久化(redis系統默認持久化策略):

    該機制是指在指定的時間間隔內將內存中的數據集快照寫入磁盤。   

可以通過配置設置自動做快照持久化的方式。我們可以配置redis在n秒內如果超過m個key被修改就自動做快照,下面是默認的快照保存配置

   save 900 1     #900秒內如果超過1個key被修改,則發起快照保存
   save 300 10    #300秒內容如超過10個key被修改,則發起快照保存
   save 60 10000


    2). AOF(append only file)持久化:

該機制將以日誌的形式記錄服務器所處理的每一個寫操作,在Redis服務器啓動之初會讀取該文件來重新構建數據庫,以保證啓動後數據庫中的數據是完整的

 

3、mySQL裏有2000w數據,redis中只存20w的數據,如何保證redis中的數據都是熱點數據


   redis 內存數據集大小上升到一定大小的時候,就會施行數據淘汰策略(回收策略)。

redis 提供 6種數據淘汰策略:

volatile-lru:從已設置過期時間的數據集(server.db[i].expires)中挑選最近最少使用的數據淘汰
volatile-ttl:從已設置過期時間的數據集(server.db[i].expires)中挑選將要過期的數據淘汰
volatile-random:從已設置過期時間的數據集(server.db[i].expires)中任意選擇數據淘汰
allkeys-lru:從數據集(server.db[i].dict)中挑選最近最少使用的數據淘汰
allkeys-random:從數據集(server.db[i].dict)中任意選擇數據淘汰
no-enviction(驅逐):禁止驅逐數據

  

4、 redis常見性能問題和解決方案


   1).Master寫內存快照,save命令調度rdbSave函數,會阻塞主線程的工作,當快照比較大時對性能影響是非常大的,會間斷性暫停服務,所以Master最好不要寫內存快照。

   2).Master AOF持久化,如果不重寫AOF文件,這個持久化方式對性能的影響是最小的,但是AOF文件會不斷增大,AOF文件過大會影響Master重啓的恢復速度。Master最好不要做任何持久化工作,包括內存快照和AOF日誌文件,特別是不要啓用內存快照做持久化,如果數據比較關鍵,某個Slave開啓AOF備份數據,策略爲每秒同步一次。

   3).Master調用BGREWRITEAOF重寫AOF文件,AOF在重寫的時候會佔大量的CPU和內存資源,導致服務load過高,出現短暫服務暫停現象。

   4). Redis主從複製的性能問題,爲了主從複製的速度和連接的穩定性,Slave和Master最好在同一個局域網內

5、 redis的併發競爭問題如何解決?


Redis是單進程單線程的,redis利用隊列技術將併發訪問變爲串行訪問,消除了傳統數據庫串行控制的開銷

6、redis事務的命令


Redis 事務命令

下標列出了redis事務的相關命令

1. DISCARD

  取消事務,放棄執行事務塊內的所有命令。

2. EXEC

  執行所有事務塊內的命令

3. MULTI

  標記一個事務塊的開始

4. UNWATCH

  取消WATCH命令對所有key的監視

5. WATCH key [key ...]

  監視一個(或多個)key,如果在事務執行之前這個(或這些) key 被其他命令所改動,那麼事務將被打斷。

https://www.cnblogs.com/Survivalist/p/8119891.html

6、 redis事務的特徵


(1)在事務中的所有命令都將會被串行化的順序執行,事務執行期間,Redis不會再爲其它客戶端的請求提供任何服務,從而保證了事物中的所有命令被原子的執行。

(2)在Redis事務中如果有某一條命令執行失敗,其後的命令仍然會被繼續執行。

(3)MULTI命令開啓一個事務,可以通過執行EXEC/DISCARD命令來提交/回滾該事務內的所有操作

redis中也是有事務的,不過這個事務沒有mysql中的完善,只保證了一致性和隔離性,不滿足原子性和持久性。

7、 redis集羣搭建


架構細節:

(1)所有的redis節點彼此互聯(PING-PONG機制),內部使用二進制協議優化傳輸速度和帶寬.

(2)節點的fail是通過集羣中超過半數的節點檢測失效時才生效.

(3)客戶端與redis節點直連,不需要中間proxy層.客戶端不需要連接集羣所有節點,連接集羣中任何一個可用節點即可

(4)redis-cluster把所有的物理節點映射到[0-16383]slot上,cluster 負責維護node<->slot<->value

Hash一致性算法

Redis 集羣中內置了 16384(2^{14}) 個哈希槽,當需要在 Redis 集羣中放置一個 key-value 時,redis 先對 key 使用 crc16 算法算出一個結果,然後把結果對 16384 求餘數,這樣每個 key 都會對應一個編號在 0-16383 之間的哈希槽,redis 會根據節點數量大致均等的將哈希槽映射到不同的節點

 

集羣搭建

集羣中應該至少有三個節點,每個節點有一備份節點。需要6臺服務器。

搭建僞分佈式,需要6個redis實例。

搭建集羣的步驟:

第一步:創建6個redis實例指定端口從7001到7006

第二步:修改redis.conf 打開Cluster-enable yes前面的註釋。

第三步:需要一個ruby腳本。在redis源碼文件夾下的src目錄下。redis-trib.rb

第四步:把redis-trib.rb文件複製到到redis-cluster目錄下。

第五步:執行ruby腳本之前,需要安裝ruby環境。

1、yum install ruby

2、yum install rubygems

3、安裝redis-trib.rb運行依賴的ruby的包。

 

[root@bogon ~]# gem install redis-3.0.0.gem

第六步:啓動所有的redis實例。

第七步:使用redis-trib.rb創建集羣。

./redis-trib.rb create --replicas 1 192.168.25.153:7001 
192.168.25.153:7002 192.168.25.153:7003 
192.168.25.153:7004 192.168.25.153:7005  192.168.25.153:7006

使用客戶端連接集羣: redis01/redis-cli -p 7001 -c

 

8、redis是單線程,爲什麼那麼快?

 1、完全基於內存,絕大部分請求是純粹的內存操作,非常快速。數據存在內存中,類似於HashMap,HashMap的優勢就是查找和操作的時間複雜度都是O(1);

  2、數據結構簡單,對數據操作也簡單,Redis中的數據結構是專門進行設計的;

  3、採用單線程,避免了不必要的上下文切換和競爭條件,也不存在多進程或者多線程導致的切換而消耗 CPU,不用去考慮各種鎖的問題,不存在加鎖釋放鎖操作,沒有因爲可能出現死鎖而導致的性能消耗;

  4、使用多路I/O複用模型,非阻塞IO;

  5、使用底層模型不同,它們之間底層實現方式以及與客戶端之間通信的應用協議不一樣,Redis直接自己構建了VM 機制 ,因爲一般的系統調用系統函數的話,會浪費一定的時間去移動和請求;

  以上幾點都比較好理解,下邊我們針對多路 I/O 複用模型進行簡單的探討:

  多路 I/O 複用模型

  多路I/O複用模型是利用 select、poll、epoll 可以同時監察多個流的 I/O 事件的能力,在空閒的時候,會把當前線程阻塞掉,當有一個或多個流有 I/O 事件時,就從阻塞態中喚醒,於是程序就會輪詢一遍所有的流(epoll 是隻輪詢那些真正發出了事件的流),並且只依次順序的處理就緒的流,這種做法就避免了大量的無用操作。

  這裏“多路”指的是多個網絡連接,“複用”指的是複用同一個線程。採用多路 I/O 複用技術可以讓單個線程高效的處理多個連接請求(儘量減少網絡 IO 的時間消耗),且 Redis 在內存中操作數據的速度非常快,也就是說內存內的操作不會成爲影響Redis性能的瓶頸,主要由以上幾點造就了 Redis 具有很高的吞吐量。

9、緩存穿透

     緩存穿透是指查詢一個一定不存在的數據,由於緩存是不命中時需要從數據庫查詢,查不到數據則不寫入緩存,這將導致這個不存在的數據每次請求都要到數據庫去查詢,造成緩存穿透。

解決辦法:

     1.布隆過濾

  對所有可能查詢的參數以hash形式存儲,在控制層先進行校驗,不符合則丟棄。還有最常見的則是採用布隆過濾器,將所有可能存在的數據哈希到一個足夠大的bitmap中,一個一定不存在的數據會被這個bitmap攔截掉,從而避免了對底層存儲系統的查詢壓力。

2. 緩存空對象. 將 null 變成一個值.

  也可以採用一個更爲簡單粗暴的方法,如果一個查詢返回的數據爲空(不管是數 據不存在,還是系統故障),我們仍然把這個空結果進行緩存,但它的過期時間會很短,最長不超過五分鐘。

10、緩存雪崩

    是指在我們設置緩存時採用了相同的過期時間,導致緩存在某一時刻同時失效,請求全部轉發到DB,DB瞬時壓力過重雪崩。

解決方案:

將系統中key的緩存失效時間均勻地錯開,防止統一時間點有大量的key對應的緩存失效。比如我們可以在原有的失效時間基礎上增加一個隨機值,比如1-5分鐘隨機,這樣每一個緩存的過期時間的重複率就會降低,就很難引發集體失效的事件。

 

 

 

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