花園里充滿了美麗的事物,但也有很多可能出錯的地方

錯誤1:查詢不變的信息

當(dāng)我構(gòu)建 Avalara AvaTax REST API 時,我必須允許用戶發(fā)送地址。由于他們的數(shù)據(jù)很亂,有時他們會發(fā)送 ISO 國家代碼,或者可能是國家名稱,也可能是別名。我可以處理這個問題,因為 GitHub 上有很多具有寬松許可證的 國家數(shù)據(jù)源 ,但最終我選擇付費(fèi)購買 官方 ISO 3166 國家代碼列表

下一步是讓我的 API 服務(wù)器在啟動時加載這些數(shù)據(jù)。代碼不必太復(fù)雜——這里有一些類似 C# 的偽代碼,大致顯示了如何使其工作:

private  static Task<List<Country>>? _cachedQuery = null ; 

private Task<List<Country>> GetCachedCountries()
{
// 將承諾保存到靜態(tài)變量
if (_cache == null ) {
_cache = Database.Countries.ToListAsync();
}

// 所有調(diào)用者加入同一個承諾
return _cache;
}

為什么要這樣做?幸運(yùn)的是,新國家/地區(qū)并不經(jīng)常創(chuàng)建。如果國家/地區(qū)列表要更改,我們會在每月的應(yīng)用程序部署期間發(fā)送 SQL 腳本來添加新記錄。

我的 C# API 服務(wù)器不是查詢數(shù)據(jù)庫中的表,而是將這些數(shù)據(jù)保存在單例中。它會在輸入或輸出時查找正確的名稱。數(shù)據(jù)只占用幾千字節(jié),為了方便起見,我有多個散列的不區(qū)分大小寫的字典。

您可能有幾十個這樣的靜態(tài)數(shù)據(jù)集。查找數(shù)據(jù)集、原因代碼、配置標(biāo)志 — 將它們存儲在靜態(tài)單例中!如果您忘記了,您可能會發(fā)現(xiàn)您的系統(tǒng)每秒對永遠(yuǎn)不會改變的數(shù)據(jù)進(jìn)行數(shù)千次不必要的查詢。

錯誤2:過度使用數(shù)據(jù)庫的狀態(tài)頁面檢查

您的 API 服務(wù)器需要一個健康檢查系統(tǒng)。它可以是一個頁面或一個 API,但它應(yīng)該執(zhí)行一系列基本功能測試,以確保機(jī)器能夠正常工作。典型的測試包括:

這些類型的狀態(tài)檢查對于啟動作為自動擴(kuò)展組一部分的服務(wù)器或使用容器化啟動模板是必不可少的。在部署服務(wù)器之前,徹底測試所有內(nèi)容非常重要——啟動缺少數(shù)據(jù)庫連接字符串的機(jī)器會很糟糕。

這些狀態(tài)檢查的一個副作用是,它們通常也用于監(jiān)控部署后的服務(wù)器整體健康狀況。一些云服務(wù)會每分鐘多次調(diào)用此狀態(tài)頁面,如果服務(wù)器無法響應(yīng),則會從負(fù)載平衡器中移除該服務(wù)器。如果您的狀態(tài)頁面在此測試中執(zhí)行查詢,這可能會迅速消耗您的數(shù)據(jù)庫。

可以想象,在啟動時測試數(shù)據(jù)庫連接至關(guān)重要。但是,一旦服務(wù)器成功部署,有效的數(shù)據(jù)庫連接以后突然變?yōu)闊o效的可能性就很小。我發(fā)現(xiàn)最好將成功的結(jié)果緩存一小段時間,比如 30 秒。這意味著我的健康檢查仍然可以將有問題的服務(wù)器排除在輪換之外,但不會使數(shù)據(jù)庫過載:

public  static DateTime LastCheckTime = DateTime.MinValue; 
public const int SECONDS_FOR_RETEST = 30 ;

public static bool Status ()
{
var now = DateTime.UtcNow;
var timeSinceLastCheck = now - LastCheckTime;
if (timeSinceLastCheck.TotalSeconds > SECONDS_FOR_RETEST) {
...在這里做一些數(shù)據(jù)庫健康檢查 ...
LastCheckTime = now;
}
return true ;
}

錯誤3:使用過多查詢進(jìn)行 API 驗證

大多數(shù)重度 API 用戶會迅速發(fā)出大量請求。對于每個請求,服務(wù)器需要檢查用戶是否經(jīng)過身份驗證,以及他們是否有權(quán)執(zhí)行他們請求的工作。許多這些檢查都需要從數(shù)據(jù)庫中提取數(shù)據(jù):

