mysql数据迁移到elasticsearch以及elasticsearch的使用

发布于:2024-10-17 ⋅ 阅读:(9) ⋅ 点赞:(0)

随着物联网平台的不断发展,平台要求接入的模块会越来越多,对应的数据存储也会有不同的变化,对应的架构也会不断地变化。

  • 存在问题,数据量大后,性能很慢
  • 单表 表字段过多

根据数据不断调整架构

第一版:
数据量不大,采用mysql单表,所有的数据都存放到一个表中,操作简单

第二版:
随着业务的发展,单个表可能达到几个百字段,某个通讯模块解析的时候,实际上只有十几个字段,但是,因为表有几百个字段,会造成空间的一个浪费。查询的性能能明显下降。

  • 采用shardingsphere分表方式, 1个月一个表,减少单表的大小
  • 实际上,空间浪费的问题还没有解决

第三版:
第二个版本运行1年后,增加的不同的物联网通讯模块(解析的场景)越来越多,例如,解析天气,解析大气、解析充电桩、解析气候、解析环保等等,导致我们不得不直面空间浪费的问题。

  • 当前版本的缺陷: 空间浪费过多,如果需要增加字段,所有的按月分的表,都需要新增对应模块的新增字段,因为数据偏多,导致新增字段的花费时长过多,再一个就是,无法达到我们物联网设计的最初的一个初衷,就是想尽量少的改动的前提下,兼容所有的通讯数据的接入。

遇到问题,那我们就要解决问题

  • 1、mysql行转列,逻辑太复杂,没有什么用,排除
  • 2、mysql只存基本的数据,设备id、时间、动态的字段都用json存储(mysql也支持按这里面的数据过滤),好像是5.7以后的版本才支持,经过测试,因为存储的是json数据,导致空间比正常的存储,还大个10倍左右,剔除
  • 3、 还有一个同事提议,不同的通讯模块定义一个表,这样就可以减少表的一个浪费,首先,多表数据查询就是一个问题,不同模块不同表分表也是一个问题,随着模块的越来越多,表维护也是一个问题,给用户的权限太大,会影响历史数据的结构,风险过大。所有该方案剔除
  • 4、mysql只存基础数据,其他的详细数据存es, 我身边认识的大部分人同样类似的业务都是采取该方案,下面就得测试一下

安装elasticsearch 版本8.12.2

下载es
在这里插入图片描述
D:\system\es\8.12.2\elasticsearch-8.12.2\config\jvm.options
增加一句,解决控制台打印乱码的问题

-Dfile.encoding=GBK

在这里插入图片描述

  • 记住这个账号和密码后面有用 账号elastic 密码6*X_gLqvJ3sK=Wbx=bsd

修改D:\system\es\8.12.2\elasticsearch-8.12.2\config\elasticsearch.yml
在这里插入图片描述

  • 设置为false

/bin/elasticsearch.bat 双击这个文件启动,启动后最后这个是账户和密码。
输入https://127.0.0.1:9200/
在这里插入图片描述

  • 设置为false后,需要去掉https的s进行访问

kibana安装

kibana下载

  • 注意需要跟es的版本一样,不然会有奇奇怪怪的问题

创建kibana用户

elasticsearch-reset-password -u kibana_system
  • 因为elastic是 超级用户,无法用这个用户来启动,所以得新创建一个用户
    在这里插入图片描述
  • 账号kibana_system 密码c2zJWwm20-0TxiiHAsEG

配置
修改配置文件"D:\env\kibana-8.15.0\config\kibana.yml"
将下面对应几个注释取消掉,如果修改的话就顺便改一下~

server.port: 5601  
server.host: "0.0.0.0"
# 国际化中文
i18n.locale: "zh-CN"
# 配置es集群url
elasticsearch.hosts: ["http://localhost:9200"] 
# 创建连接用户的账号和密码
elasticsearch.username: "kibana_system"
elasticsearch.password: "c2zJWwm20-0TxiiHAsEG"

启动bin下面的kibana.bat脚本
访问localhost:5601
在这里插入图片描述

  • 输入上面设置的账号elastic 密码6*X_gLqvJ3sK=Wbx=bsd登录,用新创建的用户会提示权限不够

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 写DSL发请求

ik分词

8.12.2下载
在这里插入图片描述

  • 在es的plugins目录下 ,新创建一个ik目录,把ik分词的压缩包放到该目录下面

进入Kibana界面

