掃二維碼與項(xiàng)目經(jīng)理溝通
我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運(yùn)營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
有2種常見的多維度查詢場景,分別是:

普通的數(shù)據(jù)庫查詢,很難實(shí)現(xiàn)上述需求場景,更不用提模糊查詢、全文檢索了。
下面結(jié)合樓主的經(jīng)驗(yàn)和知識,介紹初級方案、進(jìn)階方案(上ElasticSearch),大部分情況下推薦使用ElasticSearch來實(shí)現(xiàn)多維度查詢,趕時間的讀者可以直接跳到“進(jìn)階方案:將ElasticSearch添加到現(xiàn)有系統(tǒng)中”。
這個是為了實(shí)現(xiàn)帶多個篩選條件的列表查詢的。
于是就出現(xiàn)了經(jīng)典的一幕:產(chǎn)品提需求說要支持某個新字段的篩選查詢,開發(fā)反饋說做不了、或者成本很高,于是不了了之 :)
更加優(yōu)雅的方式,是異構(gòu)出多份數(shù)據(jù)。
例如,C端按用戶維度查詢,B端按店鋪維度查詢,如果還有供應(yīng)商,按供應(yīng)商維度查詢。一個數(shù)據(jù)庫只能按一種維度來分庫。
優(yōu)點(diǎn)是:非常簡單。
通過Canal同步數(shù)據(jù),異構(gòu)出多個維度的數(shù)據(jù)源。詳見之前寫的這篇文章: 架構(gòu)師必備:巧用Canal實(shí)現(xiàn)異步、解耦的架構(gòu)
優(yōu)點(diǎn)是:更加優(yōu)雅,無需改動程序主流程。
現(xiàn)有系統(tǒng)一般都會用到MySQL數(shù)據(jù)庫,需要引入ES,為系統(tǒng)增強(qiáng)多維度查詢的功能。
MySQL繼續(xù)承擔(dān)業(yè)務(wù)的實(shí)時讀寫請求、事務(wù)操作,ES承擔(dān)近實(shí)時的多維度查詢請求,ES可支撐十萬級別qps(取決于節(jié)點(diǎn)數(shù)、分片數(shù)、副本數(shù))。
需要注意的是:同步數(shù)據(jù)至ES是秒級延遲(主要耗費(fèi)在索引refresh),而查詢已進(jìn)入索引的文檔,是在數(shù)毫秒到數(shù)百毫秒級別。
需要同步機(jī)制,來把MySQL中的數(shù)據(jù)導(dǎo)入到ES中,主要流程如下:
代碼示例如下(含詳細(xì)注釋):
public class EsClientDemo {
// demo演示:創(chuàng)建client,然后搜索
public void createClientAndSearch() throws Exception {
// 創(chuàng)建底層的low level rest client,連接ES節(jié)點(diǎn)的9200端口
RestClient restClient = RestClient.builder(
new HttpHost("localhost", 9200)).build();
// 創(chuàng)建transport類,傳入底層的low level rest client,和json解析器
ElasticsearchTransport transport = new RestClientTransport(
restClient, new JacksonJsonpMapper());
// 創(chuàng)建核心client類,后續(xù)操作都圍繞此對象
ElasticsearchClient esClient = new ElasticsearchClient(transport);
// 多條件搜索
// fluent API風(fēng)格,并且使用lambda函數(shù)提高代碼可讀性,可以看出Java api client的語法,同http json請求體非常相似
String searchText = "bike";
String brand = "brandNew";
double maxPrice = 1000;
// 根據(jù)商品名稱,做match全文檢索查詢
Query byName = MatchQuery.of(m -> m
.field("name")
.query(searchText)
)._toQuery();
// 根據(jù)品牌,做term精確查詢
Query byBrand = new Query.Builder()
.term(t -> t
.field("brand")
.value(v -> v.stringValue(brand))
).build();
// 根據(jù)價(jià)格,做range范圍查詢
Query byMaxPrice = RangeQuery.of(r -> r
.field("price")
.lte(JsonData.of(maxPrice))
)._toQuery();
// 調(diào)用核心client,做查詢
SearchResponse response = esClient.search(s -> s
.index("products") // 指定ES索引
.query(q -> q // 指定查詢DSL
.bool(b -> b // 多條件must組合,必須同時滿足
.must(byName)
.must(byBrand)
.must(byMaxPrice)
)
),
Product.class
);
// 遍歷命中結(jié)果
List> hits = response.hits().hits();
for (Hit hit: hits) {
Product product = hit.source(); // 通過source獲取結(jié)果
logger.info("Found product " + product.getName() + ", score " + hit.score());
}
}
}
可參閱: ??https://www.elastic.co/guide/en/elasticsearch/client/index.html??
因?yàn)榧扔蠱ySQL,又有ES,所以有2種異構(gòu)的數(shù)據(jù)模型。需要在代碼中定義2種數(shù)據(jù)模型,并且實(shí)現(xiàn)類型互相轉(zhuǎn)換的工具類。
ES之所以比MySQL,能勝任多維度查詢、全文檢索,是因?yàn)榈讓訑?shù)據(jù)結(jié)構(gòu)不同:
另外簡要回顧一下ES的架構(gòu)要點(diǎn):
前面提到ES超高并發(fā)下存在瓶頸,極端情況下可能遇到OOM,因此超高并發(fā)下需要C++實(shí)現(xiàn)的專用搜索引擎
例如:

我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運(yùn)營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流