第十二章 Net 5.0 快速開發框架 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條數據查詢上限邊界。