初識redis
1)Redis的8個特性:速度快、基于鍵值對的數(shù)據(jù)結(jié)構(gòu)服務器俏扩、功能豐富糜工、簡單穩(wěn)定、客戶端語言多录淡、持久化捌木、主從復制、支持高可用和分布式嫉戚。
2)Redis并不是萬金油刨裆,有些場景不適合使用Redis進行開發(fā)。
3)開發(fā)運維結(jié)合以及閱讀源碼是用好Redis的重要方法彬檀。
4)生產(chǎn)環(huán)境中使用配置文件啟動Redis帆啃。
5)生產(chǎn)環(huán)境選取穩(wěn)定版本的Redis。
6)Redis3.0是重要的里程碑凤覆,發(fā)布了Redis官方的分布式實現(xiàn)Redis Cluster链瓦。
API的理解和使用
1)Redis提供5種數(shù)據(jù)結(jié)構(gòu),每種數(shù)據(jù)結(jié)構(gòu)都有多種內(nèi)部編碼實現(xiàn)盯桦。
2)純內(nèi)存存儲慈俯、IO多路復用技術(shù)、單線程架構(gòu)是造就Redis高性能的三個因素拥峦。
3)由于Redis的單線程架構(gòu)贴膘,所以需要每個命令能被快速執(zhí)行完,否則會存在阻塞Redis的可能略号,理解Redis單線程命令處理機制是開發(fā)和運維Redis的核心之一刑峡。
4)批量操作(例如mget、mset玄柠、hmset等)能夠有效提高命令執(zhí)行的效率突梦,但要注意每次批量操作的個數(shù)和字節(jié)數(shù)。
5)了解每個命令的時間復雜度在開發(fā)中至關重要羽利,例如在使用keys宫患、hgetall、smembers这弧、zrange等時間復雜度較高的命令時娃闲,需要考慮數(shù)據(jù)規(guī)模對于Redis的影響。
6)persist命令可以刪除任意類型鍵的過期時間匾浪,但是set命令也會刪除字符串類型鍵的過期時間皇帮,這在開發(fā)時容易被忽視。
7)move蛋辈、dump+restore属拾、migrate是Redis發(fā)展過程中三種遷移鍵的方式,其中move命令基本廢棄,migrate命令用原子性的方式實現(xiàn)了dump+restore捌年,并且支持批量操作瓢娜,是Redis Cluster實現(xiàn)水平擴容的重要工具。
8)scan命令可以解決keys命令可能帶來的阻塞問題礼预,同時Redis還提供了hscan眠砾、sscan、zscan漸進式地遍歷hash托酸、set褒颈、zset。
小功能大用處
1)慢查詢中的兩個重要參數(shù)slowlog-log-slower-than和slowlog-maxlen励堡。
2)慢查詢不包含命令網(wǎng)絡傳輸和排隊時間谷丸。
3)有必要將慢查詢定期存放。
4)redis-cli一些重要的選項应结,例如--latency刨疼、–-bigkeys、-i和-r組合鹅龄。
5)redis-benchmark的使用方法和重要參數(shù)揩慕。
6)Pipeline可以有效減少RTT次數(shù),但每次Pipeline的命令數(shù)量不能無節(jié)制扮休。
7)Redis可以使用Lua腳本創(chuàng)造出原子迎卤、高效、自定義命令組合玷坠。
8)Redis執(zhí)行Lua腳本有兩種方法:eval和evalsha蜗搔。
9)Bitmaps可以用來做獨立用戶統(tǒng)計,有效節(jié)省內(nèi)存八堡。
10)Bitmaps中setbit一個大的偏移量樟凄,由于申請大量內(nèi)存會導致阻塞。
11)HyperLogLog雖然在統(tǒng)計獨立總量時存在一定的誤差兄渺,但是節(jié)省的內(nèi)存量十分驚人不同。
12)Redis的發(fā)布訂閱機制相比許多專業(yè)的消息隊列系統(tǒng)功能較弱,不具備堆積和回溯消息的能力溶耘,但勝在足夠簡單。
13)Redis3.2提供了GEO功能服鹅,用來實現(xiàn)基于地理位置信息的應用凳兵,但底層實現(xiàn)是zset。
客戶端
1)RESP(Redis Serialization Protocol Redis)保證客戶端與服務端的正常通信企软,是各種編程語言開發(fā)客戶端的基礎庐扫。
2)要選擇社區(qū)活躍客戶端,在實際項目中使用穩(wěn)定版本的客戶端。
3)區(qū)分Jedis直連和連接池的區(qū)別形庭,在生產(chǎn)環(huán)境中铅辞,應該使用連接池。
4)Jedis.close()在直連下是關閉連接萨醒,在連接池則是歸還連接斟珊。
5)Jedis客戶端沒有內(nèi)置序列化,需要自己選用富纸。
6)客戶端輸入緩沖區(qū)不能配置囤踩,強制限制在1G之內(nèi),但是不會受到maxmemory限制晓褪。
7)客戶端輸出緩沖區(qū)支持普通客戶端堵漱、發(fā)布訂閱客戶端、復制客戶端配置涣仿,同樣會受到maxmemory限制勤庐。
8)Redis的timeout配置可以自動關閉閑置客戶端,tcp-keepalive參數(shù)可以周期性檢查關閉無效TCP連接
9)monitor命令雖然好用好港,但是在大并發(fā)下存在輸出緩沖區(qū)暴漲的可能性愉镰。
10)info clients幫助開發(fā)和運維人員找到客戶端可能存在的問題。
11)理解Redis通信原理和建立完善的監(jiān)控系統(tǒng)對快速定位解決客戶端常見問題非常有幫助媚狰。
持久化
1)Redis提供了兩種持久化方式:RDB和AOF岛杀。
2)RDB使用一次性生成內(nèi)存快照的方式,產(chǎn)生的文件緊湊壓縮比更高崭孤,因此讀取RDB恢復速度更快类嗤。由于每次生成RDB開銷較大,無法做到實時持久化辨宠,一般用于數(shù)據(jù)冷備和復制傳輸遗锣。
3)save命令會阻塞主線程不建議使用,bgsave命令通過fork操作創(chuàng)建子進程生成RDB避免阻塞嗤形。
4)AOF通過追加寫命令到文件實現(xiàn)持久化精偿,通過appendfsync參數(shù)可以控制實時/秒級持久化。因為需要不斷追加寫命令赋兵,所以AOF文件體積逐漸變大笔咽,需要定期執(zhí)行重寫操作來降低文件體積。
5)AOF重寫可以通過auto-aof-rewrite-min-size和auto-aof-rewritepercentage參數(shù)控制自動觸發(fā)霹期,也可以使用bgrewriteaof命令手動觸發(fā)叶组。
6)子進程執(zhí)行期間使用copy-on-write機制與父進程共享內(nèi)存,避免內(nèi)存消耗翻倍历造。AOF重寫期間還需要維護重寫緩沖區(qū)甩十,保存新的寫入命令避免數(shù)據(jù)丟失船庇。
7)持久化阻塞主線程場景有:fork阻塞和AOF追加阻塞。fork阻塞時間跟內(nèi)存量和系統(tǒng)有關侣监,AOF追加阻塞說明硬盤資源緊張鸭轮。
8)單機下部署多個實例時,為了防止出現(xiàn)多個子進程執(zhí)行重寫操作橄霉,建議做隔離控制窃爷,避免CPU和IO資源競爭。
復制
1)Redis通過復制功能實現(xiàn)主節(jié)點的多個副本酪劫。從節(jié)點可靈活地通過slaveof命令建立或斷開復制流程吞鸭。
2)復制支持樹狀結(jié)構(gòu),從節(jié)點可以復制另一個從節(jié)點覆糟,實現(xiàn)一層層向下的復制流刻剥。Redis2.8之后復制的流程分為:全量復制和部分復制。全量復制需要同步全部主節(jié)點的數(shù)據(jù)集滩字,大量消耗機器和網(wǎng)絡資源造虏。而部分復制有效減少因網(wǎng)絡異常等原因造成的不必要全量復制情況。通過配置合理的復制積壓緩沖區(qū)盡量避免全量復制麦箍。
3)主從節(jié)點之間維護心跳和偏移量檢查機制漓藕,保證主從節(jié)點通信正常和數(shù)據(jù)一致。
4)Redis為了保證高性能復制過程是異步的挟裂,寫命令處理完后直接返回給客戶端享钞,不等待從節(jié)點復制完成。因此從節(jié)點數(shù)據(jù)集會有延遲情況诀蓉。
5)當使用從節(jié)點用于讀寫分離時會存在數(shù)據(jù)延遲栗竖、過期數(shù)據(jù)、從節(jié)點可用性等問題渠啤,需要根據(jù)自身業(yè)務提前作出規(guī)避狐肢。
6)在運維過程中,主節(jié)點存在多個從節(jié)點或者一臺機器上部署大量主節(jié)點的情況下沥曹,會有復制風暴的風險份名。
Redis的噩夢:阻塞
1)客戶端最先感知阻塞等Redis超時行為,加入日志監(jiān)控報警工具可快速定位阻塞問題妓美,同時需要對Redis進程和機器做全面監(jiān)控僵腺。
2)阻塞的內(nèi)在原因:確認主線程是否存在阻塞,檢查慢查詢等信息壶栋,發(fā)現(xiàn)不合理使用API或數(shù)據(jù)結(jié)構(gòu)的情況想邦,如keys、sort委刘、hgetall等丧没。關注CPU使用率防止單核跑滿。當硬盤IO資源緊張時锡移,AOF追加也會阻塞主線程呕童。
3)阻塞的外在原因:從CPU競爭、內(nèi)存交換淆珊、網(wǎng)絡問題等方面入手排查是否因為系統(tǒng)層面問題引起阻塞夺饲。
理解內(nèi)存
1)Redis實際內(nèi)存消耗主要包括:鍵值對象、緩沖區(qū)內(nèi)存施符、內(nèi)存碎片往声。
2)通過調(diào)整maxmemory控制Redis最大可用內(nèi)存。當內(nèi)存使用超出時戳吝,根據(jù)maxmemory-policy控制內(nèi)存回收策略浩销。
3)內(nèi)存是相對寶貴的資源,通過合理的優(yōu)化可以有效地降低內(nèi)存的使用量听哭,內(nèi)存優(yōu)化的思路包括:
- 精簡鍵值對大小慢洋,鍵值字面量精簡,使用高效二進制序列化工具陆盘。
- 使用對象共享池優(yōu)化小整數(shù)對象普筹。
- 數(shù)據(jù)優(yōu)先使用整數(shù),比字符串類型更節(jié)省空間隘马。
- 優(yōu)化字符串使用太防,避免預分配造成的內(nèi)存浪費。
- 使用ziplist壓縮編碼優(yōu)化hash酸员、list等結(jié)構(gòu)蜒车,注重效率和空間的平衡。
- 使用intset編碼優(yōu)化整數(shù)集合沸呐。
- 使用ziplist編碼的hash結(jié)構(gòu)降低小對象鏈規(guī)模醇王。
哨兵
1)Redis Sentinel是Redis的高可用實現(xiàn)方案:故障發(fā)現(xiàn)、故障自動轉(zhuǎn)移崭添、配置中心寓娩、客戶端通知。
2)Redis Sentinel從Redis2.8版本開始才正式生產(chǎn)可用呼渣,之前版本生產(chǎn)不可用棘伴。
3)盡可能在不同物理機上部署Redis Sentinel所有節(jié)點。
4)Redis Sentinel中的Sentinel節(jié)點個數(shù)應該為大于等于3且最好為奇數(shù)屁置。
5)Redis Sentinel中的數(shù)據(jù)節(jié)點與普通數(shù)據(jù)節(jié)點沒有區(qū)別焊夸。
6)客戶端初始化時連接的是Sentinel節(jié)點集合,不再是具體的Redis節(jié)點蓝角,但Sentinel只是配置中心不是代理阱穗。
7)Redis Sentinel通過三個定時任務實現(xiàn)了Sentinel節(jié)點對于主節(jié)點饭冬、從節(jié)點、其余Sentinel節(jié)點的監(jiān)控揪阶。
8)Redis Sentinel在對節(jié)點做失敗判定時分為主觀下線和客觀下線昌抠。
9)看懂Redis Sentinel故障轉(zhuǎn)移日志對于Redis Sentnel以及問題排查非常有幫助。
10)Redis Sentinel實現(xiàn)讀寫分離高可用可以依賴Sentinel節(jié)點的消息通知鲁僚,獲取Redis數(shù)據(jù)節(jié)點的狀態(tài)變化炊苫。
集群
1)Redis集群數(shù)據(jù)分區(qū)規(guī)則采用虛擬槽方式,所有的鍵映射到16384個槽中冰沙,每個節(jié)點負責一部分槽和相關數(shù)據(jù)侨艾,實現(xiàn)數(shù)據(jù)和請求的負載均衡。
2)搭建集群劃分三個步驟:準備節(jié)點拓挥,節(jié)點握手唠梨,分配槽∽策矗可以使用redis-trib.rb create命令快速搭建集群姻成。
3)集群內(nèi)部節(jié)點通信采用Gossip協(xié)議彼此發(fā)送消息,消息類型分為:ping消息愿棋、pong消息科展、meet消息、fail消息等糠雨。節(jié)點定期不斷發(fā)送和接受ping/pong消息來維護更新集群的狀態(tài)才睹。消息內(nèi)容包括節(jié)點自身數(shù)據(jù)和部分其他節(jié)點的狀態(tài)數(shù)據(jù)。
4)集群伸縮通過在節(jié)點之間移動槽和相關數(shù)據(jù)實現(xiàn)甘邀。擴容時根據(jù)槽遷移計劃把槽從源節(jié)點遷移到目標節(jié)點琅攘,源節(jié)點負責的槽相比之前變少從而達到集群擴容的目的,收縮時如果下線的節(jié)點有負責的槽需要遷移到其他節(jié)點松邪,再通過cluster forget命令讓集群內(nèi)其他節(jié)點忘記被下線節(jié)點坞琴。
5)使用Smart客戶端操作集群達到通信效率最大化,客戶端內(nèi)部負責計算維護鍵→槽→節(jié)點的映射逗抑,用于快速定位鍵命令到目標節(jié)點剧辐。集群協(xié)議通過Smart客戶端全面高效的支持需要一個過程,用戶在選擇Smart客戶端時建議review下集群交互代碼如:異常判定和重試邏輯邮府,更新槽的并發(fā)控制等荧关。節(jié)點接收到鍵命令時會判斷相關的槽是否由自身節(jié)點負責,如果不是則返回重定向信息褂傀。重定向分為MOVED和ASK忍啤,ASK說明集群正在進行槽數(shù)據(jù)遷移,客戶端只在本次請求中做臨時重定向仙辟,不會更新本地槽緩存同波。MOVED重定向說明槽已經(jīng)明確分派到另一個節(jié)點鳄梅,客戶端需要更新槽節(jié)點緩存。
6)集群自動故障轉(zhuǎn)移過程分為故障發(fā)現(xiàn)和故障恢復参萄。節(jié)點下線分為主觀下線和客觀下線卫枝,當超過半數(shù)主節(jié)點認為故障節(jié)點為主觀下線時標記它為客觀下線狀態(tài)。從節(jié)點負責對客觀下線的主節(jié)點觸發(fā)故障恢復流程讹挎,保證集群的可用性。
7)開發(fā)和運維集群過程中常見問題包括:超大規(guī)模集群帶寬消耗吆玖,pub/sub廣播問題筒溃,集群節(jié)點傾斜問題,手動故障轉(zhuǎn)移沾乘,在線遷移數(shù)據(jù)等怜奖。
緩存設計
1)緩存的使用帶來的收益是能夠加速讀寫,降低后端存儲負載翅阵。
2)緩存的使用帶來的成本是緩存和存儲數(shù)據(jù)不一致性歪玲,代碼維護成本增大,架構(gòu)復雜度增大掷匠。
3)比較推薦的緩存更新策略是結(jié)合剔除滥崩、超時、主動更新三種方案共同完成讹语。
4)穿透問題:使用緩存空對象和布隆過濾器來解決钙皮,注意它們各自的使用場景和局限性。
5)無底洞問題:分布式緩存中顽决,有更多的機器不保證有更高的性能短条。有四種批量操作方式:串行命令、串行IO才菠、并行IO茸时、hash_tag。
6)雪崩問題:緩存層高可用赋访、客戶端降級可都、提前演練是解決雪崩問題的重要方法。
7)熱點key問題:互斥鎖进每、“永遠不過期”能夠在一定程度上解決熱點key問題汹粤,開發(fā)人員在使用時要了解它們各自的使用成本。
開發(fā)運維的“陷阱”
1)Linux相關優(yōu)化:
- vm.overcommit_memory建議為1田晚。
- Linux>3.5嘱兼,vm.swappiness建議為1,否則建議為0贤徒。
- Transparent Huge Pages(THP)建議關閉掉芹壕,但需要注意Linux發(fā)行版本改變了THP的配置位置汇四。
- 可以為Redis進程設置oom_adj,減少Redis被OOM killer殺掉的概率踢涌,但不要過度依賴此特性通孽。
- 建議對Redis所有節(jié)點所在機器使用NTP服務。
- 設置合理的ulimit保證網(wǎng)絡連接正常睁壁。
- 設置合理的tcp-backlog參數(shù)背苦。
2)理解Redis的持久化有助于解決flush操作之后的數(shù)據(jù)快速恢復問題。
3)Redis安全建議:
- 根據(jù)具體網(wǎng)絡環(huán)境決定是否設置Redis密碼潘明。
- rename-command可以偽裝命令行剂,但是要注意成本。
- 合理的防火墻是防止攻擊的利器钳降。
- bind可以將Redis的訪問綁定到指定網(wǎng)卡上厚宰。
- 定期備份數(shù)據(jù)應該作為習慣性操作。
- 可以適當錯開Redis默認端口啟動遂填。
- 使用非root用戶啟動Redis铲觉。
4)bigkey的危害不容忽視:數(shù)據(jù)傾斜、超時阻塞吓坚、網(wǎng)絡擁塞撵幽,可能是Redis生產(chǎn)環(huán)境中的一顆定時炸彈,刪除bigkey時通常使用漸進式遍歷的方式凌唬,防止出現(xiàn)Redis阻塞的情況并齐。
5)通過客戶端、代理客税、monitor况褪、機器抓包四種方式找到熱點key,這幾種方式各具優(yōu)勢更耻,具體使用哪種要根據(jù)當前場景來決定测垛。