■河南 劉景云
某單位的網(wǎng)站后臺采用的是Redis數(shù)據(jù)庫,為了便于管理Web應(yīng)用的緩存信息,使用Python開發(fā)了對應(yīng)的模塊,來提供的不同的HTTP接口,可以用來執(zhí)行數(shù)據(jù)查新、數(shù)據(jù)插入、數(shù)據(jù)測試等功能。
Redis是常用的鍵值存儲系統(tǒng),提供數(shù)據(jù)的高速處理功能。但在實(shí)際過程中,在查詢和插入數(shù)據(jù)時,接口的響應(yīng)時間卻比較緩慢,這給網(wǎng)站的管理和維護(hù)帶來了很大的問題。
對于上述故障,首先考慮的是否在系統(tǒng)配置上存儲問題,例如CPU資源占用率過高,內(nèi)存消耗嚴(yán)重等,有可能造成Redis反應(yīng)遲緩的問題。在后臺服務(wù)器上執(zhí)行“top”命令,在返回信息中的發(fā)現(xiàn)只有CPU 1的IOWait值較高,達(dá)到了89%,各進(jìn)程的CPU占用率都不高,Python和Redis-Server進(jìn)程的CPU使用率也不超過10%,存在很多的空閑內(nèi)存,從“l(fā)oad average”欄數(shù)據(jù)來看那么,系統(tǒng)的負(fù)荷并不高。
那么是不是因為系統(tǒng)的I/O性能存在瓶頸呢?為了便于發(fā)現(xiàn)問題,在前臺執(zhí)行連續(xù)查詢操作,不斷的從特定的接口讀取Redis緩存信息,之后在后臺服務(wù)薇上執(zhí)行“iostat-d-x 1”命令,對iostat的輸出信息進(jìn)行觀察,發(fā)現(xiàn)磁盤每秒寫入數(shù)據(jù)大約為7.9MB,“%util”的值為0,說明雖然存在一些I/O操作,但是沒有比較嚴(yán)重的I/O瓶頸問題。根據(jù)以上分析,雖然發(fā)現(xiàn)CPU,內(nèi)存和I/O都不存在明顯的問題,
但是對于上述案例來說,在從接口中讀取Redis數(shù)據(jù)時,涉及到的是只是數(shù)據(jù)的讀取操作,并沒有指定數(shù)據(jù)的頻繁寫入操作,根據(jù)上述iostat命令檢測信息,卻發(fā)現(xiàn)存在明顯的寫操作。
執(zhí)行“pidstat-d 1”命令,顯示所有進(jìn)程I/O使用情況,發(fā)現(xiàn)PID為8701的進(jìn)程在頻繁的寫入數(shù)據(jù),與其對應(yīng)的是“redis-server”進(jìn)程,說明Redis服務(wù)確實(shí)在不停的向磁盤寫入數(shù)據(jù)。
為了進(jìn)一步進(jìn)行觀察,執(zhí)行“strace-f-T-tt p 8701”命令,檢測該進(jìn)程的詳細(xì)的I/O操作情況,其中的“-f”參數(shù)表示跟蹤子進(jìn)程和子線程信息,“-T”參數(shù)表示顯示系統(tǒng)調(diào)用的時長,“tt”表示顯示跟蹤時間。
根據(jù)顯示的系統(tǒng)調(diào)用信息,發(fā)現(xiàn)頻繁出現(xiàn)諸如“epoll_pawit”“read”“write”“fdatasync”等系統(tǒng)調(diào)用行為,對于磁盤些操作來說,無疑是由于“write”“fdatasync”行為造成的。
執(zhí)行“l(fā)sof-p 8701”命令,顯示該進(jìn)程調(diào)用的操作對象信息。可以看到,其操作的對象包括Pipe管道、Eventpoll文件描述符、名為“/data/appendonly”的文件(編號為7),以及“Protocol:TCP”端口(編號為8)。
在這些對象中,只有涉及到到普通文件的操作,才會觸發(fā)磁盤些操作,即上述“write”和“fdatasync”調(diào)用行為針對的就是名為/data/appendonly”的文件。這其實(shí)和Redis的數(shù)據(jù)持久化有著緊密的聯(lián)系,因為它和Redis持久化中配置中的“appendonly”及“appendfync”項目有關(guān)。
在客戶端上執(zhí)行“rediscli config get 'append*'”命令,在返回信息中顯示“appendfync”參數(shù)的值為“always”,“appendonly”參數(shù)的值為“yes”。Redis的持久化指的是將數(shù)據(jù)存儲到斷電后不會丟失的設(shè)備中。
Redis支持RDB和AOF兩種持久化方式。前者是基于內(nèi)存快照的持久化方式,后者可以將所有的操作命令記錄下來形成日志文件,這樣即使Redis出現(xiàn)異常情況,也可以利用日志進(jìn)行數(shù)據(jù)的快速恢復(fù)。
在Redis配置文件中,將“appendonly”參數(shù)值設(shè)置為“yes”,表示啟用AOF功能。將“appendfsync”參數(shù)值設(shè)置為“always”,表示將每一個命令都立即同步到aof日志。如果設(shè)置為“everysec”,表示每秒鐘調(diào)用一次fsync操作,這樣即使出現(xiàn)問題,也只是丟失1秒鐘內(nèi)的數(shù)據(jù)。如果設(shè)置為“no”,表示交由操作系統(tǒng)處理。
快照方式會按照指定的周期,生成數(shù)據(jù)的快照,并最終保存到磁盤文件中,為了避免阻塞主進(jìn)程,Redis會利用Fork函數(shù)通過系統(tǒng)調(diào)用來創(chuàng)建一個子進(jìn)程,用來負(fù)責(zé)保存快照,該方式具有速度快性能好的特點(diǎn)。但其缺點(diǎn)是,在處理較大的數(shù)據(jù)量時,該子進(jìn)程會占用較大的內(nèi)存,保存數(shù)據(jù)比較耗時。如果在指定的時間間隔內(nèi)發(fā)生故障的話,就會丟失一定的數(shù)據(jù)。
AOF方式采用的是在文件尾部追加數(shù)據(jù),因此Redis寫入的的數(shù)據(jù)持久化顯得更加安全。利用“appendfsync”參數(shù)參數(shù),可以設(shè)置fsync策略,保證寫入的數(shù)據(jù)都保存在磁盤中。根據(jù)上面檢測的信息,說明“appendfsync”參數(shù)設(shè)置為“always”方式,說明在每次寫入數(shù)據(jù)時,都會觸發(fā)一次fsnc操作,所以造成了較大的磁盤I/O壓力。
為了驗證這一想法,可以借助于Strace這款工具加以檢測,Strace是一個集診斷、調(diào)試、統(tǒng)計與一體的工具,可以使用其對系統(tǒng)調(diào)用和信號傳遞進(jìn)行跟蹤分析。
例如,執(zhí)行“strace-f-p 8701-T-tt-e fdatasync”命令,對fsync調(diào)用情況進(jìn)行跟蹤,根據(jù)返回信息,可以發(fā)現(xiàn)每隔幾毫秒就會出現(xiàn)一個fdatasync調(diào)用信息,每次調(diào)用本身也會消耗一定的時間。根據(jù)上面的分析,可以看出之所以Redis出現(xiàn)反應(yīng)遲緩的問題,和其配置方式存在很大的關(guān)系。
之所以在讀取數(shù)據(jù)時,依然會出現(xiàn)寫磁盤的操作,可以執(zhí)行“strace-f-p 8701-T-tt”命令,會發(fā)現(xiàn)存在TCP Socket相關(guān)的數(shù)據(jù)讀寫操作,其中包含了“SADD”之類的指令,SADD指令的作用是將一個或多個成員元素加入到集合中。
執(zhí)行“l(fā)sof-i”命令,查看所有進(jìn)程的網(wǎng)絡(luò)訪問信息,在其中找到“redisserver”進(jìn)程,對應(yīng)的“FD”列中顯示使用了“8u”,在“NAME”列中顯示自身監(jiān)聽的端口(如“TCP 6379”),以及與其連接的端口(例如“TCP 59166”),而Python進(jìn)程正好連接在后面的端口上。
也就說是,雖然通過對應(yīng)接口執(zhí)行了讀操作,但是也涉及到了寫操作,Redis會將相關(guān)的數(shù)據(jù)保存到“appendonly.aof”的持久化文件中。
經(jīng)過上面一番分析,就已經(jīng)找到了引發(fā)上述故障的原因,這其實(shí)是由于兩方面的問題造成的,首先在Redis的配置信息中,“appendsync”參數(shù)設(shè)置為了“always”,造成Redis的所有寫操作都會頻繁的觸發(fā)fdatasync調(diào)用,造成時間延遲的現(xiàn)象,將其設(shè)置為“everysec”表示每秒同步一次,就可以滿足滿足實(shí)際的需求。
在客戶端執(zhí)行“rediscli config set appendsync everysec”命令,即可更改該參數(shù)。其次在開發(fā)的Python查詢接口模塊中,對緩存的使用存在不完善的地方。例如,將Redis作為臨時空間,來保存相關(guān)的數(shù)據(jù),從而造成在查詢時會調(diào)用Redis的SADD命令的情況。為此可以對程序進(jìn)行優(yōu)化,將這些查詢數(shù)據(jù)保存到內(nèi)存中即可。
完成了以上處理后,在客戶端利用HTTP接口執(zhí)行查詢和寫入等操作時,Redis的反應(yīng)速度大大提高,遲緩的故障徹底消失。