一、Settings设置
可以设置索引库的一些配置信息,主要针对分片数量和副本数量。分片数量只能在一开始创建索引库的时候指定,后期不能修改,副本数量可以随时修改
curl -XGET 'http://192.168.234.100:9200/user/_settings?pretty'
返回的结果:
{
"user" : {
"settings" : {
"index" : {
"routing" : {
"allocation" : {
"include" : {
"_tier_preference" : "data_content"
}
}
},
"number_of_shards" : "1",
"provided_name" : "user",
"creation_date" : "1677417886981",
"number_of_replicas" : "1",
"uuid" : "oBCobIUjTruqKYYLpNVC7w",
"version" : {
"created" : "7170699"
}
}
}
}
}
1.1、手工指定分片数量
curl -H "Content-Type:application/json" -XPUT 'http://192.168.234.100:9200/test1' -d '{"settings":{"number_of_shards":5,"number_of_replicas":1}}'
返回结果
{
"test1" : {
"settings" : {
"index" : {
"routing" : {
"allocation" : {
"include" : {
"_tier_preference" : "data_content"
}
}
},
"number_of_shards" : "5",
"provided_name" : "test1",
"creation_date" : "1677681909514",
"number_of_replicas" : "1",
"uuid" : "eWXdyJ51RyO7G6B2gzrWew",
"version" : {
"created" : "7170699"
}
}
}
}
}
1.2、通过settings修改副本数量
curl -H "Content-Type:application/json" -XPUT 'http://192.168.234.100:9200/test1/_settings' -d '{"index":{"number_of_replicas":0}}'
重新查询下信息:
{
"test1" : {
"settings" : {
"index" : {
"routing" : {
"allocation" : {
"include" : {
"_tier_preference" : "data_content"
}
}
},
"number_of_shards" : "5",
"provided_name" : "test1",
"creation_date" : "1677681909514",
"number_of_replicas" : "0",
"uuid" : "eWXdyJ51RyO7G6B2gzrWew",
"version" : {
"created" : "7170699"
}
}
}
}
}
二、mapping
表示索引库中数据的字段类型信息,类似于MySQL中的表结构信息,默认我们不需要手工指定mapping,因为ES会根据数据格式识别它的类型。如果说你需要对某一些字段添加一些特殊的属性。例如我们想要自己指定分词器,这个时候就必须要手工指定字段的mapping。也就是mapping对应的是数据类型。
| 数据格式 | 数据类型 |
|---|---|
| 字符串 | text、keyword |
| 数字 | long、double |
| 日期 | date |
| 布尔型 | true、false |
| 二进制 | binary |
Text类型支持分词,并且也支持模糊、精确查询,但是它不支持聚合和排序操作,Text类型不限制存储的内容长度,它适合大字段存储。
keyword类型不支持分词,它会直接对数据建立索引,也支持模糊、精确来源,支持聚合和排序操作。最大支持存储的内容长度为32766个utf-8类型的字符。可以通过设置ignore_above这个参数来指定某个字段最大支持的字符长度,超过这个长度的数据不再被索引。这个时候就没有办法通过termQuery去精确查询那个数据了。因为数据太长了。在设置了最大限度,超过这个长度就截掉了,后期再通过原始的数据去精确查询,肯定查不到了,如果没有超过这个最大长度,查询是没有问题的。适合存储:手机号、姓名等等,这些不需要分词的数据
普通整数会识别为long,小数会识别为double。当然也可以手工指定integer、short、byte、float这些数据类型
最常用的类型是date,这个类型可以支持到毫秒,如果特殊情况下我们需要精确到纳秒,这时候可以使用date_nanos类型。其中针对这个date类型,我们可以自定义日期格式,可以使用时间戳,或者使用年月日格式,可以通过format参数来指定这个日期格式
binary,它可以存储编码为Base64字符串的二进制,如果想要存储一些图片,可以存储图片地址或者图片本身,存储图片本身就需要用到binary
curl -XGET 'http://192.168.234.100:9200/user/_mapping?pretty'
返回内容:
{
"user" : {
"mappings" : {
"properties" : {
"age" : {
"type" : "long"
},
"name" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
}
}
}
手工创建指定类型
curl -H "Content-Type:application/json" -XPUT 'http://192.168.234.100:9200/test2' -d '{"mappings":{"properties":{"name":{"type":"text","analyzer":"ik_max_word"},"age":{"type":"integer"}}}}'
查询mappings,返回:
{
"test2" : {
"mappings" : {
"properties" : {
"age" : {
"type" : "integer"
},
"name" : {
"type" : "text",
"analyzer" : "ik_max_word"
}
}
}
}
}
三、ES的偏好查询
ES中的索引数据,最终都是存储在分片里面,分片有多个,并且分片还分为主分片和副本分片。
比如有3个节点3个分片:Node1是master(r0、p1)、node2(r0、r1)、node3(p0、r1) ES查询的步骤:
- 客户端发送一个查询请求发送到node3上面,node3会创建一个空优先队列,主要是为了存储数据结果
- node3负责将查询请求转发到索引的主分片上或者副本分片里面。比如分发到p1和ro这两个分片里面,每个分片在本地执行查询,并且将查询到的结果添加到有序的优先队列里面。node3是随机转发的,也可以由程序员控制
- 每个分片会返回各自优先队列中所有文档的id和排序值,返回给node3,node3会合并这些数据,把它们合并到自己的优先队列里面,产生全局排序后的结果列表
注意:当客户端的一个搜索请求被发送到某个节点的时候,这个节点它就变成了一个协调节点,这个节点的任务是广播查询请求到所有的相关分片,并且将它们查询到的结果整合成全局排序后的结果集合,这个结果集合最终会返回给客户端。
那么如何控制查询请求到分片之间的一个分发规则呢?
package com.strivelearn.es;
import org.apache.http.HttpHost;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import java.io.IOException;
/**
* @author strivelearn
* @version EsPreferenceOp.java, 2023年03月02日
*/
public class EsPreferenceOp {
public static void main(String[] args) throws IOException {
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("192.168.234.100", 9200)));
SearchRequest searchRequest = new SearchRequest();
// 指定索引库,支持指定一个或者多个,也支持通配符,例如:user*
searchRequest.indices("user");
// 指定分片查询方式 默认是随机的
searchRequest.preference();
client.close();
}
}
-
_local:表示查询操作会优先在本地节点(协调节点)的分片中查询,没有的话再到其它节点中查询
searchRequest.preference("_local"); -
_only_local:表示查询只会在本地节点的分片中查询
-
_only_nodes:表示只在指定的节点中查询
这种方式需要指定节点id。获取节点id的方式:
http://192.168.234.100:9200/_nodes?prettysearchRequest.preference("_only_nodes:bZJSMPN4Q4qVnpMXMfn7nw"); -
_prefer_nodes:表示优先在指定的节点上查询
searchRequest.preference("_prefer_nodes:bZJSMPN4Q4qVnpMXMfn7nw"); -
_shards:表示只查询索引库中指定分片的数据
searchRequest.preference("_shards:0,1"); -
custom-string:自定义一个参数,不能以下划线(_)开头
四、ES中的routing路由功能
ES在添加数据时候,会根据数据的id或者routing参数做一个hash,得到的hash值再与该索引库的分片数量取模,得到的值即为存入的分片编号。如果说有多条数据使用相同的routing参数,那么最终计算出来的分片编号肯定是一样的,那么这些数据就可以存储到相同的分片里面,后期我们查询的时候,就只需要到指定分片中查询就可以,可以显著提高查询性能。
ES的极速查询就是routing
新增数据的时候指定routing
curl -H "Content-Type:application/json" -XPOST 'http://192.168.234.100:9200/routtest/_doc/1?routing=testrouting' -d '{"name":"zhangsan","age":18}'
private static void addIndexByJson(RestHighLevelClient client) throws IOException {
IndexRequest request = new IndexRequest("test");
request.id("10");
String json = "{ "name": "zhangsan"}";
request.source(json);
request.routing("testrouting");
client.index(request, RequestOptions.DEFAULT);
}
查询的时候指定routing
searchRequest.routing("testrouting");
五、ES SQL
针对ES中的结构化数据,也可以使用SQL实现聚合统计,比较方便,可以减少很多工作量。ES SQL支持常见的SQL语法,包括分组、排序、函数等,但是目前不支持JOIN等其他函数。支持SQL命令行、RestAPI、JDBC、ODBC等方式操作
切换elastic用户,进入es的安装目录,输入:
bin/elasticsearch-sql-cli http://bigdata01:9200
进入的界面如下:
输入sql语句:
select * from user;
Java代码编写
引入pom依赖
<dependency>
<groupId>org.elasticsearch.plugin</groupId>
<artifactId>x-pack-sql-jdbc</artifactId>
<version>7.17.6</version>
</dependency>
java代码
package com.strivelearn.es;
import java.sql.*;
import java.util.Properties;
/**
* @author strivelearn
* @version EsJdbcOp.java, 2023年03月02日
*/
public class EsJdbcOp {
public static void main(String[] args) throws SQLException {
String esJdbcStr = "jdbc:es://http://bigdata01:9200/?timezone=UTC+8";
Properties props = new Properties();
Connection connection = DriverManager.getConnection(esJdbcStr, props);
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("select * from user");
while (resultSet.next()) {
String name = resultSet.getString(1);
Integer age = resultSet.getInt(2);
System.out.println(name + "---" + age);
}
statement.close();
connection.close();
}
}
目前我这个版本需要购买license,需要使用的话,同上面的代码一样。实际开发中,使用api接口多,很少使用es sql的方式来获取数据,了解下即可