Skip to content

Latest commit

 

History

History
501 lines (433 loc) · 18 KB

README.md

File metadata and controls

501 lines (433 loc) · 18 KB

Elasticsearch

分布式搜索和分析引擎

1.Es lucene Solr 基本概念 和区别

	lucene,先进、功能强大的搜索库,直接基于lucene开发,非常复杂,api复杂(实现一些简单的功能,写大量的java代码),需要深入理解原理(各种索引结构)

	Solr基于lucene实现了进一步的封装处理,更简单的开箱即用。

	elasticsearch,基于lucene,隐藏复杂性,提供简单易用的restful api接口、java api接口(还有其他语言的api接口)
	  1)分布式的文档存储引擎
	  2)分布式的搜索引擎和分析引擎
	  3)分布式,支持PB级数据
			
	ES与solr的区别:
      1) Solr是提供类似webservice的接口。Es提供rest风格的接口。
      2) Solr 4.x以后支持分布式,而Es天生支持分布式,数据量越多ES搜索效率越高!
      3) Solr的数据格式是xml和json,Es是json
      4) Solr不支持实时数据搜索,而Es可以(从索引一个文档直到这个文档能够被搜索到有一个轻微的延迟(通常是1秒))

2.Es的特性以及应用场景

  1) 开源 
	  2) 提供了JAVA API接口。 
	  3) 提供了RESTful API接口
	  4) REST请求和应答是典型的JSON格式	
      5) 可以作为一个大型分布式集群(数百台服务器)技术,处理PB级数据,服务大公司;也可以运行在单机上,自定义主从备份,自主切换。
	  6) 开箱即用,3分钟安装部署,非常小的耦合度。
	 
	 Es的应用核心是大数据量下的数据搜索和数据分析。
	 搜索:聊天记录的关键字搜索
	 数据分析:APP用户日活量,搜索量最高的商品等等
	 业界内典型的使用场景:
	   1) 百度百科,全文检索,高亮,搜索推荐
	   2) 搜狐新闻,用户行为日志(点击,浏览,收藏,评论)
	   3) 淘宝,搜索商品,(中文,拼音)

3.Es的安装部署过程

Es最新版本为5.6.1 
	历史版本为1.x 2.x 5.x,各版本安装部署差异较大。
    
	这里主要讲基于Linux环境下的Es5.6.1版本安装。
	环境要求:jdk1.8.0_81 及以上版本。我用的1.8.0_144,JDK安装自行百度。
	cd /usr/local  (自定义的Es_Home)
	wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.6.1.tar.gz
	tar -zxvfelasticsearch-5.6.1.tar.gz
	安装完成 后台启动Es
	bin/elasticsearch &  
	
	部分报错问题:
	Error:0x00007f2cae000000, 33554432, 0) failed; error='Cannot allocate memory' (errno=12)
	Java HotSpot(TM) 64-Bit Server VM warning: INFO: os::commit_memory(0x0000000085330000, 2060255232, 0) failed; error='Cannot allocate memory' (errno=12)
	由于elasticsearch5.x默认分配jvm空间大小为2g,修改jvm空间分配,ES_HOME/config/jvm.options  -xms2g -xmx修改为 -xms512m -xmx512m
	Error:can not run elasticsearch as root 
	显然不能用root用户启动,新建用户并切换去启动。
	Error:access denied
	权限不够,用root赋予Es用户文件夹权限 chown esUser /usr/local -R
	其余报错问题这里不占篇幅 参考 http://blog.csdn.net/li_work/article/details/78113614
	
	启动成功,查看 ps -ef|grep elastic  
	
	liunx运行: curl 127.0.0.1:9200 
	返回当前IP下Es节点的状态信息:
		
	{
	  "name" : "Oj6ase4",
	  "cluster_name" : "elasticsearch",
	  "cluster_uuid" : "qbhH0ylgQp-PBARMkB_Peg",
	  "version" : {
		"number" : "5.6.1",
		"build_hash" : "667b497",
		"build_date" : "2017-09-14T19:22:05.189Z",
		"build_snapshot" : false,
		"lucene_version" : "6.6.1"
	  },
	  "tagline" : "You Know, for Search"
	}
	
	name:当前Es节点的实例名称。
	cluster_name:当前Es节点的集群名称,若启动时不指定则默认为elasticsearch;
	剩下就是Es版本的基本信息:版本号,构建时间,基于lucene的版本等。
	
	注:外网或者局域网访问Es,需在Es_Home/config/elasticsearch.yml 中放开NetWork的注释,并将值从192.168.0.1修改为0.0.0.0 保存,重启Es生效。

