為什么不能使用數據庫做搜索?
1、比方說,每條記錄的指定字段的文本,可能會很長,比如說“商品描述”字段的長度,有長達數千個,甚至數萬個字符,這個時候,每次都要對每條記錄的所有文本進行掃描。
你包不包含我指定的這個關鍵詞(比如說“牙膏”)
2、還不能將搜索詞拆分開來,盡可能去搜索更多的符合你的期望的結果,比如輸入“生化機”,就搜索不出來“生化危機”
總的來說就是數據庫來實現搜索,是不太靠譜的。通常來說,性能會很差的。
另外:
(1)數據量較大,es的分布式本質,可以幫助你快速進行擴容,承載大量數據
(2)數據結構靈活多變,隨時可能會變化,而且數據結構之間的關系,非常復雜,如果我們用傳統數據庫,那是不是很坑,因為要面臨大量的表
(3)對數據的相關操作,較為簡單,比如就是一些簡單的增刪改查,用我們之前講解的那些document操作就可以搞定
(4)NoSQL數據庫,適用的也是類似于上面的這種場景
什么是全文檢索和Lucene?
lucene,就是一個jar包,里面包含了封裝好的各種建立倒排索引,以及進行搜索的代碼,包括各種算法。我們就用java開發的時候,引入lucene jar,然后基于lucene的api進行去進行開發就可以了。
用lucene,我們就可以去將已有的數據建立索引,lucene會在本地磁盤上面,給我們組織索引的數據結構。另外的話,我們也可以用lucene提供的一些功能和api來針對磁盤上進行操作和查詢。
什么是Elasticsearch?
Elasticsearch的功能,干什么的
(1)分布式的搜索引擎和數據分析引擎
搜索:百度,網站的站內搜索,IT系統的檢索
數據分析:電商網站,最近7天牙膏這種商品銷量排名前10的商家有哪些;新聞網站,最近1個月訪問量排名前3的新聞版塊是哪些
分布式,搜索,數據分析
(2)全文檢索,結構化檢索,數據分析
全文檢索:我想搜索商品名稱包含牙膏的商品,select * from products where product_name like "%牙膏%"
結構化檢索:我想搜索商品分類為日化用品的商品都有哪些,select * from products where category_id='日化用品'
部分匹配、自動完成、搜索糾錯、搜索推薦
數據分析:我們分析每一個商品分類下有多少個商品,select category_id,count(*) from products group by category_id
(3)對海量數據進行近實時的處理
分布式:ES自動可以將海量數據分散到多臺服務器上去存儲和檢索
海聯數據的處理:分布式以后,就可以采用大量的服務器去存儲和檢索數據,自然而然就可以實現海量數據的處理了
近實時:檢索個數據要花費1小時(這就不要近實時,離線批處理,batch-processing);在秒級別對數據進行搜索和分析
跟分布式/海量數據相反的:lucene,單機應用,只能在單臺服務器上使用,最多只能處理單臺服務器可以處理的數據量
elasticsearch的核心概念
(1)Near Realtime(RT):近實時,數據被寫入到es 到可以被搜索到這個時間 在一秒內可以完成。
(2)Cluster:集群,包含多個節點,每個節點屬于哪個集群是通過一個配置(集群名稱,默認是elasticsearch)來決定的,對于中小型應用來說,剛開始一個集群就一個節點很正常
(3)Node:節點,集群中的一個節點,節點也有一個名稱(默認是隨機分配的),節點名稱很重要(在執行運維管理操作的時候),默認節點會去加入一個名稱為“elasticsearch”的集群,如果直接啟動一堆節點,那么它們會自動組成一個elasticsearch集群,當然一個節點也可以組成一個elasticsearch集群
(4)Document&field:文檔,es中的最小數據單元,一個document可以是一條客戶數據,一條商品分類數據,一條訂單數據,通常用JSON數據結構表示,
每個index下的type中,都可以去存儲多個document。一個document里面有多個field,每個field就是一個數據字段。
product document
{
"product_id": "1",
"product_name": "高露潔牙膏",
"product_desc": "高效美白",
"category_id": "2",
"category_name": "日化用品"
}
(5)Index:索引,包含一堆有相似結構的文檔數據,比如可以有一個客戶索引,商品分類索引,訂單索引,索引有一個名稱。一個index包含很多document,一個index就代表了一類類似的或者相同的document。比如說建立一個product index,商品索引,里面可能就存放了所有的商品數據,所有的商品document。
(6)Type:類型,每個索引里都可以有一個或多個type,type是index中的一個邏輯數據分類,一個type下的document,都有相同的field,比如博客系統,有一個索引,可以定義用戶數據type,博客數據type,評論數據type。
type,日化商品type,電器商品type,生鮮商品type
日化商品type:product_id,product_name,product_desc,category_id,category_name
電器商品type:product_id,product_name,product_desc,category_id,category_name,service_period
生鮮商品type:product_id,product_name,product_desc,category_id,category_name,eat_period
每一個type里面,都會包含一堆document,類型于數據庫中,這里的每一行數據就是一個document,
{
"product_id": "2",
"product_name": "長虹電視機",
"product_desc": "4k高清",
"category_id": "3",
"category_name": "電器",
"service_period": "1年"
}
{
"product_id": "3",
"product_name": "基圍蝦",
"product_desc": "純天然,冰島產",
"category_id": "4",
"category_name": "生鮮",
"eat_period": "7天"
}
(7)shard:單臺機器無法存儲大量數據,es可以將一個索引中的數據切分為多個shard,分布在多臺服務器上存儲。有了shard就可以橫向擴展,存儲更多數據,讓搜索和分析等操作分布到多臺服務器上去執行,提升吞吐量和性能。每個shard都是一個lucene index。
(8)replica:任何一個服務器隨時可能故障或宕機,此時shard可能就會丟失,因此可以為每個shard創建多個replica副本。replica可以在shard故障時提供備用服務,保證數據不丟失,多個replica還可以提升搜索操作的吞吐量和性能。primary shard(建立索引時一次設置,不能修改,默認5個),replica shard(隨時修改數量,默認1個),默認每個索引10個shard,5個primary shard,5個replica shard,最小的高可用配置,是2臺服務器。
elasticsearch核心概念 vs. 數據庫核心概念
Elasticsearch | 數據庫 |
---|---|
Index | 庫 |
Type | 表 |
Document | 行 |
Filed | 列 |
windows 下使用es
先安裝java環境,至少JDK1.8,分別下載解壓 elasticsearch-5.2.0.zip,kibana-5.2.0-windows-x86.zip,進入到各自bin目錄下,啟動elasticsearch.bat,kibana.bat腳本,
檢驗是否啟動成功http://localhost:9200/?pretty
http://localhost:5601/
es中的數據與關系型db中的數據
對象數據存儲到數據庫中,只能拆解開來,變為扁平的多張表,每次查詢的時候還得還原回對象格式,相當麻煩。ES是面向文檔的,文檔中存儲的數據結構,與面向對象的數據結構是一樣的,基于這種文檔數據結構,es可以提供復雜的索引,全文檢索,分析聚合等功能
如以下es 中的數據
{
"email": "zhangsan@sina.com",
"first_name": "san",
"last_name": "zhang",
"info": {
"bio": "curious and modest",
"age": 30,
"interests": [ "bike", "climb" ]
},
"join_date": "2017/01/01"
}
es存儲的數據基本與客戶端提交上來的面向對象數據一致,但是假設我們將這個數據持久化到mysql中,我們需要建立兩張表 然后用一個字段來關聯起來。
es API
- cat api
GET /_cat/health?v
如何快速了解集群的健康狀況?green、yellow、red?
green:每個索引的primary shard和replica shard都是active狀態的
yellow:每個索引的primary shard都是active狀態的,但是部分replica shard不是active狀態,處于不可用的狀態
red:不是所有索引的primary shard都是active狀態的,部分索引有數據丟失了
- 快速查看集群中有哪些索引
GET /_cat/indices?v
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
yellow open .kibana Sonk96QMSgmP7seyOVhx8A 1 1 1 0 3.1kb 3.1kb
- (1)新增商品:新增文檔,建立索引。 es會自動建立index和type,不需要提前創建,而且es默認會對document每個field都建立倒排索引,讓其可以被搜索
語法:
PUT /index/type/id
{
"json數據"
}
PUT /ecommerce/product/1
{
"name" : "gaolujie yagao",
"desc" : "gaoxiao meibai",
"price" : 30,
"producer" : "gaolujie producer",
"tags": [ "meibai", "fangzhu" ]
}
// 返回以下數據
{
"_index": "ecommerce",
"_type": "product",
"_id": "1",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"created": true
}
- (2)查詢商品:檢索文檔
語法: GET /index/type/id
GET /ecommerce/product/1
{
"_index": "ecommerce",
"_type": "product",
"_id": "1",
"_version": 1,
"found": true,
"_source": {
"name": "gaolujie yagao",
"desc": "gaoxiao meibai",
"price": 30,
"producer": "gaolujie producer",
"tags": [
"meibai",
"fangzhu"
]
}
}
- (3)修改商品:替換文檔
PUT /ecommerce/product/1
{
"name" : "jiaqiangban gaolujie yagao",
"desc" : "gaoxiao meibai",
"price" : 30,
"producer" : "gaolujie producer",
"tags": [ "meibai", "fangzhu" ]
}
修改數據的時候 用這種方式,有一個容易出業務
bug
的地方是 其他字段會被清空。如下面的命令執行之后 這個數據就只剩下name 這一個filed了,其他沒了。修改數據主post會比較好一點
PUT /ecommerce/product/1
{
"name" : "jiaqiangban gaolujie yagao"
}
- (4)修改商品:更新文檔 (推薦),也叫 partial update 語法
POST /ecommerce/product/1/_update
{
"doc": {
"name": "jiaqiangban gaolujie yagao"
}
}
- (5)刪除商品:刪除文檔
語法 DELETE /ecommerce/product/1
DELETE /ecommerce/product/1
{
"found": true,
"_index": "ecommerce",
"_type": "product",
"_id": "1",
"_version": 9,
"result": "deleted",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
}
}
多種搜索方式
- 1、query string search --這種方式幾乎不會用
搜索商品名稱中包含yagao的商品,而且按照售價降序排序:GET /ecommerce/product/_search?q=name:yagao&sort=price:desc
query string search的由來,因為search參數都是以http請求的query string來附帶的 - 2、query DSL , DSL:Domain Specified Language,特定領域的語言 --這種方式更好用
查詢所有的商品
GET /ecommerce/product/_search
{
"query": { "match_all": {} }
}
查詢名稱包含yagao的商品,同時按照價格降序排序
GET /ecommerce/product/_search
{
"query" : {
"match" : {
"name" : "yagao"
}
},
"sort": [
{ "price": "desc" }
]
}
分頁查詢商品,總共3條商品,假設每頁就顯示1條商品,現在顯示第2頁,所以就查出來第2個商品
GET /ecommerce/product/_search
{
"query": { "match_all": {} },
"from": 1,
"size": 1
}
不查詢所有列,指定特定的列 下面只查詢name和price列
GET /ecommerce/product/_search
{
"query": { "match_all": {} },
"_source": ["name", "price"]
}
- 3、query filter
搜索商品名稱包含yagao,而且售價大于25元的商品
GET /ecommerce/product/_search
{
"query" : {
"bool" : {
"must" : {
"match" : {
"name" : "yagao"
}
},
"filter" : {
"range" : {
"price" : { "gt" : 25 }
}
}
}
}
}
- 4,full-text search(全文檢索)這里與之前的區別是"match" 內的字段的值是兩個詞
GET /ecommerce/product/_search
{
"query" : {
"match" : {
"producer" : "yagao producer"
}
}
}
producer 這個字段,會先被拆解,建立倒排索引
special 4
yagao 4
producer 1,2,3,4
gaoluie 1
zhognhua 3
jiajieshi 2
可以看出 最終的結果第一條肯定是4 因為4既包含yagao也包含producer
- 5、phrase search(短語搜索)
跟全文檢索相對應,相反,全文檢索會將輸入的搜索串拆解開來,去倒排索引里面去一一匹配,只要能匹配上任意一個拆解后的單詞,就可以作為結果返回
phrase search,要求輸入的搜索串,必須在指定的字段文本中,完全包含一模一樣的,才可以算匹配,才能作為結果返回
GET /ecommerce/product/_search
{
"query" : {
"match_phrase" : {
"producer" : "yagao producer"
}
}
}
- 6、highlight search(高亮搜索結果)
GET /ecommerce/product/_search
{
"query" : {
"match" : {
"producer" : "producer"
}
},
"highlight": {
"fields" : {
"producer" : {}
}
}
}
嵌套聚合,下鉆分析,聚合分析
- 1, 計算每個tag下的商品數量 根據tags字段分組顯示其數量
GET /ecommerce/product/_search
{
"size": 0,
"aggs": {
"all_tags": {
"terms": { "field": "tags" }
}
}
}
- 2,對名稱中包含yagao的商品,計算每個tag下的商品數量 (先搜索再分析)
GET /ecommerce/product/_search
{
"size": 0,
"query": {
"match": {
"name": "yagao"
}
},
"aggs": {
"all_tags": {
"terms": {
"field": "tags"
}
}
}
}
- 3,先分組,再算每組的平均值,計算每個tag下的商品的平均價格
GET /ecommerce/product/_search
{
"size": 0,
"aggs" : {
"group_by_tags" : {
"terms" : { "field" : "tags" },
"aggs" : {
"avg_price" : {
"avg" : { "field" : "price" }
}
}
}
}
}
- 4,計算每個tag下的商品的平均價格,并且按照平均價格降序排序
GET /ecommerce/product/_search
{
"size": 0,
"aggs" : {
"all_tags" : {
"terms" : { "field" : "tags", "order": { "avg_price": "desc" } },
"aggs" : {
"avg_price" : {
"avg" : { "field" : "price" }
}
}
}
}
}
- 5,按照指定的價格范圍區間進行分組,然后在每組內再按照tag進行分組,最后再計算每組的平均價格
GET /ecommerce/product/_search
{
"size": 0,
"aggs": {
"group_by_price": {
"range": {
"field": "price",
"ranges": [
{
"from": 0,
"to": 20
},
{
"from": 20,
"to": 40
},
{
"from": 40,
"to": 50
}
]
},
"aggs": {
"group_by_tags": {
"terms": {
"field": "tags"
},
"aggs": {
"average_price": {
"avg": {
"field": "price"
}
}
}
}
}
}
}
}