POST _analyze
{
  "text":"华为手机",
  "analyzer":"ik_smart"
}

在这里插入图片描述

  • 不用分词,会一个汉字一个词,不太友好
普通
im_smart
最小颗粒
ik_max_word

分词的拓展以及停用

在这里插入图片描述

  • 分词的过程中,会存在一些词汇,例如“的”,这个无意义,考虑剔除,可以用来做黑名单,防止一些敏感词出现
  • “欧力给”因为是新的词汇,无法失败,得自己添加
    在这里插入图片描述
  • D:\system\es\8.12.2\elasticsearch-8.12.2\plugins\ik\config 分词插件目录下IKAnalyzer.cfg.xml
  • ext.dic直接输入“欧力给” 如果存在多个,一直按回车
    在这里插入图片描述
  • stopword.dic 是停用词的文件,增加一个的
    在这里插入图片描述
    记得重启es的服务,不然不生效
    在这里插入图片描述
  • 欧力给变成一个词汇,“得”也被剔除

springboot实战

pom.xml

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>

application.yml

elasticsearch:
  port: 9200
  ip: 127.0.0.1
  username: elastic
  password: 6*X_gLqvJ3sK=Wbx=bsd
  • 注意使用自己的es的账号和密码

相关配置

package com.zyee.iopace.web.entity.es;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Document(indexName = "shopping", shards = 3, replicas = 1)
public class Product {
    //必须有 id,这里的 id 是全局唯一的标识,等同于 es 中的"_id"
    @Id
    private Long id;//商品唯一标识

    /**
     * type : 字段数据类型
     * analyzer : 分词器类型
     * index : 是否索引(默认:true)
     * Keyword : 短语,不进行分词
     */
    @Field(type = FieldType.Text, analyzer = "ik_max_word")
    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;//图片地址
}



package com.zyee.iopace.web.config.es;

import lombok.Data;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration;

@ConfigurationProperties(prefix = "elasticsearch")
@Configuration
@Data
public class ElasticsearchConfig extends AbstractElasticsearchConfiguration{

    private String ip ;
    private Integer port ;

    private String username;
    private String password;

    //重写父类方法
    @Override
    public RestHighLevelClient elasticsearchClient() {
        CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));

        RestClientBuilder builder = RestClient.builder(new HttpHost(ip, port));
        builder.setHttpClientConfigCallback(httpClientBuilder -> {
            httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
            return httpClientBuilder;
        });

        RestHighLevelClient restHighLevelClient = new
                RestHighLevelClient(builder);
        return restHighLevelClient;
    }
}


//controller类
    @ApiOperation(value = "测试")
    @GetMapping("/test123")
    public ResponseResult test123() {
        Product product = new Product();
        product.setId(3L);
        product.setTitle("华为手机");
        product.setCategory("手机");
        product.setPrice(2999.0);
        product.setImages("http://www.atguigu/hw.jpg");
        productDao.save(product);
        return ResponseResult.SUCCESS();
    }
  • 插入成功,但是控制台打印报错
    在这里插入图片描述
    在这里插入图片描述
    注意重启项目后,再去搜Version,能看到我们部署的版本是8.12.2,但是java引入的是7.12.1所以报错,注意不要直接再外面try catch来判断,这样代码不太友好,要从根本上解决问题,大概率是版本的问题,考虑升级版本
    在这里插入图片描述
    在这里插入图片描述
  • 再次确定,注意版本一致性,不然有各种各样的问题
    有点尴尬这里版本不一致,只能降到7.12.1版本

###降低版本7.12.1

下载7.12.1 es kibara ik
###D:\system\es\7.12.1\elasticsearch-7.12.1\config\elasticsearch.yml 增加如下
xpack.security.enabled: true
xpack.security.transport.ssl.enabled: false

###es得bin下面执行 修改elastic的初始化密码为123456,一直输入123456
/elasticsearch-setup-passwords interactive 
###新增kibana用户kibana_system c2zJWwm20-0TxiiHAsEG

###修改D:\system\es\7.12.1\kibana-7.12.1\config
server.port: 5601  
server.host: "0.0.0.0"
# 国际化中文
i18n.locale: "zh-CN"
# 配置es集群url
elasticsearch.hosts: ["http://localhost:9200"] 
# 创建连接用户的账号和密码
elasticsearch.username: "elastic"
elasticsearch.password: "123456"

#####项目的application.yml配置
elasticsearch:
  port: 9200
  ip: 127.0.0.1
  username: elastic
  password: 123456

