表格儲存體的效能和延展性檢查清單
Microsoft 開發了許多實證做法,以便使用資料表儲存體開發高效能應用程式。 此檢查清單會識別可供開發人員遵循以將效能最佳化的重要做法。 在設計應用程式時和整個過程中,請記住這些做法。
Azure 儲存體具有容量、交易速率和頻寬的延展性和效能目標。 如需 Azure 儲存體帳戶延展性目標的詳細資訊,請參閱標準儲存體帳戶的延展性與效能目標及資料表儲存體的延展性與效能目標。
檢查清單
本文會將效能實證做法整理為您在開發表格儲存體應用程式時所能遵循的檢查清單。
延展性目標
如果您的應用程式達到或超過任何可擴縮性目標,它可能會遇到增加的交易延遲或節流。 當 Azure 儲存體對您的應用程式進行節流時,該服務會開始傳回 503 (伺服器忙碌) 或 500 (作業逾時) 錯誤碼。 保持在延展性目標的限制範圍內以避免這些錯誤,對於提升應用程式效能很重要。
如需深入了解表格服務的延展性目標,請參閱適用於表格儲存體的延展性和效能目標。
儲存體帳戶的數目上限
如果您很接近特定訂用帳戶/區域組合允許的儲存體帳戶數目上限,您是否使用多個儲存體帳戶進行分區,以增加每秒的輸入、輸出、I/O 作業 (IOPS) 或容量? 在此案例中,可能的話,我們建議您善用提高儲存體帳戶的限制,以降低您的工作負載所需的儲存體帳戶數目。 請連絡 Azure 支援來要求提高儲存體帳戶的限制。
容量和交易目標
如果您的應用程式即將達到單一儲存體帳戶的延展性目標,您可以考慮採用下列其中一個方法:
- 重新思考會造成您的應用程式達到或超出延展性目標的工作負載。 您是否能夠以不同的方式設計工作負載,以使用較少的頻寬或容量或使用較少的交易?
- 如果您的應用程式必須超出其中一個延展性目標,則建立多個儲存體帳戶,並將您的應用程式資料在這幾個儲存體帳戶中進行分割。 如果您要使用此模式,那麼請確定設計您的應用程式,以便日後可以增加更多儲存體帳戶以取得負載平衡。 除了儲存資料、進行交易或傳輸資料等使用方式外,儲存體帳戶本身不會有其他費用。
- 如果您的應用程式很接近頻寬目標,請考慮在用戶端壓縮資料,以便降低將資料傳送至 Azure 儲存體所需的頻寬。 雖然壓縮資料可節省頻寬並改善網路效能,但也可能對效能造成負面影響。 針對用戶端上資料壓縮和解壓縮,評估額外處理需求所造成的效能影響。 請記住,儲存壓縮的資料可能會使疑難排解變得更困難,因為使用標準工具來檢視資料可能更具挑戰性。
- 如果您的應用程式很接近延展性目標,則務必使用指數輪詢進行重試。 最好是藉由實作本文所述的建議,嘗試避免達到延展性目標。 不過,使用指數輪詢進行重試會讓您的應用程式無法快速重試,這可能會使節流變差。 如需詳細資訊,請參閱逾時和伺服器忙碌錯誤一節。
資料作業的目標
當進入儲存體帳戶的流量增加時,Azure 儲存體會進行負載平衡,但如果流量突然激增的話,您可能無法立即取得這麼大量的輸送量。 您要有心裡準備,知道會在高載期間看到節流和 (或) 逾時的情形,原因是 Azure 儲存體會自動為資料表進行負載平衡。 緩慢增加通常會提供較好的結果,原因是系統會有時間適當地進行負載平衡。
每秒的實體數 (儲存體帳戶)
存取資料表的延展性限制為一個帳戶每秒最高 20,000 個實體 (每個 1 KB)。 一般來說,已插入、更新、刪除或掃描的每個實體都會算在這個目標內。 因此包含 100 個實體的批次插入會算為 100 個實體。 掃描 1,000 個實體並傳回 5 的查詢會算為 1,000 個實體。
每秒的實體數 (資料分割)
在單一資料分割內,存取資料表的延展性目標為每秒 2,000 個實體 (每個 1 KB),使用上一節所述的相同計算方法。
網路
應用程式的實體網路限制可能會對效能產生重大影響。 下列各節說明使用者可能會遇到的部分限制。
用戶端網路功能
如下列各節所述,網路連結的頻寬和品質在應用程式效能中扮演重要角色。
輸送量
頻寬的問題經常是用戶端的功能。 較大型的 Azure 執行個體擁有較大容量的 NIC,因此,如果您需要單一機器的較高網路限制,您應考慮使用較大型的執行個體或更多的 VM。 如果您從內部部署應用程式存取 Azure 儲存體,則適用相同的規則:了解用戶端裝置的網路功能和與 Azure 儲存體位置的網路連線能力,以及視需要進行改善或設計您的應用程式以便使用其功能。
連結品質
與任何網路使用方式一樣,請記住導致錯誤和封包遺失的網路狀況會減慢有效的輸送量。 使用 WireShark 或 NetMon 可能有助於診斷此問題。
Location
在任何分散式環境中,將用戶端放置於伺服器附近可提供最佳的效能。 若要以最低的延遲時間存取 Azure 儲存體,對用戶端而言的最佳位置是在同一個 Azure 區域內。 例如,如果您擁有使用 Azure 儲存體的 Azure Web 應用程式,則將這兩者置於單一區域內 (例如,美國西部或東南亞)。 共置資源可降低延遲和成本,因為單一區域內的頻寬使用量是免費的。
如果將存取 Azure 儲存體的用戶端應用程式不在 Azure 中託管 (例如行動裝置應用程式或內部部署企業服務),則將儲存體帳戶置於這些用戶端附近的區域內可以降低延遲。 如果您的用戶端分佈廣闊 (例如,有些在北美洲,有些在歐洲),則考慮在每個區域使用一個儲存體帳戶。 如果應用程式儲存的資料特定用於個別使用者,且不需要複寫儲存體帳戶之間的資料,則此方法較容易實作。
SAS 和 CORS
假設您需要授權在使用者的網頁瀏覽器或在行動電話應用程式中執行的程式碼 (例如 JavaScript),以存取 Azure 儲存體中的資料。 其中一種方法是建置可作為 Proxy 的服務應用程式。 使用者的裝置會向服務進行驗證,然後再授權存取 Azure 儲存體資源。 如此一來,您可以避免在未受到保護的裝置上公開您的儲存體帳戶金鑰。 不過,因為在使用者裝置與 Azure 儲存體之間轉送的所有資料都必須通過服務應用程式,所以此方法會在服務應用程式上加上顯著負荷。
使用共用存取簽章 (SAS),即可避免使用服務應用程式作為 Azure 儲存體的 Proxy。 使用 SAS,您可以藉由使用有限的存取權杖,讓使用者的裝置能直接對 Azure 儲存體提出要求。 例如,如果使用者想要將相片上傳至您的應用程式,則您的服務應用程式可以產生 SAS,並將它傳送到使用者的裝置。 SAS 權杖可以授與在指定的時間間隔內寫入 Azure 儲存體資源的權限,該時間間隔後 SAS 權杖就會過期。 如需關於 SAS 的詳細資訊,請參閱使用共用存取簽章 (SAS) 對 Azure 儲存體資源授與有限存取權。
通常,在某個網域上由網站託管的頁面中,網頁瀏覽器不允許 JavaScript 對另一個網域執行特定作業 (例如寫入作業)。 這項原則稱為同源原則,可防止某頁上的惡意程式碼取得另一個網頁上資料的存取權。 不過,在雲端建置解決方案時,同源原則會是一項限制。 跨原始資源共用 (CORS) 是一項瀏覽器功能,可讓目標網域與信任源自來源網域之要求的瀏覽器進行通訊。
例如,假設在 Azure 中執行的 Web 應用程式向 Azure 儲存體帳戶提出資源要求。 Web 應用程式是來源網域,而儲存體帳戶是目標網域。 您可以為任何 Azure 儲存體服務設定 CORS,以便與網頁瀏覽器進行通訊,而該網頁瀏覽器會向 Azure 儲存體所信任的來源網域提出要求。 如需 CORS 的詳細資訊,請參閱 Azure 儲存體的跨原始資源共用 (CORS) 支援。
SAS 和 CORS 都可協助您避免 Web 應用程式上不必要的負載。
批次交易
資料表服務支援在位於相同資料表且屬於相同資料分割群組的實體上進行批次交易。 如需詳細資訊,請參閱執行實體群組交易。
.NET 組態
對於使用 .NET Framework 的專案,本節會列出一些可用來大幅改善效能的快速組態設定。 如果使用 .NET 以外的其他語言,請查看類似的概念是否適用於您所選擇的語言。
提高預設的連線限制
注意
本節適用於使用 .NET Framework 的專案,因為連線共用是由 ServicePointManager 類別所控制。 .NET Core 引進了連線集區管理的重大變更,其中連線共用發生在 HttpClient 層級,且依預設不會限制集區大小。 這表示 HTTP 連線會自動調整以符合您的工作負載。 建議您盡可能使用最新版本的 .NET,以利用效能增強功能。
對於使用 .NET Framework 的專案,您可以使用下列程式碼來增加預設連線限制 (此值在用戶端環境中通常為 2,或在伺服器環境中通常為 10) 提高到 100。 一般而言,您應將此值大約設為應用程式所使用的執行緒數量。 在開啟任何連線之前設定連線限制。
ServicePointManager.DefaultConnectionLimit = 100; //(Or More)
若要深入了解 .NET Framework 中的連線集區限制,請參閱 .NET Framework 連線集區限制和全新的 Azure SDK for .NET (英文)。
若是其他程式設計語言,請參閱文件以確定如何設定連線限制。
提高執行緒數目上限
如果您同時使用同步呼叫與非同步工作,則可增加執行緒集區中的執行緒數目:
ThreadPool.SetMinThreads(100,100); //(Determine the right number for your application)
如需詳細資訊,請參閱 ThreadPool.SetMinThreads 方法。
無限制的平行處理原則
雖然平行處理原則很適合用於效能,但請小心使用無限制的平行處理原則,這表示執行緒或平行要求數目並未強加任何限制。 請務必將平行要求限制為上傳或下載資料、存取相同儲存體帳戶中的多個資料分割,或存取相同資料分割中的多個項目。 如果平行處理原則沒有限制,則您的應用程式可超出用戶端裝置的功能或儲存體帳戶的延展性目標,因而產生較長的延遲與節流作業。
用戶端程式庫和工具
為了達到最佳效能,請一律使用 Microsoft 所提供的最新用戶端程式庫和工具。 Azure 儲存體的用戶端程式庫適用於各種不同的語言。 Azure 儲存體也支援 PowerShell 和 Azure CLI。 Microsoft 主動開發以效能為考量的這些用戶端程式庫和工具,透過最新的服務版本將他們保持在最新的狀態,並確保這些工具會在內部處理許多已經實證的效能做法。
處理服務錯誤
當服務無法處理要求時,Azure 儲存體會傳回錯誤。 了解特定案例中 Azure 儲存體可能傳回的錯誤,對於效能最佳化很有幫助。
逾時和伺服器忙碌錯誤
如果您的應用程式很接近可擴縮性限制,Azure 儲存體可能進行節流處理。 在某些情況下,Azure 儲存體可能因某些暫時性狀況而無法處理要求。 在這兩種情況下,服務可能會傳回 503 (伺服器忙碌) 或500 (逾時) 錯誤。 如果服務正在重新平衡資料分割,以允許更高的輸送量,也可能會發生這些錯誤。 用戶端應用程式通常應該重試導致其中一個錯誤的作業。 不過,如果 Azure 儲存體因為您的應用程式超出延展性目標而進行節流,或即使服務因為一些其他原因而無法服務要求,則積極重試的結果可能會使問題雪上加霜。 建議使用指數輪詢重試原則,且用戶端程式庫會預設為此行為。 例如,您的應用程式可能會在 2 秒後進行重試、然後 4 秒、然後 10 秒、然後 30 秒,最後會完全放棄。 如此一來,您的應用程式會大幅降低其在服務上的負荷,而不會使導致節流的行為惡化。
因為連線能力錯誤不是節流的結果,且被認為是暫時性的,因此可以立即重試連線能力錯誤。
無法重試的錯誤
用戶端程式庫會在認知哪些錯誤可以重試和哪些錯誤不能重試的情況下,處理重試。 不過,如果您直接呼叫 Azure 儲存體 REST API,就會發生一些您不得重試的錯誤。 例如,400 (不正確的要求) 錯誤表示用戶端應用程式傳送了無法處理的要求,因為該要求不是預期的格式。 每次重新傳送此要求都會產生相同的回應,所以重試並沒有用。 如果您直接呼叫 Azure 儲存體 REST API,請注意可能發生的錯誤,以及是否應該重試。
如需 Azure 儲存體錯誤碼的詳細資訊,請參閱狀態和錯誤碼。
組態
本節將列出數個快速組態設定,可用來在資料表服務中大幅改善效能:
使用 JSON
自儲存體服務版本 2013-08-15 開始,資料表服務支援使用 JSON (而非以 XML 為基礎的 AtomPub 格式) 來轉換資料表資料。 使用 JSON 可降低約 75% 的裝載大小,並可大幅提高您的應用程式效能。
如需詳細資訊,請參閱 Microsoft Azure 資料表:JSON 簡介和表格服務作業的裝載格式。
停用 Nagle
在不同的 TCP/IP 網路中已廣泛採用 Nagle 的演算法,來作為提高網路效能的方法。 不過,它並非是所有情況下的最佳作法 (例如高互動式環境)。 Nagle 的演算法對於 Azure 資料表服務要求的效能有負面的影響,可以的話您應將它停用。
結構描述
如何呈現與查詢您的資料是影響資料表服務效能的單一最大因素。 雖然每個應用程式都有所不同,本節將概述與下列項目相關的部分一般已經實證做法:
- 資料表設計
- 有效率的查詢
- 有效率的資料更新
資料表和資料分割
資料表會被分為幾個資料分割。 儲存在資料分割中的每個實體會共用相同的資料分割索引鍵,並會有可在資料分割中識別該實體的唯一資料列索引鍵。 資料分割提供了優點,但也同時引進延展性限制。
- 優點:在包含高達 100 個不同儲存體作業 (總大小限制為 4 MB) 的單一、不可部分完成的批次交易中,您可以在相同資料分割中更新實體。 假設要擷取相同的實體數,則查詢單一資料分割中的資料會比查詢跨越資料分割的資料還要更有效率 (請繼續閱讀,以取得有關查詢資料表資料的進一步建議)。
- 延展性限制:因為資料分割支援不可部分完成的批次交易,因此無法針對儲存在單一資料分割中的實體存取進行負載平衡。 基於這個原因,個別資料表分割的延展性目標會低於資料表服務整體。
由於資料表和資料分割的這些特性,您應採用下列設計原則:
- 找出用戶端應用程式在相同資料分割中的相同工作邏輯單位中經常更新或查詢的資料。 例如,如果您的應用程式正在彙總寫入,或您正在執行不可部分完成的批次作業,請在相同的資料分割中尋找資料。 另外,在單一查詢中,查詢單一資料分割中的資料會比查詢跨資料分割中的資料還要有效率。
- 找出用戶端應用程式在不同資料分割中的相同工作邏輯單位 (也就是,單一查詢或批次更新) 中無法插入、更新或查詢的資料。 請記住,單一資料表中的資料分割索引鍵數目沒有限制,因此擁有數百萬個資料分割索引鍵也不是問題,而且還不會影響效能。 例如,如果您的應用程式是具備使用者登入的常用網站,則使用使用者識別碼作為資料分割索引鍵可能是個不錯的選擇。
常用資料分割
常用資料分割是指收到某個帳戶的不相稱百分比流量,但因為它是單一資料分割,所以無法進行負載平衡的資料分割。 一般來說,可以用下列兩種方法其中之一來建立常用資料分割:
只開頭附加和只結尾附加模式
所謂的「只開頭附加」模式,是指在此模式下,對所指定資料分割索引鍵的全部 (或幾乎全部) 流量會根據目前時間增加和減少。 例如,假設您的應用程式使用目前日期作為記錄資料的資料分割索引鍵。 此設計會造成所有插入都會跑到資料表的最後一個資料分割,而且系統無法適當地平衡負載。 如果進入該資料分割的流量超出資料分割層級的延展性目標,則會造成節流。 最好的方法是確保流量會傳送到多個資料分割,以在資料表中啟用負載平衡要求。
高流量資料
如果您的資料分割結構描述導致使用單一資料分割的資料比使用其他資料分割的資料高出甚多,當該資料分割達到單一資料分割的延展性目標時,您也可能會看到節流。 最好的方式是確定您的資料分割結構描述不會造成任何單一資料分割達到延展性目標。
查詢
本節說明可查詢資料表服務的實證做法。
查詢範圍
您可以透過數種方式來指定查詢的實體範圍。 下列清單說明查詢範圍的每個選項。
- 點查詢:點查詢會指定要擷取之實體的資料分割索引鍵與資料列索引鍵,藉此只取得一個實體。 這些查詢很有效率,您應盡可能地加以使用。
- 資料分割查詢:是指可擷取一組共用常見分割索引鍵之資料的查詢。 通常,除了分割索引鍵,查詢還會指定資料列索引鍵值的範圍或某些實體屬性值的範圍。 這些查詢會比點查詢沒有效率,應謹慎使用。
- 資料表查詢:是指可擷取一組不共用常見分割索引鍵之實體的查詢。 這些查詢非常沒有效率,您應盡量避免使用。
一般來說,請避免掃描 (大於單一實體的查詢),但如果您必須掃描,請嘗試組織您的資料,以方便掃描可以擷取所需資料,而無需掃描或傳回您不需要的大量實體數。
查詢密度
與為了尋找傳回的集合所掃描的實體數相較之下,查詢效率的另一個主要因素是傳回的實體數。 如果您的應用程式使用屬性值只有 1% 資料共用的篩選來執行資料表查詢,則查詢會為所傳回的每一個實體掃描 100 個實體。 先前討論的資料表延展性目標與掃描的實體數目相關,而與傳回的實體數目無關:低查詢密度會輕易導致資料表服務將您的應用程式節流,因為它必須掃描這麼多的實體,才可擷取您要尋找的實體。 如需有關如何避免節流的詳細資訊,請參閱反正規化一節。
限制傳回的資料量
當您知道查詢將傳回用戶端應用程式不需要的實體時,請考慮使用篩選來減少傳回集合的大小。 雖然未傳回至用戶端的實體仍然算在延展性目標內,但您的應用程式效能將會因為網路裝載大小減輕以及用戶端應用程式必須處理的實體數減少而提升。 請記住,延展性目標會與掃描的實體數有關,因此即使只傳回幾個實體,篩選出許多實體的查詢仍然可能會導致節流。 如需讓查詢有效率的詳細資訊,請參閱查詢密度一節。
如果您的用戶端應用程式只需要資料表中一組有限的實體屬性,則您可以使用投射來限制傳回資料集的大小。 與篩選一樣,投影有助於降低網路負荷及用戶端處理。
反正規化
與使用關聯式資料庫不同的是,為求有效率地查詢資料表資料的已經實證做法會導致將您的資料反正規化。 也就是說,為了尋找用戶端所需的資料,在多個實體 (每個您可能用來尋找資料的索引鍵一個實體) 中複製相同的資料,可將查詢所必須掃描的實體數降到最低。 例如,在電子商務網站中,您可能想要根據客戶識別碼 (向我提供此客戶的訂單) 和根據日期 (向我提供某個日期的訂單) 來尋找一筆訂單。 在資料表儲存體中,最好是儲存實體 (或它的參照) 兩次 – 一次包括資料表名稱、PK 和 RK,可根據客戶識別碼加快尋找的速度,一次可根據日期加快尋找的速度。
插入、更新和刪除
本節說明可修改儲存在資料表服務中實體的實證做法。
批次處理
在 Azure 儲存體中,批次交易稱為實體群組交易。 實體群組交易內的所有作業都必須在單一資料表的單一資料分割上。 可能的話,請使用實體群組交易分批執行插入、更新及刪除。 使用實體群組交易可減少從用戶端應用程式到伺服器的反覆存取次數、減少計費交易數目 (為方便計費,實體群組交易會被視為單一交易,並可包含最多 100 個儲存體作業),以及啟用不可部分完成的更新 (實體群組交易內的所有作業都成功或全都失敗)。 具有高延遲的環境 (例如行動裝置) 將會因為使用實體群組交易而獲得極大的好處。
Upsert
您應盡可能地使用資料表 Upsert 作業。 Upsert 有兩種類型,這兩種類型都會比傳統的 Insert 和 Update 作業還要有效率:
- InsertOrMerge:當您想要上傳實體屬性的子集,但不確定此實體是否已存在時,請使用此作業。 如果實體已存在,則此呼叫會更新包含在 Upsert 作業的屬性,並將所有現有的屬性保留不變,如果實體不存在,則此呼叫會插入新的實體。 這類似於在查詢中使用投射,在此情況下,您只需要上傳正在變更的屬性。
- InsertOrReplace:當您想要上傳全新的實體,但不確定此實體是否已存在時,請使用此作業。 當您知道此全新上傳的實體完全正確時,請使用此作業,因為它會完全覆寫舊的實體。 例如,您想要更新儲存使用者目前位置的實體,而不管應用程式先前是否已儲存使用者的位置資料;新的位置實體已完成,而且您無需任何先前實體的任何資訊。
在單一實體中儲存資料序列
有時候,應用程式會儲存它經常需要一次擷取的一系列資料:例如,應用程式可能會隨著時間追蹤 CPU 使用量,以便繪製資料在過去 24 小時的機動圖表。 一個方法是每個小時有一個資料表實體,且每個實體會代表特定的小時數,並儲存該小時的 CPU 使用量。 若要繪製此資料,應用程式必須擷取保存過去 24 小時資料的實體。
或者,您的應用程式可以儲存每小時 CPU 使用量做為單一實體的個別屬性:若要每小時更新,您的應用程式可以使用單一 InsertOrMerge Upsert 呼叫來更新最近一小時的值。 若要繪製此資料,應用程式只需擷取單一實體 (而不是 24 個實體),提供很有效率的查詢。 如需查詢效率的詳細資訊,請參閱查詢範圍一節。
在 Blob 中儲存結構化資料
如果您要執行批次插入,然後一起擷取實體範圍,請考慮使用 Blob,而不是資料表。 記錄檔是一個好例子。 您可以批次處理數分鐘的記錄並加以插入,然後一次擷取數分鐘的記錄。 在此情況下,您使用 Blob (而不是資料表) 的效能比較好,因為您可大幅降低寫入或讀取的物件數,也可能降低需要提出的要求數。