一次對 API 響應時間的優化探索。
在這普通的一天,我們用著普通的 API,突然發現響應速度過慢的警報意外亮起。
結果顯示,我們的 API 需要約 70 秒的時間才能對常規流量下的客戶端做出響應。開什么玩笑……
從問題入手先向大家匯報一下我們的這個慢速 API 是做什么,又是怎么做的。
在這款應用程序中,我們把書籍及其作者的目錄存儲在 MySQL 數據庫中。其中共包含約 6800 萬本書,每本書對應一家出版社。
下面來看書籍和作者的表結構。
CREATE TABLE `book` (
`id` int NOT NULL AUTO_INCREMENT,
`book_uuid_bin` binary(16) NOT NULL,
`publishing_house_uuid_bin` binary(16) NOT NULL,
`display_name` varchar(750) NOT NULL,
`normalized_name` varchar(750) NOT NULL,
`description` varchar(1000) DEFAULT NULL,
`level` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `unique_book_uuid_bin` (`book_uuid_bin`),
KEY `book_description_idx` (`description`(768)),
KEY `book_display_name_idx` (`display_name`),
KEY `book_normalized_name_idx` (`normalized_name`),
KEY `publishing_house_uuid_bin_idx` (`publishing_house_uuid_bin`),
KEY `book_uuid_bin_idx` (`book_uuid_bin`)
)
CREATE TABLE `publishing_house` (
`id` int NOT NULL AUTO_INCREMENT,
`publishing_house_uuid_bin` binary(16) DEFAULT NULL,
`display_name` varchar(750) NOT NULL,
`normalized_name` varchar(750) NOT NULL,
`alias_uuid_bin` binary(16) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `unique_publishing_house_uuid_bin` (`author_uuid_bin`),
KEY `publishing_house_normalized_name_idx` (`normalized_name`),
KEY `publishing_house_display_name_idx` (`display_name`)
)
再說回 API,其用例是在 UI 上提供自動補全功能,方便用戶更好地查找特定出版社出版的書籍,同時保證用戶的查詢字符串同書籍名稱或描述的前綴相匹配。
API 中使用的 MySQL 查詢如下所示:
select book_uuid_bin,
display_name,
normalized_name,
description,
author_uuid_bin
from book
where
((lower(display_name) like lower("%Software E%") or lower(description) like lower("%Software E%")) and publishing_house_uuid_bin = UUID_TO_BIN("d2230981-e570-5ba4-9a3a-16028c51d54f"))order by display_name asc limit 100;
即使查詢在單表上就能完成,不需要連接作者表,這條 SQL 查詢也需要 7 秒左右才能執行完成。
我們在 where 子句所使用的列上建立了索引。但這一實現還是存在問題,包括:
1、display_name 和 description 等列屬于 VARCHAR 類型。
2、會在 VARCHAR 類型列上使用帶有 OR 子句的 LIKE 運算符。
3、會使用 ORDER BY。
4、 WHERE 子句中使用的所有列,都缺少復合索引。
5、表中共包含 5800 萬條記錄。
我們曾嘗試在查詢中使用的各列上創建一個復合索引,但最終發現無濟于事。因為對于 RDBMS 數據庫內的大表來說,在 VARCHAR 列上搜索文本的效率就不可能太高。
我們知道 Elasticsearch 提供全文本搜索功能,所以想在自己的用例中試試看。我們一直在用 AWS 的云服務,因此選擇了相應的 AWS OpenSearch 服務。
Amazon OpenSearch 托管服務能幫助用戶輕松在 AWS 云中部署、操作和擴展 OpenSearch 集群。Amazon OpenSearch 是 Amazon Elasticsearch 的繼任方案。
開始行動我們通過腳本將表數據從 MySQL 加載到了 AWS OpenSearch 集群當中。整個數據遷移過程大概用了幾個小時。
我們為索引保留了 5 個分片和 1 個副本因子。
我們還為用例編寫了一條等效的 OpenSearch 查詢,具體如下所示:
API — POST /books-catalog/_search
{
"query": {
"bool": {
"must": [
{
"match_phrase": {
"publisherUuid": "1f754fc0-610c-5b29-b22b-fa8140afb7be"
}
},
{
"bool": {
"should": [
{
"match_phrase": {
"displayName": "Software E"
}
},
{
"match_phrase": {
"description": "Software E"
}
}
]
}
}
]
}
},
"size": 100,
"sort": [
{
"displayName.keyword": {
"unmapped_type": "keyword",
"order": "asc"
}
}
]
}
結果我們的 API 響應速度直接縮短至 70 毫秒以內。
API 響應速度提高了 1000 倍!
關于 OpenSearch 全文搜索的一些細節 :
在 ElasticSearch 中對文檔進行索引(創建)時,AWS OpenSearch 會對字符串類型的字段使用文本分析器。
文本分析器會將字符串字段拆分為多個 token,為各 token 構建內部索引,然后根據查詢中提供的 token 進行匹配。
權衡取舍為了避免重寫整個服務,同時盡快在 MySQL 切換至 AWS OpenSearch 后恢復正常生產,我們決定只在這個特定用例中使用 OpenSearch。
而且速度提升 1000 倍的代價,就是多了一套需要在 OpenSearch 當中維護的數據副本。但由于我們的數據大多是靜態的,持續更新量非常有限,所以維護強度和成本都很低。
可以看到,選擇正確的數據庫引擎往往會給業務用例帶來翻天覆地的提升。
希望我們的經歷能給大家帶來一點啟發,祝編程愉快!
-
API
+關注
關注
2文章
1530瀏覽量
62761 -
MySQL
+關注
關注
1文章
836瀏覽量
26934 -
響應時間
+關注
關注
0文章
11瀏覽量
6963
原文標題:將 API 從 MySQL 遷移到 AWS OpenSearch 后,我們將響應時間提高了 1000 倍
文章出處:【微信號:AI前線,微信公眾號:AI前線】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
產品響應時間
請問AD9144輸出響應時間?
SAR ADC響應時間實現迅速響應、快速控制的方法
什么是液晶電視的響應時間
光敏電阻響應時間研究

SAR ADC 響應時間:迅速響應、快速控制

什么是單片機的中斷響應時間

評論