框架集成-SpringData-集成测试-文档操作

import com.lun.dao.ProductDao;
import com.lun.model.Product;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.ArrayList;
import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringDataESProductDaoTest {

    @Autowired
    private ProductDao productDao;
    /**
     * 新增
     */
    @Test
    public void save(){
        Product product = new Product();
        product.setId(2L);
        product.setTitle("华为手机");
        product.setCategory("手机");
        product.setPrice(2999.0);
        product.setImages("http://www.atguigu/hw.jpg");
        productDao.save(product);
    }
    //POSTMAN, GET http://localhost:9200/product/_doc/2

    //修改
    @Test
    public void update(){
        Product product = new Product();
        product.setId(2L);
        product.setTitle("小米 2 手机");
        product.setCategory("手机");
        product.setPrice(9999.0);
        product.setImages("http://www.atguigu/xm.jpg");
        productDao.save(product);
    }
    //POSTMAN, GET http://localhost:9200/product/_doc/2


    //根据 id 查询
    @Test
    public void findById(){
        Product product = productDao.findById(2L).get();
        System.out.println(product);
    }

    @Test
    public void findAll(){
        Iterable<Product> products = productDao.findAll();
        for (Product product : products) {
            System.out.println(product);
        }
    }

    //删除
    @Test
    public void delete(){
        Product product = new Product();
        product.setId(2L);
        productDao.delete(product);
    }
    //POSTMAN, GET http://localhost:9200/product/_doc/2

    //批量新增
    @Test
    public void saveAll(){
        List<Product> productList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            Product product = new Product();
            product.setId(Long.valueOf(i));
            product.setTitle("["+i+"]小米手机");
            product.setCategory("手机");
            product.setPrice(1999.0 + i);
            product.setImages("http://www.atguigu/xm.jpg");
            productList.add(product);
        }
        productDao.saveAll(productList);
    }

    //分页查询
    @Test
    public void findByPageable(){
        //设置排序(排序方式,正序还是倒序,排序的 id)
        Sort sort = Sort.by(Sort.Direction.DESC,"id");
        int currentPage=0;//当前页,第一页从 0 开始, 1 表示第二页
        int pageSize = 5;//每页显示多少条
        //设置查询分页
        PageRequest pageRequest = PageRequest.of(currentPage, pageSize,sort);
        //分页查询
        Page<Product> productPage = productDao.findAll(pageRequest);
        for (Product Product : productPage.getContent()) {
            System.out.println(Product);
        }
    }
}

import com.lun.dao.ProductDao;
import com.lun.model.Product;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.PageRequest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringDataESSearchTest {

    @Autowired
    private ProductDao productDao;
    /**
     * term 查询
     * search(termQueryBuilder) 调用搜索方法,参数查询构建器对象
     */
    @Test
    public void termQuery(){
        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("title", "小米");
                Iterable<Product> products = productDao.search(termQueryBuilder);
        for (Product product : products) {
            System.out.println(product);
        }
    }
    /**
     * term 查询加分页
     */
    @Test
    public void termQueryByPage(){
        int currentPage= 0 ;
        int pageSize = 5;
        //设置查询分页
        PageRequest pageRequest = PageRequest.of(currentPage, pageSize);
        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("title", "小米");
                Iterable<Product> products =
                        productDao.search(termQueryBuilder,pageRequest);
        for (Product product : products) {
            System.out.println(product);
        }
    }

}

相关代码调整

package com.zyee.iopace.web.entity.es;

import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Document(indexName = "test", shards = 3, replicas = 1)
public class Product {
    //必须有 id,这里的 id 是全局唯一的标识,等同于 es 中的"_id"
    @JsonSerialize(using = ToStringSerializer.class)
    @Id
    private Long id;//商品唯一标识

    @Field(type = FieldType.Integer)
    private Integer stationInfoId;

    @JSONField(format = "yyyy-MM-dd HH:mm:ss")
    @Field(type = FieldType.Date)
    private Date reportTime;

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    @Field(type = FieldType.Date)
    private Date createTime;

    //里面是json格式 动态的
    @Field(type =  FieldType.Keyword,index = false)
    private String detail;
}

  • 之前mysql的数据以json格式存到detail字段中
    在这里插入图片描述
    在这里插入图片描述
  • 经过分析,mysql存6w多条数据大约是60M左右,约大了4倍

到目前为止,已经实现了es动态列的功能