4.Es的基本教程

	集群:包含多个节点,每个节点属于哪个集群是通过一个配置(集群名称,默认是elasticsearch)来决定的
		  节点只能通过指定某个集群的名字,来加入这个集群。
		  
	节点:名称(默认是随机分配的),节点名称很重要(在执行运维管理操作的时候),默认会去加入一个名称为“elasticsearch”的集群,
	如果直接启动一堆节点,那么它们会自动组成一个elasticsearch集群,当然一个节点也可以组成一个elasticsearch集群
	
	索引(index),类型(type) 和文档(document)
	比较直观的说法是和典型的数据库进行对比:
		Es            DB
		index	      database      每一个索引对应数据库里的一个库
		type          table         每一个文档对应数据库里的一个表
		document      record 		每一个文档对应到表里的一条数据
		区别在于在数据库表里存储某条数据时, 数据所包含的字段 必须等同于 表里所有的字段,多出字段会报错。
		文档在插入type时可以多出来一些字段,多出的字段会在文档里自动添加映射。
		举例:
		DB: insert User(id,name) values(1,'zhangsan','男') 就会报错,因为user表里没有sex字段去存储 男。存储失败!
		ES: 就会在索引的mapping里自动添加上一个field字段,并定义为String类型,并添加数据成功。	  
		
		document通常用JSON数据结构表示,
		每个index下的type中,都可以去存储多个document。一个document里面有多个field,每个field就是一个数据字段
		
	分片:当一个索引上存储上的数据超出了当前节点硬件存储的限制,Es允许索引上的数据进行化成多份,每份叫做一个分片,分片可以存储到当前集群上的
		  其他节点上,通过分片可以水平分割/扩展你的内容容量,允许用户在分片之上进行分布式的、并行的操作,进而提高性能/吞吐量
	
	复制:任何一个服务器随时可能故障或宕机,此时分片可能就会丢失,
		  因此可以为每个分片创建多个复制的副本。副本可以在分片故障时提供备用服务,保证数据不丢失,
		  多个副本还可以提升搜索操作的吞吐量和性能。
		  分片(建立索引时一次设置,不能修改,默认5个),
		  复制(随时修改数量,默认1个),默认每个索引10个shard,5个分片,5个备份副本,最小的高可用配置,是2台服务器
	
	现在开始和es restful接口的一些基本通信:
	
	/_cat Es提供的一些系统级的API查询restful 接口,返回均为json格式数据
		curl 127.0.0.1:9200/_cat/health?v              检查集群的健康状态
		curl 127.0.0.1:9200/_cat/nodes?v               获得节集群中的节点列表
		curl 127.0.0.1:9200/_cat/indices?v             检查所有的索引信息
		curl 127.0.0.1:9200/customer/_mapping?pretty   查看customer索引的当前mappping配置,返回当前索引Mapping,
													       当新增的文档中存在mapping不存在的字段时,会自动根据字段类型映射到mapping中。
		
	基本增删改查语句:
		curl -XPUT 127.0.0.1:9200/customer?pretty      创建一个名字叫做customer的索引,参数pretty意思为返回的json格式化一下。
		curl -XPUT 127.0.0.1:9200/customer/message/1?pretty -d '
		{
		  "name": "zhang san"
		}'
		给索引customer下类型为message中存入一条文档,文档字段名为name,值为 zhangsan,id指定为1,若不指定,则Es随机分配一个Id,并返回
		curl 127.0.0.1:9200/customer/message/1?pretty   获取customer下message里id为1的文档信息。
		curl 127.0.0.1:9200/customer/_search?pretty     获取customer索引下所有文档。
		curl -XDELETE 127.0.0.1:9200/customer?pretty    删除customer索引
		curl -XPOST 127.0.0.1:9200/customer/external/1/_update?pretty' -d '
		{
		  "doc": { "name": "Jane Doe" }
		}'       修改语句
	
	批处理语句 _bulk
		 curl -XPOST 127.0.0.1:9200/customer/external/_bulk?pretty -d '
		{"update":{"_id":"1"}}
		{"doc": { "name": "lisi" } }
		{"delete":{"_id":"2"}}
		首先更新了id为1的name字段,然后执行了删除id=2的文档。
		bulk API按顺序执行这些动作。如果其中一个动作因为某些原因失败了,将会继续处理它后面的动作。
		当bulk API返回时,它将提供每个动作的状态(按照同样的顺序),所以你能够看到某个动作成功与否。
	
	进阶DSL(多条件搜索语句):
		Elasticsearch提供一种JSON风格的特定领域语言,利用它你可以执行查询称为查询DSL。
		具体方法参照SQL 基本就懂了。
		curl -XPOST 127.0.01:9200/customer/_search?pretty -d '
			{
			  "query": { "match_all": {} }
			}'
		解析:select * from customer ;	
		
		curl -XPOST 127.0.01:9200/customer/_search?pretty -d '
		{
		  "query": { "match_all": {} },
		  "size": 1
		}'
		解析:select * from customer limit 1;	注意,如果没有指定size的值,那么它默认就是10。
		
		curl -XPOST 127.0.01:9200/customer/_search?pretty -d '
		{
		  "query": { "match_all": {} },
		  "from": 10,
		  "size": 10
		}'
		解析:select * from customer limit 10,10;		
		
		curl -XPOST 127.0.01:9200/customer/_search?pretty -d '
		{
		  "query": { "match_all": {} },
		  "sort": { "balance": { "order": "desc" } }
		}'
		解析:select * from customer order by balance desc limit 0,10;	
		
		curl -XPOST 127.0.01:9200/customer/_search?pretty -d '
		{
		  "query": {
			"bool": {
			  "must": [
				{ "match": { "address": "mill" } },
				{ "match": { "address": "lane" } }
			  ]
			}
		  }
		}'
		解析:select * from customer where address like "%mill%" and address like "%lane%";
		{

		 "query": {
		    "match": {"name": "liu" }
		  }, 
		    "from":0,
		    "size":10
		}
		
		curl -XPOST 127.0.01:9200/customer/_search?pretty -d '
			{
			 
			 "query": {
				"bool": {
				
				"must": [					
				{ "match": { "age": "40" } }
				  ],					 
				 "must_not": [		
					{ "match": { "state": "ID" } }
				  ]
				}
			  }
			}'
		
		解析:select * from customer where age =40 and state not in ("ID");
		
		curl -XPOST 127.0.01:9200/customer/_search?pretty -d '
			{
			  "size": 0,
			  "aggs": {
				"group_by_state": {
				  "terms": {
					"field": "state"
				  }
				}
			  }
			}'
		解析:SELECT COUNT(*) from customer GROUP BY state ORDER BY COUNT(*) DESC

