1、redis的刪除策略
Redis是一種內存級數據庫,所有數據均存放在內存中,內存中的數據可以通過TTL指令獲取其狀態,返回值:
XX :具有時效性的數據;
-1 :永久有效的數據 ;
-2 :已經過期的數據 或 被刪除的數據 或 未定義的數據 ;
提問1:過期了的數據真的刪除了嗎?
首先要知道redis針對設置expire數據的三種刪除策略:
1)定時刪除, 創建一個定時器,當key設置有過期時間,且過期時間到達時,立即執行對鍵的刪除操作 。特點:拿時間換空間。雖然節約內存,但會增加cpu壓力。
2)惰性刪除,數據到達過期時間,不做處理。等下次訪問該數據時:如果未過期,返回數據 ;發現已過期,刪除,返回不存在。特點拿空間換時間。
3)定期刪除,是以上兩種的折中策略,其實刪除策略就是在內存占用和cpu占用找一種平衡。像定時刪除和惰性刪除都有可能降低redis性能。
Redis啟動服務器初始化時,讀取配置server.hz的值,默認為10 ,在命令行中輸入info server可以查詢到。
每秒對redis中16個expires執行10次輪詢,對expires中的過期數據檢查,隨機挑w個key檢查,如果超時刪除key,如果刪除量超過一定值,會繼續對這個expires進行檢查,然后刪除過期值;如果刪除量小會對下一個expirs進行檢查,以此類推。
提問2:內存中都是有效數據,當插入新的數據時,內存不足怎么辦?
Redis使用內存存儲數據,在執行每一個命令前,會調用freeMemoryIfNeeded()檢測內存是否充足。如果內存不滿足新加入數據的最低存儲要求,redis要臨時刪除一些數據為當前指令清理存儲空間。清理數據的策略稱為逐出算法。
那么按照什么樣的策略?
第一種,在expires種挑選數據,也就是設定有過期時間的數據
1)選擇一定時間內使用次數最少的數據進行刪除(LFU)
2)選擇一定時間內最長時間沒有使用的進行刪除(LRU)(推薦)
3)選擇將要過期的數據進行刪除(TTL)
4)任意選擇
第二種,在所有數據種,不管有期還是無期
1)選擇一定時間內使用次數最少的數據進行刪除(LFU)
2)選擇一定時間內最長時間沒有使用的進行刪除(LRU)
3)任意選擇
第三種,放棄逐出策略,no-enviction
逐出算法的相關配置
#占用物理內存的比例,默認值為0,表示不限制。 maxmemory
#每次選取待刪除數據的個數 maxmemory-samples
#達到最大內存后的對被挑選出來的數據進行刪除的策略 #默認 no-enviction:禁止驅逐數據 maxmemory-policy
2、高級數據結構
Bitmaps:用於狀態信息統計,一個bit八位,每一位可以表示一個狀態,如果我們想統計一個單位的黨員,比如“0000 0000”,就表示沒有一個黨員;“0011 0110”表示3號、4號、6號、7號是黨員。
二進制位的基本操作
//設置一個key它的第0位是1 setbit bitkey 0 1 //第二位是1 setbit bitkey 2 1 //獲取第二位上的狀態位 getbit bitkey 2
業務場景:
某電影網站統計某一天/一周/一個月/一年有多少電影被點播,有多少電影沒有被點播。比如一天的統計“1100 1110”,我們得知《武林外傳》的id為1,查看二進制位為“1”,說名一天內有被點播,還可以從“1”個數得出一共有5部電影被點播,3部電影沒有被點播。如果想統計每周的數據,只需要將每天的二進制位按位或,得到的二進制位就是一周的點播狀態。如法炮制,可以統計一年的電影播放狀態,如果某個二進制位是“0”,說明該電影一年內沒有被點播,可以考慮下架。節省存儲空間。
//對指定的key按位或/按位與/按位/異或/將結果保存在destkey中 bitop op destkey key1 key2.... //統計指定key中某一范圍中1的數量 bitconut key start end
hyperloglog :用來做基數統計。
{1, 3, 5, 7, 5, 7, 8} 基數集: {1, 3, 5 ,7, 8} 基數:5
#添加數據 127.0.0.1:6379> pfadd id 001 (integer) 1 127.0.0.1:6379> pfadd id 002 (integer) 1 127.0.0.1:6379> pfadd id 002 (integer) 0 127.0.0.1:6379> pfadd id 002 (integer) 0 #統計數據 127.0.0.1:6379> pfcount id (integer) 2 #合並數據 127.0.0.1:6379> pfmerge destkey id OK
GEO:有關地理位置的數據結構,例如已知A點和B點,求兩點的距離。
基本操作:
#添加數據,其中“key”可以看作是一個容器,
geoadd key longitude latitude member [longitude latitude member ...]
#獲取某個點的位置
geopos key member [member ...]
#計算兩點的距離,默認單位為米
geodist key member1 member2 [unit]
127.0.0.1:6379> geoadd geos 1 2 A (integer) 1 127.0.0.1:6379> geoadd geos 1 3 B (integer) 1 127.0.0.1:6379> geopos geos A 1) 1) "0.99999994039535522" 2) "2.0000001856465488" 127.0.0.1:6379> geopos geos B 1) 1) "0.99999994039535522" 2) "3.00000091215010656" 127.0.0.1:6379> geodist geos A B "111226.3808" 127.0.0.1:6379> geodist geos A B km "111.2264" 127.0.0.1:6379>
進階操作:想知道附近有多少人
//把坐標點放入容器中 127.0.0.1:6379> geoadd geos 0 0 A (integer) 1 127.0.0.1:6379> geoadd geos 0 1 B (integer) 1 127.0.0.1:6379> geoadd geos 0 2 C (integer) 1 127.0.0.1:6379> geoadd geos 1 0 D (integer) 1 127.0.0.1:6379> geoadd geos 1 2 F (integer) 1 127.0.0.1:6379> geoadd geos 2 0 G (integer) 1 127.0.0.1:6379> geoadd geos 2 1 H (integer) 1 127.0.0.1:6379> geoadd geos 2 2 I (integer) 1 127.0.0.1:6379> geoadd geos 1 1 E (integer) 1 //獲取E點180km范圍內有多少點 127.0.0.1:6379> georadiusbymember geos E 180 km 1) "A" 2) "D" 3) "B" 4) "E" 5) "G" 6) "H" 7) "C" 8) "F" 9) "I" 127.0.0.1:6379> georadiusbymember geos E 100 km 1) "E" 127.0.0.1:6379> georadiusbymember geos E 150 km 1) "D" 2) "B" 3) "E" 4) "H" 5) "F"
3、主從復制
單機redis會有很多風險,比如機器故障,丟失數據;單台機器容量有限,不可能無限制增加內存;當讀操作頻率遠遠大於大於寫操作頻率時,單台結點負載過大。
解決方案:准備多台服務器,互相連通。將數據復制多個副本保存在不同的服務器上,連接在一起,並保證數據是同步的。即使有其中一台服務器宕機,其他服務器依然可以繼續 提供服務,實現Redis的高可用,同時實現數據冗余備份。
多台機器分為兩個角色,一個為master,實現寫數據,是提供數據的一方,另一個是角色slave,可以有多個,實現讀數據,是接受數據的一方。把master中的數據復制到slave中。
主從復制的原理:當數據庫啟動后,slave會向master發送SYNC請求。master 會執行rdb持久化的同時會起一個buffer用來存執行rdb期間有客戶端發來的命令。快照完成后,master會將rdb文件和buffer中的命令依次發送給slave。后續master再有接受到客戶端的寫命令,就會將命令同步給slave。
主從復制的流程:
1、建立連接。
slave主動連接master,slave發送指令“slaveof ip port”給目標服務器
master接受到指令,響應對方
slave保存master的ip和port
在slave端創建與master進行通信連接的socket
slave端周期性的向master發送"ping"命令,master回應“pong”
如果master設置有密碼,slave需要驗證
slave發送命令“replconf listen-port<port>”告訴master的端口號,master接受並保存
建立連接的方式:
#方法一:客戶端發送命令slaveof <masterip> <masterport> slaveof 192.168.31.119 6379 #方法二:客戶端啟動時 redis-server -slaveof 192.168.31.119 6379 #方法三:修改配置文件 slaveof 192.168.31.119 6379
master設置密碼和密碼校驗
#master端設置密碼 requirepass 123456 #slave端驗證密碼 auth 123456
手動搭建主從結構
一共需要,master服務器,master客戶端,slave服務器,slave客戶端
第一步:設置兩個角色的服務器配置文件
第二步:分別啟動master(端口號:6379)服務器、slave(端口號:6380)服務器
第三步:slave主動連接master
通過配置文件實現主從復制
在6380的配置文件中添加
slaveof 127.0.0.1 6379
2、數據同步。在slave初次連接master后,復制master中的所有數據到slave。具體步驟:
slave發送指令"psync2 runid offset",請求同步,初始時發送“psync2 ? -1”,因為master的rundi和offset都不知道。
master執行bgsave,再創建一個命令緩沖區(存放新的指令),記錄當前的偏移量offset,生成rdb文件,通過socket通信把文件、runid和offset傳遞給slave;
slave清空原始數據,接受rdb,恢復文件,並保存runid和offset;
(以上三步是完成全量復制)
salve恢復完成后通知master,發送命令psync2 runid offset
master會判斷對方的runid匹不匹配,offset是否在緩沖區中,只要有一個不滿足就執行全量復制。如果校驗通過,但是slaver發送的offset與master保存的offset不一致,master將緩沖區中的指令(可以理解為aof)和offset發送給slave
slave保存master的offset,執行aof重寫,恢復數據。
(以上部分復制)
數據同步階段的注意事項:如果master數量很大,數據同步時應該錯開高峰。
當master與slave全量復制的過程中有持續的命令進來,復制緩沖區有限(默認一兆),那么最前面的數據很可能丟失,master與slave進行部分同步時,發現有丟失,就會全量復制。
3、命令轉播。當master數據庫狀態被修改后,導致主從服務器數據庫狀態不一致,此時需要讓主從數據同步到一致的狀態。實時同步。 進入命令傳播階段候,master與slave間需要進行信息交換,使用心跳機制進行維護,實現雙方連接保持在線
補充部分復制中的幾個要素:
runid:每一台服務器每次運行的身份識別碼,一台服務器多次運行可以生成多個運行id ,40位字符組成,是一個隨機的十六進制字符 。在第一個階段,當slave來連master時,就會把自己的runid發送給master,master會保存,如果下次slaver與master再次通信時,master發現slaver的runid變了,master就會意識到數據不一致。
master的復制積壓緩沖區:當master收到一個命令時,master中的一個"命令傳播程序"會把命令發送給每一個slaver,如果其中的某一條線路發生故障,該命令沒有傳播到slaver中,此時數據就不能同步。為了解決這個問題,引入緩沖區,master再發送命令的同時會把命令保存在緩沖區一份。
master記錄已發送的信息對應的offset
slave記錄已接收的信息對應的offset
雙方的復制偏移量:
master復制偏移量:記錄發送給所有slave的指令字節對應的位置(多個)
slave復制偏移量:記錄slave接收master發送過來的指令字節對應的位置(一個)
4、哨兵
哨兵(sentinel) 是一個分布式系統,用於對主從結構中的每台服務器進行監控,當出現故障時通過投票機制選擇新的 master並將所有slave連接到新的master,哨兵也是一台redis服務器,只是不提供數據服務 通常哨兵配置數量為單數 。
哨兵的配置文件:
#哨兵的端口 port 26379 #有關哨兵的信息存儲目錄 dir /tmp #“mymaster”起的名稱,后面ip+port是監聽的對象,“2”表示如果有兩台哨兵認為master宕機了,就表示真的宕機了 sentinel monitor mymaster 127.0.0.1 6379 2 #指定哨兵在監控Redis服務時,判定服務器掛掉的時間周期,默認30秒 sentinel down-after-milliseconds mymaster 30000 #新的master一次向多少個slave同步 sentinel parallel-syncs mymaster 1 #同步超時時間 sentinel failover-timeout mymaster 180000
搭建哨兵。預備搭建一個master,兩個slave,三個哨兵
第一步:設置配置文件
第二步:搭建主從結構
第三步:啟動三個哨兵
最后一個哨兵啟動后打印信息
當我們關閉6379端口的服務器(master),哨兵服務器打印如下信息
哨兵的工作原理:
第一階段:監控階段,同步各個結點的狀態信息
sentinel會分別向master、slave、其他sentinel索取狀態信息。
向master發送“info”獲取mster的runid、role、與master連接的各個slave信息,根據從master上獲取的slave信息然后分別向slave再獲取信息,獲取各個slave的runid、role、offset、master_ip、master_port。各個sentinel之間會搭建一個環,他們在這個環中訂閱信息、發布信息、同步信息。
第二階段:通知階段
幾個sentinel形成一個小的群體,他們之前進行信息的同步。而master和slave正常運行,sentinel會發送"hello"獲取他們幾個的工作狀態,其中的一個sentinel拿到這個狀態會向其他幾個sentinel進行信息互通。
第三階段:故障轉移階段
當其中一台sentinel發現master下線了,會給master標記一個狀態“SRI_S_DOWN”,然后將這個信息發送給其他幾個sentinel,其他幾個也獲取master的狀態信息,有一半以上的sentinel認為master掛掉了,就會給master標記“SRI_O_DOWN”,證明該master確實下線了。
內部幾個sentinel競選,選出一個“負責人”,這個“負責人”會根據一定的原則(在線的、響應快的、優先級高的)從slave中選擇一個充當master。其他的slave切換master。