目录
1.安装elasticsearch-head插件看集群的运行情况
14.elasticSearch框架集成Spring Data
19.在并发情况下,Elasticsearch如何保证读写一致性
1.安装elasticsearch-head插件看集群的运行情况
(1)下载es-head.crx文件,插件下载地址: https://github.com/mikewuhao/es-head
(2)把下载好的es-head.crx文件扩展名改为.zip的压缩包文件, 如:es-head.zip,解压缩es-head.zip文件到es-head文件夹
(3)到chrome的扩展程序里–打开开发者模式—加载已解压的扩展程序–上传刚才的es-head文夹, 打开浏览器看右上角的es-head图标插件, 即安装成功
(4)打开elasticsearch-head连接服务即可
2.集群环境下创建索引,进行分片和副本设置
分配三个主分片和一个副本(每个主分片拥有一个副本分片)
使用elasticsearch-head查看情况,边框加粗的是主分片,细的是副本,加星号的是主节点,为了数据的安全,主分片和副本不能放在同一个节点下
3.水平扩容
创建索引的时候设置了主分片的大小,主分片是没法更改的,所以只能对副本进行扩容(一个主分片对应两个副本)
4.应对故障
关闭节点1服务器,此时集群健康状态为黄色,为绿色时表示所有节点正常,为黄色表示有节点故障,但是任然可以运行。此时会重新选取master节点,并且分配分片情况
重新启动node-1001节点,往elasticsearch.yml配置中添加寻找主节点的配置
discovery.seed_hosts: ["localhost:9302","localhost:9303"]
启动node-1001节点,重新分配主分片和副本
5.分片之后存储在哪个分片计算方式
使用路由计算:hash(id) % 主分片数量,会得到一个num值,这个num值即是需要存放的主分片编号
6.分片之后请求数据响应节点
根据路由规则,确定从哪个分片中获取数据,每个节点的主副分片都存放着数据,可以访问任意一个节点,一般使用轮询节点的方式获取数据
7.es集群情况下,写入一行数据的操作流程
(1)客户端请求集群节点(任意)-协调节点
(2)协调节点将请求转换到指定的节点,指定的分片
(3)主分片需要将数据保存
(4)主分片需要将数据发送给副本
(5)副本保存后向主分片反馈
(6)主分片反馈保存情况
(7)客户端获取反馈情况
是否进行写操作,参数consistency可以进行设置,设置值有one:只要主分片状态ok就运行执行写操作;all:必须要主分片和所有的副分片都没有问题才执行写操作;quorum:默认值,大多数分片副本没有问题即执行写操作。
参数timeout:如果没有足够的副本分片,elasticsearch会等待,等待的时间默认是1分钟,如果有需要可以设置timeout的时间,使它更早的停止等待。
8.es集群情况下,查询一条记录
(1)客户端发送查询请求到协调节点
(2)协调节点计算数据所在的分片和全部的副本位置
(3)为了能够负载均衡,可以轮询所有的节点
(4)将请求转发给具体的节点
(5)节点从对应的分片中返回查询结果,将结果反馈给客户端
9.倒排索引
elasticsearch使用的是倒排索引的方式,倒排索引也叫反向索引,通俗来讲正向索引是通过key找value,反向索引则是通过value找key。
假设有3条文档数据
那么Elasticsearch建立的索引如下:
Elasticsearch分别为每个field都建立了一个倒排索引,24,Kate, John Female这些叫term,而[1,2]就是Posting List倒排列表。Posting list就是一个int的数组,倒排列表记录了出现过某个单词的所有文档的文档列表及单词在该文档中出现的位置信息,每条记录称为一个倒排项(Posting)。根据倒排列表,即可获知哪些文档包含某个单词。
10.elasticsearch分析器
(1)内置分词器-标准分析器(standard):使用get方式,向es发送分词数据,返回分词结果,以及分完词后的偏移量起止位置,返回结果中每个元素代表一个单独的词条
(2)ik中文分词器,下载与es相同版本的elasticsearch-analysis-ik-7.13.0,作为es的插件添加到es服务中,添加目录为plugins,重新启动es服务。
此时输入中文分词内容
返回分词结果,按中文的习惯进行分词拆分
{
"tokens": [
{
"token": "测试数据",
"start_offset": 0,
"end_offset": 4,
"type": "CN_WORD",
"position": 0
},
{
"token": "测试",
"start_offset": 0,
"end_offset": 2,
"type": "CN_WORD",
"position": 1
},
{
"token": "数据",
"start_offset": 2,
"end_offset": 4,
"type": "CN_WORD",
"position": 2
}
]
}
ik_max_word:会将文本做最细粒度的拆分,同时输入相同的待拆分文本:中国人
{
"tokens": [
{
"token": "中国人",
"start_offset": 0,
"end_offset": 3,
"type": "CN_WORD",
"position": 0
},
{
"token": "中国",
"start_offset": 0,
"end_offset": 2,
"type": "CN_WORD",
"position": 1
},
{
"token": "国人",
"start_offset": 1,
"end_offset": 3,
"type": "CN_WORD",
"position": 2
}
]
}
ik_smart:会将文本做最粗粒度的拆分
{
"tokens": [
{
"token": "中国人",
"start_offset": 0,
"end_offset": 3,
"type": "CN_WORD",
"position": 0
}
]
}
(3)es扩展词汇(在ik分词器的基础上):在es的plugins目录下的ik文件有一个config目录,创建custom.dic文件,写入我们需要扩展的词汇,在此目录下打开IKAnalyzer.cfg.xml文件,将新疆的custom.dic文件配置进去,重启es服务
此时进行分词时,返回我们扩展的分词项
(4)自定义分析器:通过在一个适合你的特定数据的设置之中组合字符过滤器、分词器、词汇单元过滤器来创建自定义的分析器。
字符过滤器:用来整理一个尚未被分词的字符串
分词器:把字符分解为单个词条或者词汇单元
词汇单元过滤器:经过分词,作为词单元流会按照指定的顺序通过指定的词单元过滤器。
例:创建一个索引的时候添加上自定义的分词器
# PUT http://localhost:9200/create_index
参数body设置
{
"settings":{
"analysis": {
"char_filter": {
"&_to_and":{
"type":"mapping", //类型为映射方式
"mappings":["&=> and"] //将&符号映射为and
}
},
"filter" : {
"my_stopwords":{
"type":"stop", //类型为停用词过滤
"stopwords":["the","a"] //停用的词汇集合
}
},
"analyzer":{
"my_analyzer": { //分词器的名称
"type":"custom", //类型-自定义custom
"char_filter":["html_strip","&_to_and"], //字符过滤器
"tokenizer":"standard", //标准的方式
"filter":["lowercase","my_stopwords"] //过滤器
}
}
}
}
}
执行创建index及自定义分词器my_analyzer
测试此索引下的分词情况
#GET http://localhost:9200/create_index/_analyze
body内容
{
"text":"The quick & brown box",
"analyzer":"my_analyzer"
}
分词结果过滤了The,&好映射为and
{
"tokens": [
{
"token": "quick",
"start_offset": 4,
"end_offset": 9,
"type": "<ALPHANUM>",
"position": 1
},
{
"token": "and",
"start_offset": 10,
"end_offset": 11,
"type": "<ALPHANUM>",
"position": 2
},
{
"token": "brown",
"start_offset": 12,
"end_offset": 17,
"type": "<ALPHANUM>",
"position": 3
},
{
"token": "box",
"start_offset": 18,
"end_offset": 21,
"type": "<ALPHANUM>",
"position": 4
}
]
}
11.创建索引时,内存和磁盘的相关操作
用户创建索引时,先在内存中生成index-》索引在内存中分段Segment-》segment从内存中通过flush刷洗到到disk磁盘中才能被用户查询到-》为了保障内存数据的安全性,数据写入到内存的index后,再向内存的Translog文件中写入数据(与数据库先写日志再写内存相反,写入到index逻辑复杂,容易出错,从translog恢复数据便利,所以先写内存后写日志),当从内容刷洗到磁盘成功后,清除内容中的translog-》因为需要落到disk的数据才能被es检索到,所以在内存和磁盘之间加了操作系统缓存区(os cache),缓存区可以对外提供查询服务,每一秒中从内存中refresh数据到os cache,三十分钟再从os cache中flush刷洗到disk磁盘中
12.解决数据冲突
我们可以使用version版本号来确保运用中相互冲突的变更不会导致数据丢失,老的版本使用version,但是新版本不行了,需要使用_seq_no和_primary_term进行版本控制
使用verison会报错
{
"error": {
"root_cause": [
{
"type": "action_request_validation_exception",
"reason": "Validation Failed: 1: internal versioning can not be used for optimistic concurrency control. Please use `if_seq_no` and `if_primary_term` instead;"
}
],
"type": "action_request_validation_exception",
"reason": "Validation Failed: 1: internal versioning can not be used for optimistic concurrency control. Please use `if_seq_no` and `if_primary_term` instead;"
},
"status": 400
}
使用if_seq_no和if_primary_term参数进行更新
可以使用version加version_type=external进行更新,version版本号需要比当前版本高
13.kibana
kibana是一个免费且开发的用户界面,能够让你对elasticsearch数据进行可视化,并让你再elastic stack中进行导航。你可以进行各种操作,从跟踪查询负载,到理解请求如何流经你的整个运用,都能轻松完成。下载地址:Kibana 7.13.0 | Elastic
(1)解压下载的zip文件
(2)在config/kibana.yml配置文件中进行服务配置
#默认端口
server.port: 5601
#es服务器的地址
elasticsearch.hosts: ["http://localhost:9200"]
#索引名
kibana.index: ".kibana"
#支持语言
i18n.locale: "zh-CN"
(3)window下执行bin/kibana.bat启动文件
(4)浏览器中使用http://localhost:5601访问
14.elasticSearch框架集成Spring Data
(1)Spring Data框架
用于简化数据库、非关系型数据库、索引库访问,并支持云服务的开源框架。其主要目标时使得对数据的访问变得方便快捷,并支持map-reduce框架和云计算数据服务。spring data可以极大的简化JPA的写法,可以在几乎不用写实现的情况下,实现对数据的访问和操作。除了crud外,还包括分页、排序等一些常见的功能。
(2)spring data elasticsearch
基于spring data jpa简化elasticsearch操作,将原始操作elasticsearch客户端的api进行封装。spring data为elasticsearch项目提供集成搜素引擎。Spring boot 2.3.x一般可以兼容elasticsearch7.x
(3)框架集成
①创建maven项目
②编辑pom.xml文件
<!--spring boot父启动项依赖-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
<relativePath/>
</parent>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<!--添加lombok,使实体类通过注解代替get、set、tostring等方法-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.18</version>
</dependency>
<!--引入springboot结合elasticsearch的jar-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<!--spring boot进行热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<!--spring boot测试jar包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--junit测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
</dependencies>
③增加配置文件
在项目的resources目录下创建application.properties文件
#elasticsearch服务地址
elasticsearch.host=127.0.0.1
#elasticsearch端口
elasticsearch.port=9200
#配置日志级别,开启debug模式(com.qingyun是项目的包名,与我们项目的包结构一致)
logging.level.com.qingyun=debug
④创建springboot项目主程序启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 主程序启动类
*/
@SpringBootApplication
public class SpringDataElasticsearchAppliction {
public static void main(String[] args) {
SpringApplication.run(SpringDataElasticsearchAppliction.class,args);
}
}
⑤创建数据实体类
使用lombok注解的方式简化代码
/**
* 实体类,使用lombok注解的方式,简化字段的get、set、tostring、无参构造、有参构造等方法
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Product {
private Long id; //商品唯一id
private String title; //商品标题
private String category;//商品分类
private Double price; //商品价格
private String images; //商品图片
}
⑥配置类
ElasticsearchRestTemplate是spring-data-elasticsearch项目中的一个类,和其它spring项目中的template类似。
ElasticsearchRestTemplate基于RestHighLevelClient客户端的,需要自定义配置类,继承AbstractElasticsearchConfiguration,并实现elasticsearchClient()抽象方法,创建RestHighLevelClient对象。
/**
* elasticsearch配置类
* @Data带有字段的set、get、tostring方法
* @Configuration:配置成配置类,加载进spring容器中
* @ConfigurationProperties(prefix = "elasticsearch"):从application.properties配置文件中加载前缀为elasticsearch的配置
*
*/
@Data
@Configuration
@ConfigurationProperties(prefix = "elasticsearch")
public class ElasticsearchConfig extends AbstractElasticsearchConfiguration {
private String host; //服务地址,数据从配置文件中取
private Integer port; //端口
@Override
public RestHighLevelClient elasticsearchClient() {
RestClientBuilder builder = RestClient.builder(new HttpHost(host, port));
RestHighLevelClient restHighLevelClient = new RestHighLevelClient(builder);
return restHighLevelClient;
}
}
⑦创建实体的dao层
与spring-boot jpa一样的方式,前者继承的是JpaRepository,后者继承的是ElasticsearchRepository
@Repository
@Table(name = "adjust_parame", catalog = "score")
public interface AdjustParameRepository extends JpaRepository<AdjustParame, String>,JpaSpecificationExecutor<AdjustParame>{
}
/**
* 操作实体的dao接口层
*/
@Repository
public interface ProductDao extends ElasticsearchRepository<Product,Long> {
}
⑧修改product实体类,加上映射信息,配置分片等信息
/**
* 实体类,使用lombok注解的方式,简化字段的get、set、tostring、无参构造、有参构造等方法
* @Document(indexName = "product",shards = 3,replicas = 1):
* 配置索引信息,indexName:索引名
* shards:主分片数量
* replicas:副本数量
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Document(indexName = "product",shards = 3,replicas = 1)
public class Product {
//必须有主键id,这里的id是全局唯一标识,等同于es中的_id
@Id
private Long id; //商品唯一id
/**
* type:字段数据类型
* analyzer:分词器类型
* index:是否索引(默认:true)
* Keyword:短语,关键字,不进行分词
*/
//@Field配置每个字段的信息
@Field(type = FieldType.Text)
private String title; //商品标题
@Field(type = FieldType.Keyword)
private String category;//商品分类
@Field(type = FieldType.Double)
private Double price; //商品价格
@Field(type = FieldType.Keyword,index = false)
private String images; //商品图片
}
(4)索引测试
①创建索引
**
* spring data elasticsearch集成测试
* @RunWith(SpringRunner.class):
* Test测试类要使用注入的类,比如@Autowired注入的类,
* 有了@RunWith(SpringRunner.class)这些类才能实例化到spring容器中,自动注入才能生效,
* 不然直接一个NullPointerExecption
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringDataEsIndexTest {
//注入spring-data-elasticsearch包中的类
@Autowired
ElasticsearchRestTemplate elasticsearchRestTemplate;
/**
* 自动读取环境中的类,创建索引
*/
@Test
public void createIndex(){
System.out.println("创建索引");
}
}
执行创建之前
执行之后
②删除索引deleteIndex
//删除索引
@Test
public void deleteIndex(){
boolean flag = elasticsearchRestTemplate.deleteIndex(Product.class);
System.out.println("删除索引:"+flag);
}
(5)向索引操作数据
①使用dao映射的方式向索引添加数据
与spring boot jpa一样,使用dao层的save方法,没有索引也会根据实体配置的indexName创建索引
/**
* 测试使用dao的映射方式向索引中添加数据
* @RunWith(SpringRunner.class):
* Test测试类要使用注入的类,比如@Autowired注入的类,
* 有了@RunWith(SpringRunner.class)这些类才能实例化到spring容器中,自动注入才能生效,
* 不然直接一个NullPointerExecption
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringDataEsProductDaoTest {
@Autowired
ProductDao productDao;
//添加数据
@Test
public void save(){
Product product = new Product();
product.setId(1L);
product.setPrice(2900.0);
product.setCategory("手机");
product.setTitle("小米手机");
product.setImages("http://www.qingyun.com/qingyun.png");
productDao.save(product);
}
}
查询添加的记录,有一个_class记录实体存在的包路径
②修改数据
与spring boot jpa一样,使用实体的方式,根据id相同进行修改
//修改数据数据
@Test
public void update(){
Product product = new Product();
product.setId(1L);
product.setPrice(2900.0);
product.setCategory("手机");
product.setTitle("华为手机");
product.setImages("http://www.qingyun.com/qingyun.png");
productDao.save(product);
}
③根据id查询记录
与spring boot jpa一样,使用dao的findById方法查询
//根据id查询
@Test
public void findById(){
Product product = productDao.findById(1L).get();
System.out.println(product.toString());
}
④查询索引下的所有记录
与spring boot jpa一样,使用dao的findAll方法查询
//根据索引下的所有记录
@Test
public void findAll(){
Iterable<Product> list = productDao.findAll();
for(Product product : list) {
System.out.println(product.toString());
}
}
⑤删除记录
与spring boot jpa一样,根据实体删除记录
//删除记录
@Test
public void delete(){
Product product = new Product();
product.setId(1L);
productDao.delete(product);
}
⑥批量新增
与spring boot jpa一样,根据实体集合使用saveAll新增
//批量添加
@Test
public void saveAll(){
List<Product> list = new ArrayList<Product>();
for(int i = 0;i < 10;i++) {
Product product = new Product();
product.setId(Long.valueOf(i));
product.setPrice(2900.0);
product.setCategory("手机");
product.setTitle("华为手机"+i);
product.setImages("http://www.qingyun.com/qingyun.png");
list.add(product);
}
productDao.saveAll(list);
}
⑦分页排序查询
使用findAll带分页参数查询
//分页排序查询
@Test
public void findByPage(){
//排序的方式:正序(ASC)还是倒序(DESC),排序的字段
Sort sort = Sort.by(Sort.Direction.ASC, "id");
//查询的起始位置
int from = 0;
//查询的游标尺度
int size = 5;
//设置查询分页
PageRequest pageRequest = PageRequest.of(from, size, sort);
Iterable<Product> list = productDao.findAll(pageRequest);
for (Product product : list) {
System.out.println(product);
}
}
(6)文档搜索
与elasticsearch中api的方式类型,使用实体dao层的search带参数TermQueryBuilder进行查询
①使用字段查询
//term查询,search(termQueryBuider)查询方法,参数为构造器对象
@Test
public void termQuery(){
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("category", "手机");
Iterable<Product> list = productDao.search(termQueryBuilder);
for(Product product : list) {
System.out.println(product);
}
}
②字段分页排序查询
//字段分页排序查询
@Test
public void termQueryByPage(){
//排序的方式:正序(ASC)还是倒序(DESC),排序的字段
Sort sort = Sort.by(Sort.Direction.ASC, "id");
//查询的起始位置
int from = 0;
//查询的游标尺度
int size = 5;
//设置查询分页
PageRequest pageRequest = PageRequest.of(from, size, sort);
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("category", "手机");
Iterable<Product> list = productDao.search(termQueryBuilder,pageRequest);
for(Product product : list) {
System.out.println(product);
}
}
③按字段模糊搜索
//按字段模糊搜索
@Test
public void fuzzyQueryByPage(){
//排序的方式:正序(ASC)还是倒序(DESC),排序的字段
Sort sort = Sort.by(Sort.Direction.ASC, "id");
//查询的起始位置
int from = 0;
//查询的游标尺度
int size = 5;
//设置查询分页
PageRequest pageRequest = PageRequest.of(from, size, sort);
FuzzyQueryBuilder fuzziness = QueryBuilders.fuzzyQuery("title", "手机").fuzziness(Fuzziness.ONE);
Iterable<Product> list = productDao.search(fuzziness,pageRequest);
for(Product product : list) {
System.out.println(product);
}
}
15.es优化方式
(1)硬件选择
Elasticsearch的基础是Lucene,所有的索引和文档数据都是存在本地磁盘中,具体的路径可以在config/elasticsearch.yml文件中配置,配置信息如下
# ----------------------------------- Paths ------------------------------------
#
# Path to directory where to store the data (separate multiple locations by comma):
#
#path.data: /path/to/data
#
# Path to log files:
#
#path.logs: /path/to/logs
es重度依赖磁盘,磁盘的吞吐量越大,节点越稳定,优化磁盘的方式:
①使用SSD(固态硬盘),比机械硬盘处理速度快,芯片式的散存存放数据
②使用多块硬盘,并允许elasticsearch通过path.data目录配置把数据条带话分配到它们上面
③不要使用远程的挂载式存储,例如NFS或者SMB/CIFS
(2)分片策略
分片和副本的设计为es提供了分布式和故障转移的特性,但分片和副本不是无限分配的。索引的分片完成分配后由于路由的机制,我们不能重新修改分片数。
主分片、副本、节点数关系可以参考:节点数<=主分片数*(副本+1)
(3)路由选择
当我们查询文档的时候,es是通过路由计算规则知道文档存放在哪个分片的,计算规则:shard=hash(rounting)%number_of_primary_shards
rounting默认是文档的id,也可以采用自定义值,例如用户的id。
不带路由查询方式:因为不知道数据在哪个分片上,所以需要执行的步骤为①分发:请求到达协调节点后,协调节点将请求分发到每个分片上。②聚合:协调节点搜集到每个分片的查询结果,再将查询结果进行排序,之后给用户返回结果
带路由查询方式:查询的时候,可以根据routing信息,定位到某个分片,不需要查询所有的分片。
(4)写入速度优化
①加大Translog flush,目的是降低Iops、WriteBlock
②增加index refresh间隔,目的是减少 Segment merge次数
③优化节点间的任务分布
④合理使用段合并:当有新的数据写入索引时,lucene就会创建新的段,由于lucene段合并的计算量庞大,会消耗大量的IO,所以es默认采用较保守的策略,让后台定期进行段合并。
⑤减少副本数量:如果我们需要进行大批量写入操作,可以先禁止Replica(副本)复制,设置index.number_of_replicas:0 关闭副本。在写入完成后,Replica修改为正常的状态
(5)内存设置
es默认安装后内存大小是1Gb,通过解压安装的es,可以在config/jvm.options文件添加命令设置es的堆大小,xms标识堆的初始大小,xmx表示堆的最大大小。
es堆内存的分配满足原则:
①不要超过物理内存的50%:因为lucene需要有缓存os来存放内存中refresh上来的数据,如果缓存os可用的空间较小,会造成缓存中数据flush到磁盘的操作比较频繁。
②堆内存的大小最好不要超过32Gb:我们会采用31Gb设置 XMS 31g XMX 31g
16.Elasticsearch的master选举流程
(1)elasticsearch的选主模块由ZenDiscovery模块负责的,主要包括Ping(节点之间通过这个RPC来发现彼此),Unicast(单播模块包含一个主机列表,以控制哪些节点需要ping通)
(2)对所有可以成为master的节点(node.master:true)根据nodeId排序,每次选举,每个节点都把自己所知道的节点排一次序,然后选出第一个(第0位)节点,暂且认为它是master节点。
(3)若果对某个节点的投票数达到一定的值,(可以成为master节点数n/2+1),并且该节点自己也选举自己,那这个节点就是master。否则重新选举,一直满足条件为止。
(4)master节点的职责主要包括集群、节点和索引的管理,不负责文档级别的管理。
17.Elasticsearch集群脑裂问题
“脑裂”问题可能的原因
(1)网络问题:集群间的网络问题,导致一些节点访问不到master,认为master挂了从而选举出新的master,并对master上的分片和副本标红,分配新的主分片。
(2)节点负载:主节点的角色既为master又为data,访问量较大时,可能会导致es停止响应造成大面积延迟,此时其它节点得不到主节点的响应,会认为主节点挂了,从而选举新的master节点
(3)内存回收:data节点上的es进程占用的内存较大,引发JVM的大规模内存回收,造成es进程失去响应
“脑裂”问题解决方案
(1)减少误判:discovery.zen.ping_timeout节点状态的响应时间,默认为3秒,可以适当调大,如果master在该响应时间范围内没有做出响应,判断为已经挂掉了。调大参数,例6s(discovery.zen.ping_timeout:6s),可以减少误判
(2)选举触发:discovery.zen.minimum_master_nodes:1该参数是用于控制选举行为发生的最小集群主节点数量。当备选主节点的数值大于等于此值时,且备选节点中有该参数个节点认为主节点已经挂了,才进行选举。官方建议n/2+1,n为具有资格成为主节点的节点个数
(3)角色分离:即master节点和data节点分离,限制角色
主节点配置为:node.master:true node.data:false
从节点配置为:node.master:false node.data:true
18.Elasticsearch更新和删除文档的流程
(1)删除和更新都是写操作,但是es中的文档是不可变的,因此不能被删除或者改动
(2)磁盘上的每个段都有一个.del文件,当删除请求发送后,文档并没有真的被删除,而是在.del文件中被标记为删除。改文档依然能匹配查询,但是会在结果中被过滤掉。当段合并时,在.del文件中被标记为删除的段,将不会写入到新段中
(3)在新的文档被创建时,es会为该文档指定一个版本号,当执行更新时,旧版本的文档在.del中被标记为删除,新版本的文档被索引到一个新段,旧版本的文档依然能匹配查询,但是会在结果中被过滤掉
19.在并发情况下,Elasticsearch如何保证读写一致性
(1)可以通过版本号使用乐观并发控制,以确保新版本不会被旧版本覆盖,由运用层来处理具体的物理冲突
(2)对于写操作,一致性级别支持quorum/one/all,默认为quorum,即只有当大多数分片可用时才允许写操作
(3)对于读操作,可以设置replication为async(默认),这使得操作在主分片和副本分片都完成才返回。如果设置replication为async时,也可以通过设置搜索请求参数_preference为primary来查询主分片,确保文档是最新版本。
20.什么是字典树
字典树又称单词查找树,Trie树,它有三个基本性质:
(1)根节点不包含任何字符,除根节点外每个节点只包含一个字符
(2)从根节点到某一个节点,路径上所有的字符连接起来,为改节点对应的字符串
(3)每个节点包含的所有子节点都不相同,相同的已经合并了
21.深度分页
(1)深度分页:(from:9999;size:10) 第9999后面的10条数据的话就会报错,因为es最大深度是支持搜索到底10000条数据,这个:index.max_result_window字段控制的,默认是10000条数据
(2)解决方式一(修改max_result_window初始值)
PUT 192.168.1.117:9200/sell/_settings
{
"index.max_result_window": 100000
}
(3)解决方式二(使用scroll api)
第一次查询的时候使用scroll语句,带上from和size参数,返回结果中会包含_scroll_id字段,后面的查询参数中带上此scroll_id,每次查询都是基于此scroll_id游标参数进行,当hits返回为空的时候,表示已经查询完所有的记录。第一次执行scroll的时候,会生成当前查询数据的一个快照,若是后面有数据变动,不会同步到此快照中,不能获取到最新的数据;带着scroll_id是基于游标进行查询的,所有它不能跳页,也不能往前,只能往后访问。scroll一般用在导出数据时进行分页查询,查询完所有的数据(当hits为空),进行导出。
(4)search_after
第一次查询数据的时候,需要加上sort排序参数,结果会返回一个sort字段值,后面查询的时候,增加一个参数search_after,值为上次查询时最后一个结果的sort值,当再次查询的时候,从上次查询的最后一个记录往后查询。它不支持跳页,只能往后访问,可以获取到最新的数据。
(5)分页优化
①像百度、Google查询分页一样,显示10页,不支持跳页,进行小页面的跳转,在用户侧进行限制;
②向淘宝式的只允许显示最多100页,后面的记录进行截断处理,在服务端测做处理;
③可以使用search_after分页进行优化,每页显示10条记录,使用search_after每次查询100条记录,可以分为10页,当点击前10页的时候就从一开始查询到的100条里面返回结果;当点击到第11页,再查询下一个100条记录。
项目中使用情况
(1)按年度路线信息,根据路线编码或者路线名称查询
(2)按年度桥梁信息,根据桥梁编码或者桥梁名称查询
(3)按年度隧道信息,根据隧道编码或者隧道名称查询