服務器端一般都是類Unix系統,以linux的CentOS使用為多,無論使用的是哪種類Unix系統,服務端都不會安裝窗口插件,而是使用命令和腳本來做一切事情,在這樣的場景下,登錄,執行命令,執行腳本,檢查服務運行狀態,檢查服務產生日志,檢查配置這些基本操作就會頻繁的用到,但在命令行模式下操作這些動作很繁瑣。比如檢查某個服務是否是運行狀態,需要先登錄服務器,然后在進程中再查找該服務的進程是否存在。這里就介紹怎樣通過web端來執行服務器端動作的方法,簡化操作,提升工作效率。
設計這個實現時比較了兩個實現方式:
1、通過java的Runtime類,執行命令和腳本。需要創建和維護一個工程,在工程里對接口權限控制也方便,但新加腳本和操作時需要修改工程,重新發布工程。
2、使用CGI接口,配置便捷,使用靈活,直接在服務器上寫腳本,通過接口的通用路徑就可以訪問使用,但無法控制接口訪問權限。
由于這個接口是使用在測試系統,方便,靈活是首選,權限問題就顯示的不那么重要了。
CGI是一個很古老的技術,后來隨著java servlet技術的興起,在生產場景已經沒有CGI的一點空間了,但它的靈活,便捷正好用在測試環境上。
tomcat和apache等中間件已經內置了CGI功能,只是默認是非啟用狀態。此處以tomcat為例,配置CGI接口。
修改點1、在Tomcat的 conf/web.xml 中釋放下面CGI相關的兩段內容
第一段:
<servlet>
<servlet-name>cgi</servlet-name>
<servlet-class>org.apache.catalina.servlets.CGIServlet</servlet-class>
<init-param>
<param-name>cgiPathPrefix</param-name>
<param-value>WEB-INF/cgi</param-value>
</init-param>
<load-on-startup>5</load-on-startup>
</servlet>
第二段:
<servlet-mapping>
<servlet-name>cgi</servlet-name>
<url-pattern>/cgi-bin/*</url-pattern>
</servlet-mapping>
修改點2、在Tomcat的 conf/context.xml 中給標簽增加屬性。
在</Context>標簽中添加屬性 privileged="true",因為默認情況下Tomcat是不允許web應用使用容器內的Servlet的,web應用只能使用自己項目的Servlet。
tomcat中開啟CGI配置時,沒有修改文件路徑和訪問路徑,那空項目就按照默認路徑創建目錄即可。
在tomcat路徑下,直接 mkdir -p webapps/test/WEB-INF/cgi,創建CGI項目目錄完畢。
然后在webapps/test/WEB-INF/cgi路徑下創建一個文件a,a的內容如下
#!/bin/bash
echo "Content-Type: text/plain"
echo
#上面內容是CGI腳本格式,必須存在
#下面內容是自定義要執行的動作
echo "Today is:"
date
http://localhost:8080/test/cgi-bin/a
4.1、實際場景
此時,CGI接口已經配置完畢了,盡管功能已經完畢,但還是不滿足實際的使用場景,現在的一個接口只對應一個功能,不能復用。要滿足實際的使用場景,就要參數化,傳遞的參數可以是腳本名稱,機器的ip,服務名稱,進程名稱等等,這樣接口的通用性就大大增加了。
4.2、初步解決方案
若想腳本參數化就要解決服務器端的傳參和url中傳參不一致的問題。url中使用“&”和“參數名=參數值”的傳參形式,而服務器端是使用的是空格+直接參數值得形式。需要在執行的a腳本前,先經過一個轉換腳本,轉換腳本需要先切割url,分成幾部分,取出參數和執行的a腳本,再重新拼在一起來執行,那就變成url需要先訪問轉換腳本,把實際的a腳本和參數都作為參數傳進來,處理流程變復雜了。
4.3、網上一個神腳本
直到后來在互聯網上最終找到了一個完美的解決方案。找到了一個名字叫“proccgi.sh”的腳本,腳本里的注釋中描述是 Frank Pilhofer在1995年寫的。我原封不動的拿過來使用了(腳本下載:鏈接:https://pan.baidu.com/s/1oZbN13Eog3OKld93f6hEkw?pwd=chen
提取碼:chen)。這個腳本設計的很巧妙,它不在傳輸中間處理url再拼接,而是把url直接都傳進執行的a腳本里,然后在a腳本中利用了“eval”命令的的二次掃描功能,掃描出需要的參數,然后把參數放一個特殊的key-value形式的環境變量里,使用的時候直接從環境變量里面去取。
例如:
url:http://localhost:8080/test/cgi-bin/a?ip=115&servername=customer&thread=aabbcc
a腳本改造如下:
#!/bin/bash
eval `proccgi.sh $*` # 解析參數
echo "Content-Type: text/plain"
echo
# ############
echo $FORM_ip
echo $FORM_servername
echo $FORM_thread
有以下幾種情況:
1、對頁面展示無樣式要求的,接口鏈接直接新開瀏覽器窗口,接口返回的數據會直接顯示在瀏覽器中。
2、頁面有格式的,需要通過ajax觸發接口,接口返回值通過innerHTML直接填充到頁面的展示區域。
3、對于那些耗時較長的任務,接口在還沒有返回值的時候,頁面停留在加載狀態,此時從頁面也無法判斷是否出現未知問題。這時可以給頁面放一個等待的圖片,定義一個標志位給它放一個默認值,然后js輪訓判斷這個標志位的值,當接口的shell處理完成,接口返回時,要變更標志位的值,輪訓發現變更后,就可以把接口返回內容替換掉等待圖片全部顯示在頁面上了。
在定義環境的時候,就盡量定義的通用一些,規律一些。這樣可以維護一些通用腳本,通過傳入變量參數來做動作。使在服務器中環境維護和定位問題都不再繁瑣。
作一封郵件和制作web頁面還是有很大不同的。當不同的瀏覽器都在不斷向標準靠近的同時,大多數郵件客戶端卻止步不前,甚至有一些是在退步的。在2007年,Microsoft 將 Outlook 的渲染引擎從 IE 轉換成 Word的渲染方式,而一些基于web的郵件客戶端,像Gmail和Hotmail,則增加了一些怪異的模式,還有Lotus Notes的一些技巧。
根據我的經驗來看,我們解決這些問題的關鍵是要關注下面三件事情。首先,保持簡單,你的郵件設計的越復雜,你的郵件在某個受歡迎的、不支持標準的客戶端上“抽風”的可能性就越大。其次,你需要將你的編碼技巧退步十年,這通常意味著我們要使用嵌套的表格,將CSS寫成內聯的形式等。最后,你需要對你的設計進行規律性的測試。
因為諸如Gmail和Outlook 2007 無法支持浮動(float)、外邊距(margin)、內填充(padding),你需要使用表格來作為你的郵件的框架。雖然表格嵌套的方法被廣泛支持,但是在對單元格的寬度、外邊距和內填充的處理方法并不一致。為了達到最優的效果,當制作表格結構時,請記住下面的技巧。
1、為每個單元格設置寬度,而不是表格
當你把表格寬度、td 寬度、td的填充和CSS的填充寫到一封郵件時,你看到的結果可能是每個郵件客戶端它們看上去都不一樣。最可靠的方法是我們將為表格的每個單元格(th,td)設置寬度,而不是表格(table)本身。如下:
永遠別指望郵件客戶端能夠計算出你沒有指定寬度的單元格的寬度。它絕對不會。同時也要避免使用基于百分比的寬度,像 Outlook 2007 這樣的客戶端從來不考慮這種寬度方式,特別是這些嵌套的表格。像素級視覺,如果你想對每個單元格做填充,可以使用表單的單元格填屬性或者用CSS的內填充,但是不要這兩種一起使用。
2、嵌套迷思
表格的嵌套相對于設置左右浮動和外邊距(margin)或者表單單元格填充的方法更加穩固。如果你能使用這種表格嵌套的方法達到相同的效果,這將會給你在那些蹩腳的(buggier)郵件終端上面獲得最好的結果。
3、使用一個容器表單來設置 body 背景色彩
很多郵件客戶端會忽略掉在CSS中或者<body>標簽中設置的背景色。針對這種情況,將你的整封郵件用一個寬度為100%的表單包起來,并且為其設置背景色。如下:
你可以使用同樣的方案在背景圖片的設置上,需要記住的是某些郵件客戶端是不支持背景圖片的,這樣你就需要設置一個背景顏色作為備份方案。
4、在單元格中避免使用多余的空格(whitespace)
盡最大可能,避免<td>標簽中出現空格。某些郵件客戶端(Yahoo!或者Hotmail)可能會在某些場景下,對單元格的上面或者下面增加額外的填充,把你的設計破壞掉。
當某些郵件設計師盡他們最大的努力去避免使用CSS時,他們又會去依賴夢魘般的<font>標簽,但實際情況是很多的CSS屬性是被大部分郵件客戶端支持的。請查看下面的跨郵件終端的綜合CSS支持列表list of CSS support,從中你也能發現一些安全的屬性和一些應該被避免使用的屬性。
1、將css寫成內聯(inline)的樣式
Gmail就是這方面的罪魁禍首。CSS被從<head>和<body>中剝離,我們別無選擇的會將樣式寫成內聯的形式。一個好消息是你可以完全自動化的完成轉化。像Premailer提供意見點擊的方式完成這一過程。我強烈建議你將此步驟作為你構建活動的最后一步,你就能感受到這個CSS的所有益處。
2、避免使用字體的簡寫和十六進制計數法
一部分郵箱客戶端會放棄對簡寫的css字體屬性的解析。比如,絕對不要將你的字體樣式設置成下面的樣子。如下:
相反,我們應該寫成下面的形式:
談到字體這個話題,我最近也在不同的郵件客戶端測試引用字體(@font-face)。結果是凄涼的,這些瀏覽器安全的字體在郵件中使用還是遙遙無期。
當我們用CSS來聲明顏色屬性時,有些郵件客戶端并不支持簡寫的16進制的顏色值,比如 color:#f60; 我們需要將其補充完整 color:#ff6600;。為了達到最優的效果,我們需要使用常規寫法。
像前面提到的單元格的間距,段落的間距也無法做到所有客戶端的一致。我看到過設計師使用兩個<br>或者用DIV寫上內聯(inline)的外邊距(margin)樣式彌補這個短板,但是我最近的測試顯示大多數情況下對段落的支持都還是比充足的(有一段時間 Yahoo! 根本不支持段落標簽)。
最好的實踐方法是對每個段落通過內聯(inline)的方法設置外邊距(margin),像下面這樣:
再次提示,在你構建郵件的時候通過在head標簽中增加樣式,然后通過Premailer將他們轉化成每個段落的內聯樣式。
如果你的設計對高度是很敏感的或者需要像素級別的完美,我強烈建議你不要將所有的段落寫到一起,而是將文本的格式化工作放到表單的單元格中來做。你可能會需要使用到表單的嵌套或者單元格填充(cellpadding)/CSS 來達到期望的樣子。下面就是一個例子:
某些郵件客戶端將會用他們的默認樣式覆蓋你的鏈接色,你可以通過兩部來防止其發生。第一,針對每一個鏈接設置一個內聯的(inline)的顏色:
接下來,增加一個冗余的 span 標簽在 a 標簽中。
也許這些方案看上去比較過激,如果這個顏色對你的設計很重要,這個多余的 span 標簽是你達到一致表現的最好解決方案。
很重要的一件需要牢記在心中的關于圖片的事情是你的訂閱者可能看不到你的圖片。如果你有這方面的準備,你就會保持你的內容簡單,并且重要的內容不通過圖片的形式來展示。
在這個思想的指導下,在使用HTML郵件的過程中,下面有一些基本的要領需要牢記:
1、避免占位圖片
雖然使用占位圖片和嵌套表格的方式在10年前很流行,許多郵件客戶端已經將其排除作為一種可靠的技術。很多客戶端會使用一個相同尺寸的空占位來替換圖片,另外一些會將所有的圖片移除。大多數郵件客戶端會給圖片賦予默認的圖片區塊,這將導致訂閱者的第一感覺很差。堅持將單元格賦予固定的寬度,讓其在沒有圖片的時候版式不會亂掉。
2、將圖片定義尺寸
如果你沒有給每個圖片設置尺寸,當圖片沒有被下載時,有些客戶端會自己發明一個他們自己的尺寸,你的版式就亂掉了。同時,確保你的所有圖片在被用到郵件中前,都被賦予了正確的尺寸。某些客戶端會忽略你代碼中設置的尺寸,而去使用真實的圖片尺寸。
3、避免使用 PNG 圖片
Lotus Notes 6 和 7 并不支持 8位(8-bit)和24位(24-bit)的 PNG 圖片,所以需要使用GIF或者JPG格式的圖片,即使這會增加而外的圖片大小。
4、為背景圖片提供備份的顏色
Outlook 2007 不支持背景圖片(aside from this hack to get full page background images working)。如果你想在你的設計中使用背景圖像,提供一個背景色作為備份支持方案。這樣就能同時解決圖片被屏蔽和Outlook 2007的問題。
5、不要忘記標注替代文本(alt text)
缺少標準的支持意味著郵件客戶端對語義化和訪問性良好的HTML郵件的破壞性是很大的。即使這樣,從圖片可能被屏蔽角度看,提供替代文本也是很重要的。這樣即使圖片在默認狀態下被限制,大多數郵件客戶端也能顯示提供的文本來替代。另外還需要技術的是某些客戶端,比如 Outlook 2007, Hotmail 和 Apple Mail 在圖片被屏蔽的時候,并不提供替代文本(alt text).
6、針對 Hotmail 使用顯示hack
令人費解的是,Windows Live Hotmail 對每個圖片增加了幾個像素的填充。一個變通的方案就是使用下面的顯示屬性來解決這個問題。
這樣就能移除掉Hotmail的填充值,但是你也可能會給其它客戶端埋下隱患。
7、避免使用浮動屬性(float)
Outlook 2007 和早期版本的 Notes 并不支持浮動屬性(float)。在郵件中我們可以使用對齊屬性在針對圖像標簽做到浮動圖片的目的。
如果你在 Yahoo!的郵件中發現圖片的怪異表現,增加 align="top" 可能能夠解決你遇到的問題。
由于缺少 Javascript 或者其他對象標簽(object tag)的支持,視頻郵件最大的程度就是gif動畫(如果你認為那是視頻的話)。盡管如此,我最近做的一些關于用html5 videio 標簽的測試結果,還是讓人感覺不錯。
HTML 5的標簽目前在一部分郵件終端是無法運行的,包括 Apple Mail,Entourage 2008, MobileMe 和 iPhone.作為如果視頻不被支持的補救方法,你可以提供穩定的備選內容,比如gif 動畫或者一個可以點擊到瀏覽器播放視頻的圖片。
當然,你是否需要將視頻添加到你的郵件里面,那就是另外一個議題了,如果你的答案是肯定的,你可以使用這些代碼案例。
移動端有機胺的情況近期顯得比較雜亂了,隨著iPhone,Android的發明和Palm和RIM的改進,認為移動端電子郵件終端不重要的年代一去不復返了。
為了給移動端訂閱用戶良好的體現,我們在編碼的過程中也有幾個關鍵點需要牢記心中。
1、保持寬度小于600像素
受限于郵件客戶端的視窗,這條規則來移動視窗到來之前的年代就很重要。事實上,iphone 的視窗是320像素,Droid是480像素,Blackberry大概360像素。堅持最大600像素寬的設計,能夠讓你的郵件縮小到上面提到的設備上面依然可讀。這個尺寸在桌面端和web端的預覽效果也很好。
2、注意文本尺寸的自動調整
作為一個好的特性,基于webkit郵件客戶端(比如 iPhone, Pre 和 Android) 能夠自動調整文本的大小來提高閱讀性。如果你的測試結果表明這項特點給你帶來的好處是破外了你的設計,你可以通過下面的屬性禁用:
雖然近幾年郵件客戶端對標準的支持并沒有取得長足的進步,但是某些郵件客戶端的改變卻從未停止(有好有壞),基于 web 的客戶端,如 Yahoo!、hotmail 和 Gmail 在這方面乏善可陳。我看到過無數次可行的設計方案被停止支持,沒有任何解釋。
基于這個原因,你也要對你的郵件設計保持規律的測試。我發現每個月進行一些快速的測試的小技巧,特別基于web的客戶端。好的消息是經過幾次設計和測試,你將會從這些雜亂無章中找到規律。一些潛在的陷阱將變的可以預計,一個對郵箱友好的設計模型也會在你心中成型。
本文參考“新浪UED”:創建堅如磐石的HTML郵件
、知識結構及面試題目分析
緩存技術的大規模使用是互聯網架構區別于傳統 IT 技術最大的地方,是整體高并發高性能架構設計中是重中之重的關鍵一筆,也是互聯網公司比較偏好的面試題目。按照在軟件系統中所處位置的不同,緩存大體可以分為三類:客戶端緩存、服務端緩存、網絡中的緩存;根據部署方式大體可分為:本地緩存和分布式緩存。專欄將以分布式緩存為重點,挑選其中應用最廣的 memcached、redis 分別予以介紹,同時兼顧其他緩存方案,從部署、設計、應用場景等方面展開。
二、典型面試例題及思路分析
問題 1:memchaced 中的一致性哈希算法是怎樣工作的?
在計算一致性哈希時采用一般采用如下步驟:
(1) 首先求出 memcached 服務器(節點)的哈希值,并將其配置到 0~2^32-1 的圓上。
(2) 然后采用同樣的方法求出存儲數據的鍵的哈希值,并映射到相同的圓上。
(3)然后從數據映射到的位置開始順時針查找,將數據保存到找到的第一個服務器上。如果超過 2^32-1 仍然找不到服務器,就會保存到第一臺 memcached 服務器上。
點評:
傳統的哈希算法最常見的是哈希取模算法,即集群中可用機器節點數量為 N,那么 key 值為 K 的的數據請求會路由到 hash (K) mod N 對應的節點。這種算法簡單,但并不適用于分布式系統。在分布式系統中,每個節點都可能失效,且節點會隨時動態增減,如果用 hash 取模算法,會導致節點增減時的大量緩存無法命中,瞬間穿透緩存,給下游 DB 等系統帶來極高的負載,甚至引起宕機。
在分布式緩存中,判定哈希算法好壞有三個標準:
(1)平衡性 (Balance):指哈希的結果能夠盡可能分布到所有的緩存中去,盡可能地利用緩存機器;
(2)單調性 (Monotonicity):是哈希的結果盡可能地保證原有已分配的內容可以被映射到原有緩存中去,避免在節點增減過程中導致不能命中;
(3)分散性 (Spread):是指同一個哈希結果,應盡量存儲在同一個緩存機器,以提升系統存儲的效率。分散性的定義就是上述情況發生的嚴重程度。好的哈希算法應能夠盡量避免不一致的情況發生,也就是盡量降低分散 性。
相較于哈希取模算法,一致性哈希能比較好地解決單調性(Monotonicity)和分散性 (Spread) 等問題,我們將上一致性哈希的計算步驟更細化一下:
(1)服務器初始化。
整個哈希值空間虛擬成一個有 2^32 節點的圓環,并按順時針方向組織。假設有 Node A、Node B、Node C、Node D 四臺服務器,計算得到各服務器的哈希值(通常使用 ip 或主機名作為關鍵字進行哈希),分布如下:
假設有四個數據對象 Object A、Object B、Object C、Object D,** 計算得到其哈希值,并映射到上述虛擬圓環的節點上,根據一致性哈希算法,從此位置出發沿環順時針 “行走”,第一臺遇到的服務器就是其應該定位到的服務器。** 在這里,Object A 會存儲到 Node A 上,Object B 會存儲到 Node B 上,Object C 會存儲到 Node C 上,Object D 會存儲到 Node D 上。
(3)單調性分析
假設在 Node B 和 Node C 之間新增一個 Node X,可以看到 Object C 被重定位到 Node X,而其余的三個數據對象 Object A/Object B/Object D 都不會有影響。在一致性哈希算法中,如果集群中增加或減少一臺服務器,則受影響的數據僅僅是此服務器到其環空間中前一臺服務器(即沿著逆時針方向行走遇到的第一臺服務器)之間數據,其它數據不會受到影響,因此具有較好的容錯性和可擴展性。
(4)虛擬化
在一致性哈希算法中,如果服務節點太少(比如說兩臺),容易引起數據分布不均。為此,一致性哈希算法引入了虛擬節點機制,即將每一個物理服務節點拆成若干個虛擬的邏輯服務節點(具體做法可以在服務器 ip 或主機名的后面增加編號來實現),這樣定位算法會先定位到邏輯服務節點上去,再由邏輯節點映射到真正的物理節點。
問題 2:redis 和 memcached 的區別是什么?
(1)數據類型支持不同。 memcached 僅支持簡單的 key-value 結構的數據類型,復雜的對象需要客戶端自己處理;Redis 支持的數據類型要豐富得多。最為常用的數據類型主要由五種:String、Hash、List、Set 和 Sorted Set。
(2)持久化支持不同,memcached 不支持數據持久存儲 ,數據一直在內存中;redis 支持數據落地持久化存儲,可以將內存中的數據保持在磁盤中,因此在某一時刻,redis 中并不是所有的數據都一直存儲在內存中;
(3)分布式支持不同。memcached 一般是在客戶端通過一致性哈希等算法來實現分布式存儲; Redis 更偏向于在服務器端構建分布式存儲,支持主從復制等集群模式;
(4)內存管理不同。memcached 默認使用 Slab Allocation 機制管理內存,其主要思想是按照預先規定的大小呈階梯狀分配好,會根據接收到數據的大小選擇一個最合適的 Slab Class 進行存儲,該方法可以避免內存碎片問題,可也不可避免地出現一定的空間浪費;redis 則采用包裝過的 mallc/free 來分配內存,什么時候需要什么時候分配,更簡單一些。
(5)應用場景不同。 memcached 是一個分布式內存對象緩存系統(distributed memory object caching system,),旨在通過減輕數據庫負載來加速動態 Web 應用程序;redis 是內存中的數據結構存儲系統(in-memory data structure store),它可以用作數據庫、緩存和消息中間件。
點評:
這個題目也是半開放式的,這類題目之前也出現得比較多了,回答可能會有好幾個差異點,但其實除了重要的差異點外,其他差異點多一個少一個不會影響大局,關鍵還是要有自己的理解,不要表現得像死記硬背。
當然本題目的問法比較直白,在實際上的面試中可能會換一種問法,比如說 “你們的系統當時為什么選擇 redis/memcached 作為緩存方案,為什么沒有選擇 memcached/redis” 等等,本質上是想考察做技術選型背后的邏輯。通常來講:redis 比 memcached 的功能多一些,實現也更復雜。 不過 memcached 更專注于保存 key-value 數據(這已經能滿足大多數使用場景了),而 redis 提供更豐富的數據結構及其他的一些功能。
三、總結
像 memcached 這類分布式緩存在互聯網場景下大量應用,因此也或多或少地為開發人員所知。但是大家在簡歷上寫的時候還是要實事求是地寫,知道就是知道,了解就是了解,熟悉就是熟悉,如果僅僅是了解,但是寫成了熟悉,面試官就可能逮著問,回答不上來還可能留下不誠實的印象。其實像這 memcached 這類緩存,即使手中的業務一時半會兒用不上,如果自己想了解的,也可以簡單地在線下環境搭一個,可以去體驗一下。畢竟知道和動手用過,還是有一定差距的。
四、擴展閱讀及思考題
鏈接: Memcached slab 分配策略.
鏈接: Memcached 官網.
問:適用memcached的業務場景?
1)如果網站包含了訪問量很大的動態網頁,因而數據庫的負載將會很高。由于大部分數據庫請求都是讀操作,那么memcached可以顯著地減小數據庫負載。
2)如果數據庫服務器的負載比較低但CPU使用率很高,這時可以緩存計算好的結果( computed objects )和渲染后的網頁模板(enderred templates)。
3)利用memcached可以緩存session數據、臨時數據以減少對他們的數據庫寫操作。
4)緩存一些很小但是被頻繁訪問的文件。
5)緩存Web 'services’或RSS feeds的結果.。
問:不適用memcached的業務場景?
1)緩存對象的大小大于1MB
Memcached本身就不是為了處理龐大的多媒體(large media)和巨大的二進制塊(streaming huge blobs)而設計的。
2)key的長度大于250字符
3)虛擬主機不讓運行memcached服務
如果應用本身托管在低端的虛擬私有服務器上,像vmware, xen這類虛擬化技術并不適合運行memcached。Memcached需要接管和控制大塊的內存,如果memcached管理的內存被OS或 hypervisor交換出去,memcached的性能將大打折扣。
4)應用運行在不安全的環境中
Memcached為提供任何安全策略,僅僅通過telnet就可以訪問到memcached。如果應用運行在共享的系統上,需要著重考慮安全問題。
5)業務本身需要的是持久化數據或者說需要的應該是database
問:能夠遍歷memcached中所有的item嗎?
不能,這個操作的速度相對緩慢且阻塞其他的操作(這里的緩慢時相比memcached其他的命令)。memcached所有非調試(non-debug)命令,例如add, set, get, fulsh等無論memcached中存儲了多少數據,它們的執行都只消耗常量時間。任何遍歷所有item的命令執行所消耗的時間,將隨著memcached中數據量的增加而增加。當其他命令因為等待(遍歷所有item的命令執行完畢)而不能得到執行,因而阻塞將發生。
問:memcached和MySQL的query cache相比,有什么優缺點?
缺點:
1)相比MySQL的query cache,把memcached引入應用中需要不少的工作量。MySQL的query cache,可以自動地緩存SQL查詢的結果,被緩存的SQL查詢可以被反復、快速的執行。
優點:
1)當修改表時,MySQL的query cache會立刻被刷新(flush)。當寫操作很頻繁時,MySQL的query cache會經常讓所有緩存數據都失效。
2)在多核CPU上,MySQL的query cache會遇到擴展問題(scalability issues)。在多核CPU上,query cache會增加一個全局鎖(global lock), 由于需要刷新更多的緩存數據,速度會變得更慢。
3)在MySQL的query cache中,是不能存儲任意的數據的(只能是SQL查詢結果)。利用memcached,我們可以搭建出各種高效的緩存。比如,可以執行多個獨立的查詢,構建出一個用戶對象(user object),然后將用戶對象緩存到memcached中。而query cache是SQL語句級別的,不可能做到這一點。在小的網站中,query cache會有所幫助,但隨著網站規模的增加,query cache的弊將大于利。
4)query cache能夠利用的內存容量受到MySQL服務器空閑內存空間的限制。給數據庫服務器增加更多的內存來緩存數據,固然是很好的。但是,有了memcached,只要您有空閑的內存,都可以用來增加memcached集群的規模,然后您就可以緩存更多的數據。
問:memcached和服務器的local cache(比如PHP的APC、mmap文件等)相比,有什么優缺點?
1)首先,local cache面臨著嚴重的內存限制,能夠利用的內存容量受到(單臺)服務器空閑內存空間的限制。
2)local cache有一點比memcached和query cache都要好,那就是它不但可以存儲任意的數據,而且沒有網絡存取的延遲。因此,local cache的數據查詢更快。考慮把highly common的數據放在local cache中吧。如果每個頁面都需要加載一些數量較少的數據,可以考慮把它們放在local cached。
3)local cache缺少集體失效(group invalidation)的特性。在memcached集群中,刪除或更新一個key會讓所有的觀察者覺察到。但是在local cache中, 我們只能通知所有的服務器刷新cache(很慢,不具擴展性)或者僅僅依賴緩存超時失效機制。
問:memcached如何處理容錯的?
在節點失效的情況下,集群沒有必要做任何容錯處理。如果發生了節點失效,應對的措施完全取決于用戶。節點失效時,下面列出幾種方案供您選擇:
1)忽略它! 在失效節點被恢復或替換之前,還有很多其他節點可以應對節點失效帶來的影響。
2)把失效的節點從節點列表中移除。做這個操作千萬要小心!在默認情況下(余數式哈希算法),客戶端添加或移除節點,會導致所有的緩存數據不可用!因為哈希參照的節點列表變化了,大部分key會因為哈希值的改變而被映射到(與原來)不同的節點上。
3)啟動熱備節點,接管失效節點所占用的IP。這樣可以防止哈希紊亂(hashing chaos)。
4)如果希望添加和移除節點,而不影響原先的哈希結果,可以使用一致性哈希算法(consistent hashing)。
5)兩次哈希(reshing)。當客戶端存取數據時,如果發現一個節點down了,就再做一次哈希(哈希算法與前一次不同),重新選擇另一個節點(需要注意的時,客戶端并沒有把down的節點從節點列表中移除,下次還是有可能先哈希到它)。如果某個節點時好時壞,兩次哈希的方法就有風險了,好的節點和壞的節點上都可能存在臟數據(stale data)。
問:memcached是如何做身份驗證的?
沒有身份認證機制!memcached是運行在應用下層的軟件(身份驗證應該是應用上層的職責)。memcached的客戶端和服務器端之所以是輕量級的,部分原因就是完全沒有實現身份驗證機制。這樣,memcached可以很快地創建新連接,服務器端也無需任何配置。如果您希望限制訪問,您可以使用防火墻,或者讓memcached監聽unix domain socket。
問:memcached能接受的key的最大長度是多少?
memcached能接受的key的最大長度是250個字符。需要注意的是,250是memcached服務器端內部的限制。如果使用的Memcached客戶端支持"key的前綴"或類似特性,那么key(前綴+原始key)的最大長度是可以超過250個字符的。推薦使用較短的key,這樣可以節省內存和帶寬。
問:memcached對item的過期時間有什么限制?
item對象的過期時間最長可以達到30天。memcached把傳入的過期時間(時間段)解釋成時間點后,一旦到了這個時間點,memcached就把item置為失效狀態。
問:memcached最大能存儲多大的單個item?
memcached最大能存儲1MB的單個item。如果需要被緩存的數據大于1MB,可以考慮在客戶端壓縮或拆分到多個key中。
問:為什么單個item的大小被限制在1M byte之內?
簡單的回答:因為內存分配器的算法就是這樣的。
詳細的回答:
1)Memcached的內存存儲引擎,使用slabs來管理內存。內存被分成大小不等的slabs chunks(先分成大小相等的slabs,然后每個slab被分成大小相等chunks,不同slab的chunk大小是不相等的)。chunk的大小依次從一個最小數開始,按某個因子增長,直到達到最大的可能值。如果最小值為400B,最大值是1MB,因子是1.20,各個slab的chunk的大小依次是:slab1 - 400B;slab2 - 480B;slab3 - 576B …slab中chunk越大,它和前面的slab之間的間隙就越大。因此,最大值越大,內存利用率越低。Memcached必須為每個slab預先分配內存,因此如果設置了較小的因子和較大的最大值,會需要為Memcached提供更多的內存。
2)不要嘗試向memcached中存取很大的數據,例如把巨大的網頁放到mencached中。因為將大數據load和unpack到內存中需要花費很長的時間,從而導致系統的性能反而不好。如果確實需要存儲大于1MB的數據,可以修改slabs.c:POWER_BLOCK的值,然后重新編譯memcached;或者使用低效的malloc/free。另外,可以使用數據庫、MogileFS等方案代替Memcached系統。
問:memcached的內存分配器是如何工作的?為什么不適用malloc/free!?為何要使用slabs?
實際上,這是一個編譯時選項。默認會使用內部的slab分配器,而且確實應該使用內建的slab分配器。最早的時候,memcached只使用malloc/free來管理內存。然而,這種方式不能與OS的內存管理以前很好地工作。反復地malloc/free造成了內存碎片,OS最終花費大量的時間去查找連續的內存塊來滿足malloc的請求,而不是運行memcached進程。slab分配器就是為了解決這個問題而生的。內存被分配并劃分成chunks,一直被重復使用。因為內存被劃分成大小不等的slabs,如果item的大小與被選擇存放它的slab不是很合適的話,就會浪費一些內存。
問:memcached是原子的嗎?
所有的被發送到memcached的單個命令是完全原子的。如果您針對同一份數據同時發送了一個set命令和一個get命令,它們不會影響對方。它們將被串行化、先后執行。即使在多線程模式,所有的命令都是原子的。然是,命令序列不是原子的。如果首先通過get命令獲取了一個item,修改了它,然后再把它set回memcached,系統不保證這個item沒有被其他進程(process,未必是操作系統中的進程)操作過。
memcached 1.2.5以及更高版本,提供了gets和cas命令,它們可以解決上面的問題。如果使用gets命令查詢某個key的item,memcached會返回該item當前值的唯一標識。如果客戶端程序覆寫了這個item并想把它寫回到memcached中,可以通過cas命令把那個唯一標識一起發送給memcached。如果該item存放在memcached中的唯一標識與您提供的一致,寫操作將會成功。如果另一個進程在這期間也修改了這個item,那么該item存放在memcached中的唯一標識將會改變,寫操作就會失敗。
問:什么時候失效的數據項會從緩存中刪除?
memcached 使用懶失效,當客戶端請求數據項時, memcached 在返回數據前會檢查失效時間來確定數據項是否已經失效。同樣地,當添加一個新的數據項時,如果緩存已經滿了, memcached 就會先替換失效的數據項,然后才是緩存中最少使用的數據項。
問:在設計應用時,可以通過Memcached緩存那些內容?
1)緩存簡單的查詢結果:查詢緩存存儲了給定查詢語句對應的整個結果集,最合適緩存那些經常被用到,但不會改變的 SQL 語句對查詢到的結果集,比如載入特定的過濾內容。記住,如果查詢語句對應的結果集改變,該結果集不會展現出來。這種方法不總是有用,但它確實讓工作變得比較快。
2)緩存簡單的基于行的查詢結果:基于行的緩存會檢查緩存數據key的列表,那些在緩存中的行可以直接被取出,不在緩存中的行將會從數據庫中取出并以唯一的鍵為標識緩存起來,最后加入到最終的數據集中返回。隨著時間的推移,大多數數據都會被緩存,這也意味著相比與數據庫,查詢語句會更多地從 memcached 中得到數據行。如果數據是相當靜態的,我們可以設置一個較長的緩存時間。
基于行的緩存模式對下面這種搜索情況特別有用:數據集本身很大或是數據集是從多張表中得到,而數據集取決于查詢的輸入參數但是查詢的結果集之間的有重復部分。
比如,如果你有用戶 A , B , C , D , E 的數據集。你去點擊一張顯示用戶 A , B , E 信息的頁面。首先, memcached 得到 3 個不同的鍵,每個對應一個用戶去緩存中查找,全部未命中。然后就到數據庫中用 SQL 查詢得到 3 個用戶的數據行,并緩存他們?,F在,你又去點擊另一張顯示顯示 C , D , E 信息的頁面。當你去查找 memcached 時, C , D 的數據并沒有被命中,但我們命中了 E 的數據。然后從數據庫得到 C , D 的行數據,緩存在 memcached 中。至此以后,無論這些用戶信息怎樣地排列組合,任何關于 A , B , C , D , E 信息的頁面都可以從 memcached 得到數據了。
3)緩存的不只是 SQL 數據,可以緩存最終完成的部分顯示頁面,以節省CPU計算時間
例如正在制作一張顯示用戶信息的頁面,你可能得到一段關于用戶的信息(姓名,生日,家庭住址,簡介),然后你可能會將 XML 格式的簡介信息轉化為 HTML 格式或做其他的一些工作。相比單獨存儲這些屬性,你可能更愿意存儲經過渲染的數據塊。那時你就可以簡單地取出被預處理后的 HTML 直接填充在頁面中,這樣節省了寶貴的 CPU 時間。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。