zoukankan      html  css  js  c++  java
  • mysql转ElasticSearch的分析 及JAVA API 初探

    前言

    最近工作中在进行一些技术优化,为了减少对数据库的压力,对于只读操作,在程序与db之间加了一层-ElasticSearch。具体实现是db与es通过bin-log进行同步,保证数据一致性,代码调用es查询数据,与mysql解耦。
    优势:

    • 减少与mysql的耦合,查询不依赖于mysql特性。因为当前ElasticSearch的势头如同json一样,轻量、简洁。
    • ElasticSearch扩展性强,可以使用廉价机器平行扩展性能。
    • ElasticSearch对所有字段进行了索引,不用在原mysql表中大量添加索引,减少了数据复杂度。

    API的个人理解

    由于es的java api文档不多,因此参照es官方文档的概念,自己总结了一些api的用法,个人看法,不保证正确。
    ElasticSearch官方文档
    Spring-data-es官方文档

    1. term 和 terms 是包含操作,而不是相等操作,假如真的需要完全匹配这种行为,最好是通过添加另一个字段来实现。
    2. 在 bool 条件中过滤器的顺序对性能有很大的影响。更详细的过滤条件应该被放置在其他过滤器之前,以便在更早的排除更多的文档。
    3. 由于es有打分功能,所以api是有配合条件的。withFilter->filter->term,terms,range等一系列不打分的聚合条件。withQuery->must->matchPhrase
    4. 查询语句不仅要查找相匹配的文档,还需要计算每个文档的相关性,所以一般来说查询语句要比 过滤语句更耗时,并且查询结果也不可缓存。《官方文档.p.133》(因此在进行mysql迁移时,优先使用filter)

    迁移案例分析

    实体

    @Document(indexName = "dbName", type = "tableName", shards = 6)
    public class UserInfo{
        /**
         * 主键
         */
        @JsonProperty("id")
        private Long id;
    
        /**
         * 用户编号
         */
        @JsonProperty("user_id")
        private String userId;
    
        /**
         * 分数
         */
        @JsonProperty("score")
        private String score;
        /**
         * 创建时间
         */
        @JsonProperty("order_time")
        @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
        @Field(type = FieldType.Date, index = FieldIndex.not_analyzed, format = DateFormat.custom, pattern = "yyyy-MM-dd HH:mm:ss")
        private Date createTime;

     

    • queryOne
    select * from user_info where id = #{id}

     

    public UserInfo getById(String id){
        CriteriaQuery query = new CriteraQuery(Criteria.where("id").is(id));
        UserInfo userInfo = elasticsearchTemplate.queryForObject(query, UserInfo.class);
    }
    • queryForList(小数据量)
    select * from user_info where user_id in #{userIdList}

     

    public List<UserInfo> getByUserIdList(List<String> userIdList){
        SearchQuery searchQuery = new NativeSearchQueryBuilder().
        withIndices(EsQueryConstant.obtainIndicesName("dbName","tableName")).
        withFilter(QueryBuilders.termsQuery("user_id",userIdList)).
        return elasticsearchTemplate.queryForList(searchQuery,UserInfo.class);
    }
    • queryForList(大数据量)
    select * from user_info where crete_time > #{createTime}

     

    public List<UserInfo> getByUserIdList(Date createTime){
    BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery().
    filter(QueryBuilders.rangeQuery("create_time").gt(new DateTime(createTime).toString("yyyy-MM-dd HH:mm:ss")));
        SearchQuery searchQuery = new NativeSearchQueryBuilder().
        withIndices(EsQueryConstant.obtainIndicesName("dbName","tableName")).
        withFilter(boolQueryBuilder ).build();
    String scrollId = elasticsearchTemplate.scan(searchQuery, TimeValue.timeValueMinutes(20).getMillis(), false);
            List<UserInfo> result= Lists.newArrayList();
            while (true) {
                Page<UserInfo> userInfoPage = elasticsearchTemplate.scroll(scrollId, TimeValue.timeValueMinutes(20).getMillis(), UserInfo.class);
                List<UserInfo> userInfoContent= orderEsPage.getContent();
                if (CollectionUtils.isEmpty(userInfoContent)) {
                    break;
                }
                result.addAll(userInfoContent);
            }
            return result;
    }

    由于es采用的是分布式存储,所以在数据量大到一定程度的情况下,分页已经变得不可行。比如要拿1000-1010的数据,假设es有6个分片,则每个分片都要拿到1010条数据,总体排序以后取到1000-1010的数据。这样的计算显然是不可能的。所以如果数据量够大,应当使用游标的方式查询数据。虽然指定了页大小,但是这只针对于每一片,实际得到的数据不超过片数*页大小。一直循环,直到所有分片都没有满足条件的数据为止。

    • queryForPage
    select * from user_info where score != #{score} limit #{pageIndex},#{pageSize}

    public Page<UserInfo> getByUserIdList(String score,int pageIndex,int pageSize){
    BoolQueryBuilder query= QueryBuilders.boolQuery().
    mustNot(QueryBuilders.termQuery("score", score);
        SearchQuery searchQuery = new NativeSearchQueryBuilder().
        withIndices(EsQueryConstant.obtainIndicesName("dbName","tableName")).
        withPageable(new PageRequest(pageIndex, pageSize)).
        withFilter(query).build();
        return elasticsearchTemplate.queryForPage(searchQuery,UserInfo.class);
    }

    后记

    本文简单的介绍了mysql转ElasticSearch时的一些场景的案例,API并不难,只是相关资料少,很多功能只能探索前进,以后用到了更深入的功能会继续更新。

  • 相关阅读:
    Swift3 重写一个带占位符的textView
    Swift3 使用系统UIAlertView方法做吐司效果
    Swift3 页面顶部实现拉伸效果代码
    Swift3 倒计时按钮扩展
    iOS 获取当前对象所在的VC
    SpringBoot在IDEA下使用JPA
    hibernate 异常a different object with the same identifier value was already associated with the session
    SpringCloud IDEA 教学 番外篇 后台运行Eureka服务注册中心
    SpringCloud IDEA 教学 (五) 断路器控制台(HystrixDashboard)
    SpringCloud IDEA 教学 (四) 断路器(Hystrix)
  • 原文地址:https://www.cnblogs.com/snake23/p/9414570.html
Copyright © 2011-2022 走看看