引言

情况大概是这样的,我需要模糊查询Mysql里的数据,但是那张表很大,有几万条数据,并且还会无限扩展(指字段),使用like效率极其低下

在腾讯云中检查慢日志时,看到相关的like语句执行了将近一分钟 :han:

后来也是为了解决问题,又懒得去该表设计,就采用了ElasticSearch

踩坑

ElasticSearch中存在数据,但是查出的结果为空

插数据,删除,修改都没问题,但是查询查不到,查出来一个空数组,为此花了我不少时间

因为百度上根本找不到类似的文章 :spray: , 然后就只能一个一个排查

后来发现是因为没有先创建索引,直接插数据导致的,需要先创建索引

// 索引名
test  -  put
{
    "index": {
        "analysis": {
            "analyzer": {
				"comma": {
					 "type": "pattern",
					 "pattern":","
				}
			}
		}
    }
}
test/system/_mapping  -  post
{
    "system": {
        "properties": {
            "parentIds": { 
                "type": "text","index": true,
                "analyzer": "comma", 
                "search_analyzer": "comma"
            },  
            "id": {
                "index" : true,"type": "text"
            }
        }
    }
}

Linux中配置ElasticSearch

相比在Win中使用,Linux需要修改很多的地方

elasticsearch.yml 增加配置

cluster.name: xiamo
#节点名称
node.name: node-1
#设置索引数据的存储路径
path.data: /data/elasticsearch/data
#设置日志的存储路径
path.logs: /data/elasticsearch/logs
#设置当前的ip地址,通过指定相同网段的其他节点会加入该集群中
network.host: 0.0.0.0
#设置对外服务的http端口
http.port: 9200
#设置集群中master节点的初始列表,可以通过这些节点来自动发现新加入集群的节点
#discovery.zen.ping.unicast.hosts: ["127.0.0.1","10.10.10.34:9200"]
#开启面板可访问
http.cors.enabled: true 
http.cors.allow-origin: "*"
#Centos
bootstrap.system_call_filter: false

limits.conf

vi /etc/security/limits.conf
-------------------------------
#修改/添加配置
* soft nofile 65536
* hard nofile 131072

sysctl.conf

vi /etc/sysctl.conf
-------------------------------
#修改/添加配置
vm.max_map_count=655360
-------------------------------
sysctl -p

配置完毕之后切换到esuser启动

Java Code

对数据库中的数据进行增删改时,需要同步对ES中的数据进行操作

ES中索引对应的Bean

import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
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;

/**
 * @author xiamo
 * @Description: test
 * @ClassName: EsTestBean
 * @date 2021/7/21 17:40
 */
@Data
@NoArgsConstructor
@Accessors(chain = true)
@Document(indexName = "test", type = "system", shards = 1, replicas = 0, createIndex = true)
public class UserParents {

    @Id
    private String id;

    @Field(type = FieldType.Text, index = true, analyzer = "comma", searchAnalyzer = "comma")
    private String parentIds;

}

Repository接口

import org.opsli.modulars.system.user.entity.UserParents;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

/**
 * @author xiamo
 * @Description:
 * @ClassName: ParentIdsRepository
 * @date 2021/7/21 17:42
 */
public interface UserParentsRepository extends ElasticsearchRepository<UserParents, String> {
}

对ES中数据的操作

import org.opsli.modulars.system.user.entity.UserParents;

/**
 * @author xiamo
 * @Description: 存/取用户上级信息(parentIds)
 * @ClassName: IUserParentsService
 * @date 2021/7/24 14:50
 */
public interface IUserParentsService {

    /**
     * Del all.
     * 清楚全部数据
     */
    void delAll();

    /**
     * Save.
     * 保存数据
     *
     * @param bean the bean
     */
    void save(UserParents bean);


    /**
     * Del.
     * 根据id删除
     *
     * @param id the id
     */
    void del(String id);


    /**
     * Init data integer.
     * 初始化数据 - 同步数据库数据
     *
     * @return the int
     */
    Long initData();

    /**
     * Count int.
     * 查 parentIds 中包含该id的数据
     *
     * @param id the id
     * @return the int
     */
    Long count(String id);

}
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.opsli.modulars.system.user.elasticsearch.UserParentsRepository;
import org.opsli.modulars.system.user.entity.SysUserInfo;
import org.opsli.modulars.system.user.entity.UserParents;
import org.opsli.modulars.system.user.service.ISysUserInfoService;
import org.opsli.modulars.system.user.service.IUserParentsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.stereotype.Service;

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

/**
 * @author xiamo
 * @Description: 存/取用户上级信息(parentIds)
 * @ClassName: IUserParentsService
 * @date 2021/7/24 14:50
 */
@Service
public class UserParentsServiceImpl implements IUserParentsService {

    @Autowired
    private ElasticsearchRestTemplate restTemplate;
    @Autowired
    private UserParentsRepository repository;
    @Autowired
    private ISysUserInfoService sysUserInfoService;

    /**
     * Del all.
     * 清楚全部数据
     */
    @Override
    public void delAll(){
        repository.deleteAll();
    }

    /**
     * Save.
     * 保存数据
     *
     * @param bean the bean
     */
    @Override
    public void save(UserParents bean) {
        repository.save(bean);
    }


    /**
     * Del.
     * 根据id删除
     *
     * @param id the id
     */
    @Override
    public void del(String id) {
        repository.deleteById(id);
    }


    /**
     * Init data integer.
     * 初始化数据 - 同步数据库数据
     *
     * @return the integer
     */
    @Override
    public Long initData() {
        // 查出所有数据
        List<SysUserInfo> userInfos = sysUserInfoService.list();
        // 每500条存一次
        List<UserParents> list = new ArrayList<>();
        for (int i = 0; i < userInfos.size(); i++) {
            if (list.size() < 500) {
                UserParents user = new UserParents();
                user.setId(userInfos.get(i).getId());
                user.setParentIds(userInfos.get(i).getParentIds());
                list.add(user);
                if (i >= userInfos.size() - 1) {
                    saveList(list);
                    list.clear();
                }
            } else {
                saveList(list);
                list.clear();
            }
        }
        return Long.parseLong(String.valueOf(userInfos.size()));
    }

    private void saveList(List<UserParents> list) {
        repository.saveAll(list);
    }

    /**
     * Count int.
     * 查 parentIds 中包含该id的数据
     *
     * @param id the id
     * @return the int
     */
    @Override
    public Long count(String id) {
        BoolQueryBuilder mustQuery = QueryBuilders.boolQuery();
        mustQuery.must(QueryBuilders.termQuery("parentIds", id));
        SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(mustQuery).build();
        return restTemplate.count(searchQuery, UserParents.class);
    }

}

本地测试的话,同样一条查询

MySQL中使用的时间是0.67s,但是在ES中只需要0.01s左右

服务器中MySQL查询效率低,大概是因为高负载造成的,所以做读写分离、集群还是很有必要的


Ex - ploooosion!