5.JAVA整合 Es API

Java结合Es有两种方式
	一种是直接发送http请求Es暴露对外的restful接口,类似工具:url  httpClient等等 都可以实现。
    一种是用Es提供的Jar包进行调用实现。
	
	这里主要讲一下JAVA整合 Es API
	首先在项目中导入需要的maven依赖
		<dependency>
          <groupId>org.elasticsearch</groupId>
          <artifactId>elasticsearch</artifactId>
          <version>5.3.1</version>
      </dependency>
		<dependency>
			  <groupId>org.elasticsearch.client</groupId>
			  <artifactId>transport</artifactId>
			  <version>5.3.1</version>
		</dependency>
	
	结合SpringCloud在项目的application.properties中定义好需要的配置变量,或者直接在config中统一配置管理
	也可以在自己的代码中写死,测试练习用
		es.cluster_name=elasticsearch
		es.cluster_server_ip=47.93.6.131
		es.index_name=customer
		es.url=http://47.93.6.131:9200
	
	Java Code:
		@Value("${es.cluster_name}")
		private String cluster_name;// 实例名称
		@Value("${es.cluster_server_ip}")
		private String cluster_serverip;// elasticSearch服务器ip
		@Value("${es.index_name}")
		private String indexname;// 索引名称
		

		/**
		 * 返回一个到ElasticSearch的连接客户端
		 * 
		 * @return
		 */
		private TransportClient getClient() {
			Settings settings = Settings.builder().put("cluster.name", cluster_name).build();// 设置集群名称
			TransportClient client = new PreBuiltTransportClient(settings);// 创建client
			try {
				client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(cluster_serverip), 9300));// 增加地址和端口
			} catch (UnknownHostException e) {
				e.printStackTrace();
				logger.error("ElasticSearch连接失败!");
			}

			return client;
		}
	这就创建了一个最基本的TransportClient,原理是利用IP地址和端口,集群名字,作为一个无存储的节点加入该集群,加入后就获取到了其他节点的信息
	并通过指定的API进行通信!
	
	参考了部分网上的资料,自己封装了一套API,
	查询,搜索还是建议用url请求方式,快一些。
	插入等操作用API好一些。

	代码已上传 github 
	地址:https://github.com/likaile/elasticSearcheDemo
	欢迎加星

