第十二章 Net 5.0 快速開發框架 YC.Boilerplate –千萬級數據處理解決方案
- 2021 年 10 月 10 日
- 筆記
- .NET, asp.net,C#, YC.Boilerplate, 分佈式
在線文檔://doc.yc-l.com/#/README
在線演示地址://yc.yc-l.com/#/login
源碼github://github.com/linbin524/yc.boilerplate
源碼gitee://gitee.com/linxuanming/yc.boilerplate
視頻教程:
元磁之力框架開源初心和框架設計介紹(上): //www.bilibili.com/video/BV1VM4y1G7hC/
元磁之力框架開源初心和框架設計介紹(下): //www.bilibili.com/video/BV15h411s7w6/
元磁之力框架數據庫表和代碼生成使用教程實戰: //www.bilibili.com/video/BV1oM4y137D5/
QQ群:1060819005
後續:關於框架demo和細節技巧,會在QQ群中發佈,就不撰文說明。
大數據套件 ElasticSearch
簡介
為了提升YC.Boierlate
在大數據量的處理能力,引入ES組件,封裝對應的模塊、實現租戶拆分、倉儲、集群、大數據上億級別以上數據的檢索、統計、分析,並提供千萬級別分詞搜索等演示示例。
ES基礎介紹
Elasticsearch 是一個分佈式、RESTful 風格的搜索和數據分析引擎,是PB級別大數據解決方案組件之一。
Elasticsearch是基於Lucense的搜索服務器,,基於RESTful web接口。Elasticsearch是Java語言開發的,並作為Apache許可條款下的開放源碼發佈,是一種流行的企業級搜索引擎。Elasticsearch用於雲計算中,能夠達到實時搜索,穩定,可靠,快速,安裝使用方便。
ES解決什麼問題
對海量數據進行近實時的處理
ES自動可以將海量數據分散到多台服務器上去存儲和檢索,通過內置搜索引擎、分詞、實現
千萬級別數據秒級查詢、統計、分析等,相對傳統關係型數據庫的模糊查詢在速度有着質的飛躍。
ES 適用場景
-
維基百科,類似百度百科,牙膏,牙膏的維基百科,全文檢索,高亮,搜索推薦
-
The Guardian(國外新聞網站),類似搜狐新聞,用戶行為日誌(點擊,瀏覽,收藏,評論)+社交網絡數據(對某某新聞的相關看法),數據分析,給到每篇新聞文章的作者,讓他知道他的文章的公眾反饋(好,壞,熱門,垃圾,鄙視,崇拜)
-
Stack Overflow(國外的程序異常討論論壇),IT問題,程序的報錯,提交上去,有人會跟你討論和回答,全文檢索,搜索相關問題和答案,程序報錯了,就會將報錯信息粘貼到裏面去,搜索有沒有對應的答案
-
GitHub(開源代碼管理),搜索上千億行代碼
-
電商網站,檢索商品
-
日誌數據分析,logstash採集日誌,ES進行複雜的數據分析(ELK技術,elasticsearch+logstash+kibana)
-
商品價格監控網站,用戶設定某商品的價格閾值,當低於該閾值的時候,發送通知消息給用戶,比如說訂閱牙膏的監控,如果高露潔牙膏的家庭套裝低於50塊錢,就通知我,我就去買
-
BI系統,商業智能,Business Intelligence。比如說有個大型商場集團,BI,分析一下某某區域最近3年的用戶消費金額的趨勢以及用戶群體的組成構成,產出相關的數張報表,**區,最近3年,每年消費金額呈現100%的增長,而且用戶群體85%是高級白領,開一個新商場。ES執行數據分析和挖掘,Kibana進行數據可視化國內
-
國內:站內搜索(電商,招聘,門戶,等等),IT系統搜索(OA,CRM,ERP,等等),數據分析(ES熱門的一個使用場景)
ES 常用組合
-
ELK :Elasticsearch是與名為Logstash的數據收集和日誌解析引擎以及名為Kibana的分析和可視化平台一起開發的。這三個產品被設計成一個集成解決方案。
Elasticsearch可以用於搜索各種文檔。它提供可擴展的搜索,具有接近實時的搜索,並支持多租戶。Elasticsearch是分佈式的,這意味着索引可以被分成分片,每個分片可以有0個或多個副本。每個節點託管一個或多個分片,並充當協調器將操作委託給正確的分片。再平衡和路由是自動完成的。相關數據通常存儲在同一個索引中,該索引由一個或多個主分片和零個或多個複製分片組成。一旦創建了索引,就不能更改主分片的數量。 -
阿里巴巴開發的canal:基於Mysql的binlog日誌訂閱:binlog日誌是Mysql用來記錄數據實時的變化。這裡主要的是binlog同步組件,目前實現的有國內的。
github地址://github.com/alibaba/canal -
go-mysql-elasticsearch:go-mysql-elasticsearch是一款使用go語言開發的同步數據到ES的工具。 go-mysql-elasticsearch也是基於Mysql的binlog訂閱,也可以使用使用mysqldump的方式。目前還不支持ES6.x及以上的版本,也不支持mysql8.x版本,同時該項目目前還不夠穩定,也在開發中。
項目github地址://github.com/siddontang/go-mysql-elasticsearch
ES 和常規關係型數據庫差異
ES中有幾個基本概念:索引(index)、類型(type)、文檔(document)、映射(mapping)等。我們將這幾個概念與傳統的關係型數據庫中的庫、表、行、列等概念進行對比,如下表:
常規問題
-
內存:es 的默認配置在常規服務器上大部分都有內存使用率的問題,需要根據實際情況合理調優。
-
版本:ES 每個版本配套組件有極強耦合,無法做到各個版本兼容,所以jdk、以及其他組件需要指定適配。
-
分詞:es除了內置standard分詞,還可以其他分詞組件,對中文支持比較好的有:es-ik。
-
分片(shard): 因為 ES 是個分佈式的搜索引擎, 所以索引通常都會分解成不同部分, 而這些分佈在不同節點的數據就是分片. ES自動管理和組織分片, 並在必要的時候對分片數據進行再平衡分配, 所以用戶基本上不用擔心分片的處理細節.
-
副本(replica): ES 默認為一個索引創建 5 個主分片, 並分別為其創建一個副本分片. 也就是說每個索引都由 5 個主分片成本, 而每個主分片都相應的有一個 copy。對於分佈式搜索引擎來說, 分片及副本的分配將是高可用及快速搜索響應的設計核心.主分片與副本都能處理查詢請求,它們的唯一區別在於只有主分片才能處理索引請求.副本對搜索性能非常重要,同時用戶也可在任何時候添加或刪除副本。額外的副本能給帶來更大的容量, 更高的呑吐能力及更強的故障恢復能力。
-
深度查詢:在Elasticsearch中如果需要做分頁查詢,我們通常使用form和size實現。form指定從有序哪一行開始,size表示從當前開始讀取多少行。但是我們發現查詢結果最大只能到10000,這是因為Elasticsearch中的size的默認值在index.max_result_window 中設置,並且默認值就是10000,如果需要擴展,可以通過如下操作【擴大查詢最大值】其中1000000是標識擴大為10萬:
put /tenant_1_books/_settings
{
"index.max_result_window" :"1000000"
}
還可以採用searchAfer、scroll等方案。
YC.ElasticSearch 模塊實戰
集群部署
在本地或者服務器上搭建3個es節點,形成集群,針對elasticsearch.yml
進行節點配置,最後啟動服務,並安裝對應的kibana組件【可視化】。
配置
在項目的YC.ServiceWebApi 中的配置文件 DefaultConfig.json,做如下配置,其中node是對應的es節點。
"ElasticSearchSetting": {
///elasticSearch節點集群
"Nodes": [
{ "node": "//127.0.0.1:9200" },
{ "node": "//127.0.0.1:9201" },
{ "node": "//127.0.0.1:9202" }
]
}
在項目的YC.ServiceWebApi 找到 ElasticSearchAutofacModule.cs
該文件是相關的IOC 注入配置,在Startup.cs
中進行如下注入操作:
// elasticSearch 注入
builder.RegisterModule(new ElasticSearchAutofacModule());
ES 模塊調用
在示例演示 BookAppService
中可以直接使用對應的注入調用es組件。
private IElasticSearchRepository<Book> _elasticSearchRepository;
public BookAppService(
IHttpContextAccessor httpContextAccessor, ICacheManager cacheManager, IMapper mapper, IElasticSearchRepository<Book> elasticSearchRepository) : base(httpContextAccessor, cacheManager)
{
_cacheManager = cacheManager;
_mapper = mapper;
_elasticSearchRepository = elasticSearchRepository;
}
/// <summary>
/// 查查默認1頁10條
/// </summary>
/// <returns>返回數據集合</returns>
public async Task<ApiResult<List<BookAddOrEditDto>>> GetAllAsync()
{
var res = new ApiResult<List<BookAddOrEditDto>>();
var data = await _elasticSearchRepository.GetAllAsync();
var entityDtoList = _mapper.Map<List<BookAddOrEditDto>>(data);
return res.Ok(entityDtoList);
}
YC.ElasticSearch 模塊介紹
模塊包含有請求上下文、以及默認倉儲,其中倉儲封裝了常規crud、聚合查詢、searchAfter查詢等常規操作異步方法,並在倉儲上提供一個公開請求上下文對象,用於自定義化es操作,模塊配套對應的單元測試,提供基礎調用示例。
es 其他使用介紹
- 分詞
使用 kibana 操作,對指定的Index進行分詞
//創建表 對應的分片,需要表還沒創建時候設置
put /tenant_1_books_0
{
"settings":{
"number_of_shards":2
}
}
- 數據結構修改和遷移
//創建新的索引數據庫,並指定字段映射類型,tenant_1_books_0 中的bookName 類型改為keyword
PUT tenant_1_books_0
{
"mappings": {
"properties": {
"bookName":{
"type":"keyword"
}
}
}
}
//遷移數據,將tenant_1_books 數據遷移到tenant_1_books_1
POST _reindex
{
"source": {
"index": "tenant_1_books"
},
"dest": {
"index": "tenant_1_books_1"
}
}
- 分頁查詢
//查看tenant_1_books所有數據【默認會分頁】
GET tenant_1_books/_search
{
"track_total_hits": true,
"query": {
"match_all": {}
}
}
//深度分頁方案1 擴大分頁限制
//允許深度分頁,限制在10w
put /tenant_1_books/_settings
{
"index.max_result_window" :"1000000"
}
//查詢數據 分頁,track_total_hits=真實的總數
GET tenant_1_books/_search
{
"track_total_hits": true,
"from" : 99000, "size" : 100,
"query": {
"match_all": {}
}
}
結果如下:
- 深度查詢 searchAfer
配套的net單元測試代碼如下:
/// <summary>
/// 深度分頁查詢 searchAfter
/// </summary>
/// <returns></returns>
[Fact]
public async Task GetPageByQuerySearchAfterTest()
{
int size = 100;
// "bookName" : {
//"type" : "keyword"
//},
//1、BookName 修改為keyword 所有必須完整匹配,不分詞
Func<QueryContainerDescriptor<Book>, QueryContainer> query1 = q => q.Term(t => t.BookName, "吞噬星空");
Func<QueryContainerDescriptor<Book>, QueryContainer> query2 = q => q.Match(mq =>
mq.Field(f => f.BookName).Query("哈利波特").Operator(Operator.And)
);//由於類型為 keyword,所以Match 查找不出來,只能使用Term 精確查詢
//2.全字匹配+ 分詞查詢
Func<QueryContainerDescriptor<Book>, QueryContainer> query3 = q => q
.Term(t => t.BookName, "吞噬星空")
|| q.Match(mq =>
mq.Field(f => f.BookContent).Query("哈利波特").Operator(Operator.And)
);
//排序,按照時間升序,再按照
Func<SortDescriptor<Book>, IPromise<IList<ISort>>> sort = s => s.Ascending(a => a.CreateDate).Descending(d=>d.Price);
var result1=await _elasticSearchRepository.GetPageByQuerySearchAfterAsync(query3, sort, 100, null);
//使用上一次查詢得到SearchAfter 作為下一次查詢的游標
var result2 = await _elasticSearchRepository.GetPageByQuerySearchAfterAsync(query3, sort, 100, result1.SearchAfter);
Assert.NotNull(result2.List);
}
- scroll 查詢
//深度分頁方案3 scroll,者每次查詢大量的文檔,但是對實時性要求並不高,
//後面的每次滾屏(或者叫翻頁)都是基於這個快照的結果,也就是即使有新的數據進來也不會別查詢到。
//1. 查詢
POST tenant_1_books/_search?scroll=1m
{
"size": 1000,
"query": {
"match_all" : {
}
}
}
//2. 上一次查詢所得到結果,作為游標
POST _search/scroll
{
"scroll" : "1m",
"scroll_id" : "FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ5QW5kRmV0Y2gBFmJpVWRZVWQ3UkItejk2UUx5bC15bFEAAAAAAAMv2xZyZURRWkowelF6S0NnRjMzWjhfQTh3"
}
- 聚合查詢
//聚合獲取該字段的所有統計
get /tenant_1_books/_search
{
"aggs":{
"extended_stats_price":{"extended_stats":{"field":"price"}}
}
}
//聚合 總和統計
get /tenant_1_books/_search
{
"aggs":{
"total_price":{"sum":{"field":"price"}}
}
}
YC.ElasticSearch 大數據檢索示例
在//yc.yc-l.com/
演示站點中,默認使用租戶1 作為es 檢索演示,內置1000多萬條測試數據,通過 書名、書內容關鍵詞、發佈時間範圍
等可進行查詢, 價格
查詢在演示站點中關閉了,無法查詢,請注意。
備註:演示站點默認使用10000條數據查詢上限邊界。