對每個請求都這樣做似乎很自然,但這些信息可能會浪費(fèi)大量時間。幸運(yùn)的是,有一種 方法可以解決緩慢的身份驗證數(shù)據(jù)庫查詢問題 :如果調(diào)用者發(fā)出請求,您可以在短時間內(nèi)緩存他們的憑據(jù)。

緩存授權(quán)可能看起來很可怕,因為更改不是即時的,但在實踐中,“即時”很難定義。如果在撤銷訪問權(quán)限之前 API 調(diào)用正在進(jìn)行中,則用戶可能會或可能不會根據(jù)隨機(jī)運(yùn)氣發(fā)出請求 — 無論 API 調(diào)用是否在撤銷之前到達(dá)。

如果我們更新文檔,說“更改用戶權(quán)限后,請等待 5 分鐘,所有服務(wù)器才會更新新權(quán)限”——那么您就可以規(guī)劃性能了!這里的技巧是對 API 調(diào)用的承載令牌及其 IP 地址進(jìn)行哈希處理,然后在緩存中查找所有身份驗證和授權(quán)數(shù)據(jù):

錯誤4:循環(huán)查詢的對象關(guān)系映射器

Entity Framework 等現(xiàn)代技術(shù)使得訪問數(shù)據(jù)庫變得極其簡單。事實上,這非常容易,以至于我們經(jīng)常可以編寫一個方法來執(zhí)行數(shù)據(jù)庫調(diào)用 — 然后發(fā)現(xiàn)人們在使用這個方法時并沒有意識到它接觸了數(shù)據(jù)庫。

一個簡單的例子可能是這樣的:
公共 異步任務(wù) < int > GetNumberOfUsers ( int id ) {
var count = 0 ;
var items = await _database.GetRecords(id);
foreach ( var item in records) {
count += CountUsersPerItem(item);
}
返回count;
}

這段代碼可能看起來微不足道,但如果該方法 CountUsersPerItem 聯(lián)系數(shù)據(jù)庫,可能是為了獲取一個標(biāo)志或查詢一個子表,您可能會發(fā)現(xiàn)看似一個查詢變成了數(shù)百或數(shù)千個查詢。

更糟糕的是,此功能的性能在開發(fā)人員的桌面上可能看起來不錯,但當(dāng)現(xiàn)實世界的客戶面臨同樣的情況時,可能會突然下降。

我發(fā)現(xiàn)了一些有助于追蹤此問題的技巧:

錯誤5:因為速度快而忽略查詢

這個問題非常隱蔽。現(xiàn)代數(shù)據(jù)庫技術(shù)非常強(qiáng)大,簡單的數(shù)據(jù)庫查詢通常可以與查詢 REDIS 一樣快甚至更快。在本地工作的開發(fā)人員通常會看到非常好的性能,因為他們的應(yīng)用程序和數(shù)據(jù)庫服務(wù)器之間沒有延遲,兩者都在筆記本電腦上的容器中運(yùn)行。

即使您的 SQL Server 或 Postgres 實例可以在一毫秒內(nèi)做出響應(yīng),這些毫秒也會累積起來。如果您的 API 請求發(fā)出十個一毫秒的查詢,則可能會使您的 API 延遲增加十毫秒 – 當(dāng)平均預(yù)期時間少于一百毫秒時,這是一個不可忽略的量。

這里的關(guān)鍵經(jīng)驗是,在應(yīng)用程序接口設(shè)計中,每個數(shù)據(jù)庫查詢都很重要。關(guān)注它們,你的應(yīng)用程序接口就會變得快速而實用。

原文鏈接:https://medium.com/tedspence-com/five-common-database-performance-mistakes-in-api-development-06d99c001bb2

上一篇:

Python調(diào)用IP地址API查詢國家信息

下一篇:

在 Spring Boot 中構(gòu)建 API 響應(yīng)的最佳方法
#你可能也喜歡這些API文章!

我們有何不同?

API服務(wù)商零注冊

多API并行試用

數(shù)據(jù)驅(qū)動選型,提升決策效率

查看全部API→
??

熱門場景實測,選對API

#AI文本生成大模型API

對比大模型API的內(nèi)容創(chuàng)意新穎性、情感共鳴力、商業(yè)轉(zhuǎn)化潛力

25個渠道
一鍵對比試用API 限時免費(fèi)

#AI深度推理大模型API

對比大模型API的邏輯推理準(zhǔn)確性、分析深度、可視化建議合理性

10個渠道
一鍵對比試用API 限時免費(fèi)