本博客继续记录博主学习的Elasticsearch的总结,对于(一)编程不良人的已更换,那篇博客算是废弃了,这篇博客从新开始学习。

介绍

Elasticsearch 是一个分布式可扩展的实时搜索和分析引擎,一个建立在全文搜索引擎 Apache Lucene(TM) 基础上的搜索引擎:

  • 分布式实时文件存储,并将每一个字段都编入索引,使其可以被搜索。
  • 实时分析的分布式搜索引擎
  • 可以扩展到上百台服务器,处理PB级别的结构化或非结构化数据

与MySQL术语对比

MySQL ES
数据库(database) 单元格(Index)
表(table) 类型(type)(7.x版本已经删除这个概念)
行(row) 文档(doc)
列(columns) 字段(fields)

与MySQL索引对比

Elasticsearch使用的倒排索引比关系型数据库的B-Tree索引

B-Tree索引

二叉树查找效率是logN,同时插入新的节点不必移动全部节点,所以用树型结构存储索引,能同时兼顾插入和查询的性能。

因此在这个基础上,再结合磁盘的读取特性(顺序读/随机读),传统关系型数据库采用了B-Tree/B+Tree这样的数据结构

为了提高查询的效率,减少磁盘寻道次数,将多个值作为一个数组通过连续区间存放,一次寻道读取多个数据,同时也降低树的高度。

倒排索引

倒排索引表中的每一项都包括一个属性值和具有该属性值的各记录的地址。

由于不是由记录来确定属性值,而是由属性值来确定记录的位置,因而称为倒排索引(inverted index)。