6.Es分词器

	Es中,内置了很多分词器(analyzers),
		standard (标准分词器) 默认分词器,最无脑的一个一个词(汉字)切分,所以适用范围广,但是精准度低
		english  (英文分词)	  对英文更加智能,可以识别单数负数,大小写,过滤stopwords
		chinese  (中文分词)   Es提供给中文的一个分词器,但是效果很差。
	
	所以我们要安装第三方的一个中文分词器,来达到我们想要的效果。
	首先通过对比ik和chinese的分词效果来体现ik的好处。
	chinese分词器
		http://47.93.6.131:9200/index2/_analyze?analyzer=chinese&text=我爱你我的家&pretty 
	    返回json:
		{
			"tokens": [
			  {
			"token": "我",
			"start_offset": 0,
			"end_offset": 1,
			"type": "<IDEOGRAPHIC>",
			"position": 0
			},
			  {
			"token": "爱",
			"start_offset": 1,
			"end_offset": 2,
			"type": "<IDEOGRAPHIC>",
			"position": 1
			},
			  {
			"token": "你",
			"start_offset": 2,
			"end_offset": 3,
			"type": "<IDEOGRAPHIC>",
			"position": 2
			},
			  {
			"token": "我",
			"start_offset": 3,
			"end_offset": 4,
			"type": "<IDEOGRAPHIC>",
			"position": 3
			},
			  {
			"token": "的",
			"start_offset": 4,
			"end_offset": 5,
			"type": "<IDEOGRAPHIC>",
			"position": 4
			},
			  {
			"token": "家",
			"start_offset": 5,
			"end_offset": 6,
			"type": "<IDEOGRAPHIC>",
			"position": 5
			}
			],
			}
		可以看到和standard分词器一样很无脑的分成了一个一个的单个索引,
		
		对比ik
		http://47.93.6.131:9200/index2/_analyze?analyzer=ik_max_word&text=我爱你我的家&pretty 
		返回json:
		{
			"tokens": [
			  {
			"token": "我爱你",
			"start_offset": 0,
			"end_offset": 3,
			"type": "CN_WORD",
			"position": 0
			},
			  {
			"token": "爱你",
			"start_offset": 1,
			"end_offset": 3,
			"type": "CN_WORD",
			"position": 1
			},
			  {
			"token": "你我",
			"start_offset": 2,
			"end_offset": 4,
			"type": "CN_WORD",
			"position": 2
			},
			  {
			"token": "的",
			"start_offset": 4,
			"end_offset": 5,
			"type": "CN_CHAR",
			"position": 3
			},
			  {
			"token": "家",
			"start_offset": 5,
			"end_offset": 6,
			"type": "CN_CHAR",
			"position": 4
			}
			],
			}
		很明显 IK分词 就友好了很多。
		ik 带有两个分词器 ,这里用的是ik_max_word
		ik_max_word :会将文本做最细粒度的拆分;尽可能多的拆分出词语 
		ik_smart:会做最粗粒度的拆分;已被分出的词语将不会再次被其它词语占有 
	
	ik的安装和使用过程。
		参考 github原文  https://github.com/medcl/elasticsearch-analysis-ik
		1.download or compile   

		optional 1 - download pre-build package from here: https://github.com/medcl/elasticsearch-analysis-ik/releases

		unzip plugin to folder your-es-root/plugins/

		optional 2 - use elasticsearch-plugin to install ( version > v5.5.1 ):

		./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v5.6.1/elasticsearch-analysis-ik-5.6.1.zip

		2.restart elasticsearch

		方法1:下载zip包,解压到 your-es-home/plugs/ik 下 
		方法2 使用es插件下载 版本要大于5.5.1  
		最后一步,重启ES
		很简单,
		因为索引的Mapping不可以删除,所以我们新建一个索引,并配置mapping进行测试。

		curl -XPUT http://localhost:9200/index1
		
		curl -XPOST http://localhost:9200/index1/message/_mapping -d'  
		{  
				"properties": {  
					"content": {  
						"type": "text",  
						"analyzer": "ik_max_word",  
						"search_analyzer": "ik_max_word"  
					}  
				}  
			  
		}'
		新建索引index1,类型message,设置字段content, 设置搜索分词器为ik。
		
		搜索关键字content 我爱你   返回 我爱你我的家 成功。
		
	pinyin分词器
		参考原文 https://github.com/medcl/elasticsearch-analysis-pinyin
		首先来看下pinyin分词器 对于我爱你我的家的分词效果
		{  
		  "tokens" : [  
			{  
			  "token" : "wo",  
			  "start_offset" : 0,  
			  "end_offset" : 1,  
			  "type" : "word",  
			  "position" : 0  
			},  
			{  
			  "token" : "ai",  
			  "start_offset" : 1,  
			  "end_offset" : 2,  
			  "type" : "word",  
			  "position" : 1  
			},  
			{  
			  "token" : "ni",  
			  "start_offset" : 2,  
			  "end_offset" : 3,  
			  "type" : "word",  
			  "position" : 2  
			},  
			{  
			  "token" : "wo",  
			  "start_offset" : 3,  
			  "end_offset" : 4,  
			  "type" : "word",  
			  "position" : 3  
			},  
			{  
			  "token" : "de",  
			  "start_offset" : 4,  
			  "end_offset" : 5,  
			  "type" : "word",  
			  "position" : 4  
			},  
			{  
			  "token" : "jia",  
			  "start_offset" : 5,  
			  "end_offset" : 6,  
			  "type" : "word",  
			  "position" : 5  
			},  
			{  
			  "token" : "wanwdj",  
			  "start_offset" : 0,  
			  "end_offset" : 6,  
			  "type" : "word",  
			  "position" : 5  
			}  
		  ]  
		}
		pinyin的分词器和IK一样 下载ZIP,解压到plugs 下 pinyin文件夹  重启。

		根据官方git地址 测试案例 整合一个索引进行 数据的拼音搜索   测试成功。