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() 個哈希槽,當需要在 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分鐘隨機,這樣每一個緩存的過期時間的重複率就會降低,就很難引發集體失效的事件。