(感觉比较像MySQL中MyISAM引擎的非聚簇索引

原doc:

ID Name
1 Kate
2 John
3 Bill
4 Bill

ID是Elasticsearch自建的文档id,那么Elasticsearch建立的索引如下:

Term Posting List
Kate 1
John 2
Bill [3,4]

es为每一个字段(field/term)(在倒排索引中所有 term 的合起来称 term dictionary )都建立了一个倒排索引(posting list,其实这个翻译过来叫倒排表,但我觉得可以直接理解为倒排索引)(这里之所以为list表,是因为可以有多个值,且有序,为了方便压缩为bitmap

首先会通过term index(一个索引页,可以理解term index是一颗树,包含的是term的一些前缀)可以快速定位到term dictionary的某个offset(偏移量),然后从这个位置再往后顺序查找找到对应的term,进而找到它的倒排索引(表)posting list

再根据倒排索引posting list即可定位到具体的文档内容doc(总体查找思路有些类似于mysql中的二级索引)

term index不需要存下所有的term,而仅仅是他们的一些前缀与Term Dictionaryblock之间的映射关系,再结合FST(Finite State Transducers)的压缩技术,可以使term index缓存到内存中。

term index查到对应的term dictionaryblock位置之后,再去磁盘上找term,大大减少了磁盘随机读的次数。

以上是是单field索引,如果多个field索引的联合查询,倒排索引是利用跳表(Skip list)的数据结构快速查询的。

ES索引思路


对于使用Elasticsearch进行索引时需要注意:

  • 不需要索引的字段,一定要明确定义出来,因为默认是自动建索引的
  • 同样的道理,对于String类型的字段,不需要analysis的也需要明确定义出来,因为默认也是会analysis的
  • 选择有规律的ID很重要,随机性太大的ID(比如java的UUID)不利于查询

安装

这次依然使用docker,以7.14.2为例

docker hub上elasticsearch已经弃用缺省的标签latest,请加上版本,否则不是最新是老的

elasticsearch

vim /etc/sysctl.conf
# 加入如下两个配置:
# vm.max_map_count=262144
# net.ipv4.ip_forward=1
# 启用配置
sysctl -p
systemctl restart network
# 拉取
docker pull elasticsearch:7.14.2
# 创建es网桥
docker network create esnetwork
# 启动
docker run -d --name es --net esnetwork -p 9200:9200 -p 9300:9300 -v esdata:/usr/share/elasticsearch/data -v esplugins:/usr/share/elasticsearch/plugins -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms512m -Xmx512m" elasticsearch:7.14.2

kibana

这是个ElasicSearch数据可视化的工具,看需求安装

docker pull kibana:7.14.2
docker run -d -p 5601:5601 --link es:elasticsearch --name kibana --net esnetwork kibana:7.14.2

使用

直接通过http请求

因为ElasticSearch提供了许多restful接口来操作数据,所以我们可以直接使用类似postman的工具来访问数据(假设ip地址为localhost):

# 创建shopping索引
PUT http://locahost:9200/shopping
# 查询索引情况
GET http://locahost:9200/shopping
GET http://locahost:9200/_cat/indices?v
# 查询数据ID为2001
GET http://locahost:9200/shopping/_search/2001
# 多条件查询
GET http://locahost:9200/shopping/_search
Body : 
{
    "query" : {
        "bool" : {
            "must" : [
                {
                    "match" : {
                        "title" : "小米"
                    }
                }
            ],
            "filter" : {
                "range" : {
                    "price" : {
                        "gt" : 3900
                    }
                }
            }
        }
    },
    "highlight" : {
        "fields" : {
            "title" : {}
        }
    }
}

具体的操作还有好多,可以直接根据需求翻阅使用文档

SpringBoot整合

依赖:

        <dependency>
            <groupId>org.elasticsearch</groupId>
            <artifactId>elasticsearch</artifactId>
            <version>7.14.2</version>
        </dependency>

        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
            <version>7.14.2</version>
        </dependency>

使用:
索引操作:

public void indexOperation throws Exception{
        RestHighLevelClient esClient = new RestHighLevelClient(
                RestClient.builder(new HttpHost("localhost",9200, "http"))
        );
        //创建索引
//        CreateIndexRequest request = new CreateIndexRequest("user");
//        CreateIndexResponse createIndexResponse =
//                esClient.indices().create(request, RequestOptions.DEFAULT);
        //响应状态
//        boolean acknowledged = createIndexResponse.isAcknowledged();
//        System.out.println(acknowledged);
        //查询索引
        GetIndexRequest request = new GetIndexRequest("user");
        GetIndexResponse getIndexResponse =
                esClient.indices().get(request, RequestOptions.DEFAULT);
        System.out.println(getIndexResponse.getAliases());
        System.out.println(getIndexResponse.getMappings());
        System.out.println(getIndexResponse.getSettings());
        //关闭
        esClient.close();
    }

数据的增删改查:

public class ES_DOC {
    public static void main(String[] args) throws Exception {
        get();
    }
    public static void insert() throws Exception{
        RestHighLevelClient esClient = new RestHighLevelClient(
                RestClient.builder(new HttpHost("localhost",9200, "http"))
        );
        //插入数据
        User user = new User();
        user.setAge(20);
        user.setName("Peter");

        ObjectMapper mapper = new ObjectMapper();
        String s = mapper.writeValueAsString(user);

        IndexRequest request = new IndexRequest();
        request.index("user").id("2001");
        request.source(user, XContentType.JSON);

        IndexResponse response = esClient.index(request, RequestOptions.DEFAULT);

        System.out.println(response.getResult());

        //关闭
        esClient.close();
    }
    public static void update() throws Exception{
        RestHighLevelClient esClient = new RestHighLevelClient(
                RestClient.builder(new HttpHost("localhost",9200, "http"))
        );

        //更新
        UpdateRequest request = new UpdateRequest();
        request.index("user").id("2001");
        request.doc(XContentType.JSON, "name", "Parker");

        UpdateResponse update = esClient.update(request, RequestOptions.DEFAULT);

        System.out.println(update.getResult());

        //关闭
        esClient.close();
    }
    public static void get() throws Exception{
        RestHighLevelClient esClient = new RestHighLevelClient(
                RestClient.builder(new HttpHost("localhost",9200, "http"))
        );

        //查询
        GetRequest request = new GetRequest();
        request.index("user").id("2001");
        GetResponse response = esClient.get(request, RequestOptions.DEFAULT);

        System.out.println(response.getSourceAsString());

        //关闭
        esClient.close();
    }
    public static void delete() throws Exception{
        RestHighLevelClient esClient = new RestHighLevelClient(
                RestClient.builder(new HttpHost("localhost",9200, "http"))
        );

        //删除
        DeleteRequest request = new DeleteRequest();
        request.index("user").id("1001");
        DeleteResponse response = esClient.delete(request, RequestOptions.DEFAULT);

        System.out.println(response.toString());

        //关闭
        esClient.close();
    }
}

可以看出ElasticSearch提供了许多的Request来帮助我们读写数据,跟多的查询操作还有其他的Request,可以根据需求翻阅使用文档

最后修改:2021 年 11 月 01 日