没有聚餐和旅游的春节假期,正好学习一下阮一鸣老师的《Elasticsearch 核心技术与实战》,下面是对 Elasticsearch 里的一些入门知识的学习和总结:
Elasticsearch + Logstash + Kibana(ELK)是一套开源的日志管理方案,可以使用它来搭建可视化的日志分析平台。其中 Elasticsearch 就是本文要讲的开源分布式搜索引擎,经常和它一起使用还有 Logstash,Kibana,Beats,Cerebro,这些工具是用来做什么的呢?
应用程序 -> beats -> [redis | kafka | rabbitMQ] -> logstash -> elasticSearch -> [grafana | kibana]
Logstash 是一个用于管理日志的工具,可以用它去收集日志、转换日志、解析日志并将他们作为数据提供给其它模块调用,例如将数据提供给 Elasticsearch 使用。
Kibana 也是一个开源和免费的数据可视化工具,可以为 ElasticSearch 提供友好的日志分析 Web 界面,可以帮助您汇总、分析和搜索重要数据日志。
Kibana 还提供了一个非常友好的开发工具,是学习 Elasticsearch 必备的 repl 工具,大家可以使用该工具尝试测试和验证 Elasticsearch 中的一些 restAPI 是如何工作的。
beats 是一组轻量级采集程序的统称,由 go 语言开发,负责日志收集,并将将收集到的数据发送给 Logstash 或者 Elasticsearch。elastic 官方支持的 5 种 Beats:
Cerebro 是 Elasticsearch 集群的监控管理工具,使用 Cerebro 我们可以 Elasticsearch 的节点运行情况,磁盘占用情况,并进行实时报警
bin/elasticsearch -E node.name=node1 -E cluster.name=myEs -d
bin/elasticsearch -E node.name=node2 -E cluster.name=myEs -d
bin/elasticsearch -E node.name=node3 -E cluster.name=myEs -d
- 每个节点启动,默认自己是一个 Master-eligible 节点
- 可以通过启动参数 node.master: false 禁止当前启动节点是 Master-eligible 节点
- 所有 Master-eligible 都可以参与选主流程,成为 Master 节点
- 保存分片数据的节点
- 在数据扩展上起了很大的作用
- 通过启动参数 node.data 设置
- 接收客户端请求,将请求分发到合适的节点,最终再对结果进行汇集
- 每个节点默认都是 Coordinating 节点
- 分片数设置太小,影响后续水平扩展,单个分片数据量太大将导致数据重新分配耗时
- 分片数设置过大,影响搜索结果的相关性打分,影响搜索结果数据准确性
- 分片数设置过大,导致单个节点上会有过多的分片,资源浪费,浪费性能
1. _index: 文档所属的索引名
2. _type: 文档所属的类型名
3. _id: 文档的唯一ID
4. _source: 文档存储的 Json 数据
5. _version:文档的版本信息
6. _score: 相关性打分
Elasticsearch 还提供了插件功能,用户可以根据自己的需求安装相应的插件满足搜索,分析,安全,管理,数据备份等功能,比如我们可以安装 elasticsearch-analysis-ik 来满足我们的中文分词功能。
Elasticsearch 相对关系型数据库,更适合相关性、高性能全文检索,并且支持 restAPI 调用,而关系型数据库更适合事务性要求较强的场景,以下是两者概念上的类比:
关系型数据库 | ElasticSearch |
---|---|
Table | Index |
Row | Document |
Column | Field |
Schema | Mapping |
SQL | DSL(domain-specific language) |
Elasticsearch 使用一种称为”倒排索引"的结构,它适用于快速的全文搜索。一个倒排索引由文档中所有不重复词的列表构成,对于其中每个词,有一个包含它的文档列表,这样可以通过某个单词快速的找到其所在的文档:
1. Character Filters: 对原始文本进行预处理,比如去除 HTML,字符串替换,正则匹配替换
2. Tokenizer: 按照规则切分单词,ES 内置的有 whitespace/standard/uax_url_email/pattern/keyword/path hierarchy 等 Tokenizer
3. Token Filter: 对切分的单词进行加工,例如小写转换(Lowercase),删除 stopwords(stop),增加同义词(synonym)等
Standard Analyzer:默认分词器,按词切换,小写处理
Simple Analyzer: 按照非字母切分,小写处理
Stop Analyzer: 小写处理,停用词过滤
Whitespace Analyzer:按照空格切分,不转换小写
Keyword Analyzer: 不分词,直接将输入当作输出
Patter Analyzer: 正则表达式分词
Language Analyzer:提供 30 多种常见语言的分词器
Customer Analyzer:自定义分词器
- icu_analyzer: bin/elasticsearch-plugin install analysis-icu
- ik: bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.3.0/elasticsearch-analysis-ik-6.3.0.zip
- thulac: https://github.com/microbun/elasticsearch-thulac-plugin
Mapping 主要用于定义索引的字段名称和数据类型以及倒排索引等相关配置,Mapping 可以系统自动推断生成,也可以由用户自己定义,下面我们看一个简单 Mapping 的格式:
//该索引包含三个字段
//name,类型是 long,不支持索引搜索
//phone,类型是 keyword,对于值为空的情况可以使用"NULL"字符串来搜索
//name,类型是 text,并定义了索引级别,以及自定义的分词器
{
"mappings" : {
"properties" : {
"id" : {
"type" : "long",
"index": false
},
"name" : {
"type" : "text",
"index_options": "positions",
"copy_to": "fullName",
"fields": {
"english_comment":{
"type": "text",
"analyzer": "english",
"search_analyzer": "english"
}
}
},
"phone" : {
"type" : "keyword",
"null_value": "NULL"
}
}
}
}
- Text:默认情况下会进行分词
- Keyword:不会进行分词,全文本匹配
- Date:日期类型
- Integer/Floating:整数/浮点数
- Boolean:布尔类型
- IPv4 & IPv6
- 特殊类型:geo_point & geo_shape & percolator
- docs: 记录 docId
- freqs:记录 docId 和 term frequencies
- positions:记录 docId 和 term frequencies 和 term postion
- offsets:记录 docId 和 term frequencies 和 term postion 和 character offsets
PUT _template/test_template
{
"index_patterns": ["test*"], // 什么样的 index 会使用这个模板
"order": 1, // 设置模板的优先级
"settings": {
"number_of_shards": 1, // shard 的数量
"number_of_replicas": 2 //replication 的数量
},
"mappings": {
"date_detection": false, //字符串的日期类型是否自动转换
"numeric_detection": true //字符串的数字类型是否自动转换
}
}
- ES 默认的 Settings 和 Mappings
- order 数值低的 Index Template
- order 数值高的 Index Template
- 用户对于当前索引指定的 Settings 和 Mappings
在写入文档时,如果索引不存在 ES 会自动创建索引,并且会根据用户写入的数据按照上面介绍的逻辑自动推断每个字段的类型和配置,每个索引有一个 dynamic 属性来控制动态推断逻辑,该属性只会对新增数据写入起作用:
同时 Elasticsearch 还支持用户定义 Dynamic Template 来通过字段名称来动态设置字段类型:
PUT my_test_index
{
"mappings":{
"dynamic_template": {
"path_match": "name.*", //字段以 name 开头
"path_unmatch": "*.middle", //字段以 middle 结尾
"mapping": {
"type": "text",
"copy_to": "full_name"
}
}
}
}
//对索引 users 中 ID = 1 的文档进行更新操作
POST users/_update/1
{
"doc": {
"user": "chenmangmanga",
"message": "learning elasticSearch",
"class": "one",
"age": 30
}
}
/*
- 支持在一次 API 调用中对不同的索引进行操作
- 支持 INDEX/Create/Update/Delete 四种类型的操作
- 单条记录操作失败,并不会影响其它操作
- 返回结果中包括了每一条操作执行的结果
*/
POST _bulk
{"index": {"_index": "users", "_id": "1"}}
{"user": "yangzhiwei2"}
{"delete":{"_index": "users", "_id": "2"}}
{"create":{"_index": "users", "_id": "3"}}
{"user": "zhuweilin","message": "learning elasticSearch","age": 35}
{"update":{"_index":"users","_id": "1"}}
{"doc":{"user": "helloworld"}}
GET _mget
{
"docs": [{
"_index": "users",
"_id": 1
},{
"_index": "users",
"_id": 3
},{
"_index": "users",
"_id": 2
}]
}
文档的搜索分为 URI Search 和 Request Body Search 两种方式,URI Search 主要在 URL 中通过 query string 的方式传参进行查询,方便简单;Request Body Search 是通过 POST 的请求体 Body 的方式传参进行搜索,支持丰富的搜索格式,下面是搜索相关的 rest API 如下:
搜索结果的是否合理,主要取决于搜索的相关性,搜索相关性由下面三个属性决定:
//搜索 users 索引下,user 字段包含 chenmangmanga,并对搜索结果按照 year 降序,获取前 10 个搜索结果,搜索超时时间为 1 秒
GET users/_search?q=user:chenmangmanga&sort=year:desc&from=0&size=10&timeout=1s
{
"profile": true
}
对于上面查询查询语句 q 字段,Elasticsearch 支持丰富的搜索格式:
1. q=status:active:表示搜索 status 字段中包含 active 的文档
1. q=title:(quick OR brown): 表示搜索 title 字段中包含 quick 或者 brown 的文档,等同于 q=title:(quick brown) 或者 q=title:(quick || brown)
2. q=title:(quick AND brown): 表示搜索 title 字段中包含 quick 并且包含 brown 的文档,等同于 q=title:(quick && brown) 或者 q=title:(quick +brown)
3. q=author:"John Smith": 表示搜索 author 字段中包含 John Smith 的文档
4. q=_exists_:title:表示搜索 title 字段为非 null 的文档
5. q=title:(quick NOT brown): 表示搜索 title 字段中包含 quick 并且不包含 brown 的文档,q=title:(quick ! brown) 或者 q=title:(quick -brown)
1. q=date:[2012-01-01 TO 2012-12-31]:查询 date 字段在 2012-01-01 和 2012-12-31 之间的文档
2. q=count:[10 TO *]:count 字段大于 10 的范围查询
3. q=age:(>=10 AND <20):查询 age 字段大于等于 10 且小于 20 的文档
通配符占用内存大,一般情况下不建议使用
1. q=status:act?ve*a:“?”表示匹配一个任意字符,“*”表示匹配0个或者多个任意字符
1. q=name:/joh?n(ath[oa]n)/
1. q=title:befutifl~1 : 表示增加,删除,替换,或者字符位置转换不超过1的模糊匹配
2. q=title:"lord rings"~2:表示lord 和 rings 中间不超过2个字符的模糊匹配情况
将查询语句通过 HTTP Request Body 的形式发送给 Elasticsearch,支持非常丰富的查询格式,详情请参考官方文档
POST users/_search
{
"profile": "true", //输出ES是如何执行查询的
"query": {
"match": { //user字段匹配 chenmangmanga 或者 zhuweil
"user": {
"query": "chenmangmanga, zhuweil",
"operator": "OR"
}
}
},
"_source": ["user", "age"], //只返回部分字段
"from": 0, //分页
"size": 2,
"sort": [{"age": "desc"}], //排序,支持多个字段排序
"script_fields": {
"newField": { //通过脚本生成新的字段
"script": {
"lang": "painless",
"source": "doc['age'].value + '_hello'"
}
}
}
}
类似关系型数据库,Elasticsearch 同样支持丰富的聚合运算,可以根据某个维度对数据进行求和,平均值,最大值等运算,并可以进行二次聚合运算:
GET kibana_sample_data_flights/_search
{
"size": 0,
"aggs":{
"flight_dest":{
"terms":{
"field":"DestCountry" //根据字段 DestCountry 进行聚合
},
"aggs":{
"avg_price":{ // 求 AvgTicketPrice 的平均值
"avg":{
"field":"AvgTicketPrice"
}
},
"wather":{
"terms": {
"field": "DestWeather", // 二次聚合,求每个 DestCountry 下每个 DestWeather 的情况
"size": 5
}
}
}
}
}
}
Elasticsearch 中聚合主要分为以下几类:
POST /_analyze
{
"analyzer": "standard",
"text": "hello world, you are right"
}
PUT users/_mapping
{
"dynamic": "false"
}