在生产使用elasticsearch作为唯一存储源

对系统进行架构设计后,需要把es当做唯一存储源,记录下其中踩到的坑:

1,首先es不支持事务,所以在架构设计的时候一定要考虑这一点。

      特别的,es在生产环境一般不允许使用脚本,更新操作都是在业务Java系统内存中去更新,然后再刷新到es数据库,所以当多个线程并发修改时,只会有最后一条更新成功(其实其他的线程也更新成功了,只是被最后一个线程覆盖了),解决办法是上接口的调用者加分布式锁,或者把请求放到保证串行的消息队列(比如kafka的同一个分区)

2,es不支持动态修改mapping,在做具体的index设计时,应该把业务需要的字段都分析完,不要丢

3,非特殊情况,所有字段都应该采用keyword类型,否则将发生灾难!!!在mapping一个index的时候,一定要检查业务Java要操作的字段,在mapping中是否都有定义。比如新建一个index,在JavaBean中定义了一个Person类,有个 String  name的字段,在mapping的时候,漏掉了name,于是在项目跑起来后,第一个插入的会把它mapping成text类型,后续按照trem查询的时候一条都查不出,或者查出错误的数据,所以一定要重视mapping!!!

  另外需要注意的是,在项目启动的时候,应该先检查es对应的index是否存在,然后在运行项目(可以利用spring的InitializingBean接口),这样基本就能保证mapping是正确的了。

@Configuration
@ConfigurationProperties(prefix = "elastic.search.index")
public class ElasticSearchIndexConfig implements InitializingBean {


 @Override
    public void afterPropertiesSet() {
        // ...
        this.checkAllIndexExist();
        // ...
    }


    /**
     * 服务器启动的时候,检查所有index是否都在
     */
    private void checkAllIndexExist() {

     // 判断index,不存在就抛出异常

    }

}

4,推荐使用elasticsearch-rest-high-level-client作为Java操作es的api,虽然写起来比较繁琐,但是更能明白为什么,有时候查不到数据,可以debug出他拼装的查询语句,然后到kibana工具台上执行,进而发现问题,需要注意版本。

5,按照条件查询的时候(排除模糊查询),建议使用trem API。

6,保存数据后,保证立刻再次可见,需要把同步刷新策略改为:IMMEDIATE(强制刷新),es作为一个分布式的搜索引擎,不会对每条数据都实时刷新到磁盘,而是先缓存,再根据策略去刷新。所以改成了强制刷新能保证保存成功后数据可见性,但是他对性能影响严重,需要根据业务去选择。

    /**
         * Don't refresh after this request. The default.
         */
        NONE("false"),
        /**
         * Force a refresh as part of this request. This refresh policy does not scale for high indexing or search throughput but is useful
         * to present a consistent view to for indices with very low traffic. And it is wonderful for tests!
         */
        IMMEDIATE("true"),
        /**
         * Leave this request open until a refresh has made the contents of this request visible to search. This refresh policy is
         * compatible with high indexing and search throughput but it causes the request to wait to reply until a refresh occurs.
         */
        WAIT_UNTIL("wait_for");

7,es的bulk操作不支持刷新策略,他们都是默认的,之所以这样是因为bulk不知道你要操作的类型是什么和你要操作的数据量。比如使用了一个indexRequest之前为了保证数据可见性,策略改为强制刷新,后面有个批量插入的需求,复用了这个indexRequest,就会报异常,需要小心。

8,es能存储海量数据,在查询这些数据的时候,可能会用分页,推荐使用searchAfter API一步到位。sortValues其实就理解为游标。下次查询的时候,es按照这个游标去查。

   public void pageQuery(int age) {
        int resultLength;
        Object[] sortValues = null;
        boolean hasLogTotal = true;
        do {
            SearchResponse response = this.pageQueryData(age, sortValues);
            SearchHit[] searchHits = response.getHits().getHits();
            if (searchHits.length > 0) {
                this.processData(searchHits);
                int index = searchHits.length - 1;
                SearchHit searchHit = searchHits[index];
                sortValues = searchHit.getSortValues();
            }
            resultLength = searchHits.length;
        } while (resultLength > 0);
    }


  private SearchResponse pageQueryData(int  age, Object[] sortValues) {
        BoolQueryBuilder boolQueryBuild = QueryBuilders.boolQuery();
        QueryBuilder matchQuery = QueryBuilders.matchQuery("age", age);
        boolQueryBuild.must(matchQuery);
        SearchSourceBuilder builder = new SearchSourceBuilder();
        builder.query(boolQueryBuild);
        builder.sort(SortBuilders.fieldSort("id").order(SortOrder.ASC));
        builder.size(100);
        if (sortValues != null && sortValues.length > 0) {
            builder.searchAfter(sortValues);
        }

        SearchRequest searchRequest = new SearchRequest("person");
        searchRequest.source(builder);
        SearchResponse response = null;
        try {
            response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return response;
    }

private void processData( SearchHit[] searchHits){
// 转换成Java对象,完成业务操作
}

9,es不支持像mysql丰富的索引,他本身就不是干这一行的,但是当做唯一数据源的时候,有些数据要保证唯一,所以要建唯一索引,比如person,需要保证每个用户身份证号唯一。es实践中那么可以把身份证号当做id,需要小心id不支持修改,意味着用户身份证号发生变化了,需要先删除再创建,而不能企图更新来完成,这里有删除的逻辑,所以要问这样的数据重要吗?用户是否要关心,如果是,那么可以把这样的数据放在backup表。

10,有些时候需要同步数据,在使用mysql的时候,可以利用binlog日志,但是在用es的时候不再支持类似的同步方式,可以使用mq。

11,需要考虑数据备份的情况,一般运维会每天备份一次es,不是热备份,所以通过各种工具链接es的时候一定要小心操作生产环境的数据,否则当天的数据无法恢复!!!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用elasticsearch-sql框架,可以通过以下步骤将SQL数据插入ES: 1. 创建一个ES索引,定义字段映射 2. 使用ES-SQL框架连接ES,执行SQL查询获取数据 3. 遍历查询结果,使用ES-SQL框架提供的API将数据插入ES 下面是一个示例代码,演示如何使用ES-SQL框架将SQL数据插入ES: ```java import io.github.iamazy.elasticsearch.dsl.sql.parser.SqlParser; import org.elasticsearch.action.bulk.BulkRequest; import org.elasticsearch.action.bulk.BulkResponse; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.nlpcn.es4sql.exception.SqlParseException; import org.nlpcn.es4sql.query.QueryAction; import org.nlpcn.es4sql.query.SqlElasticRequestBuilder; import java.io.IOException; import java.sql.*; public class SqlToEs { // 定义ES索引名称 private static final String INDEX_NAME = "my_index"; // 定义ES连接客户端 private RestHighLevelClient client; // 定义SQL查询语句 private String sql = "SELECT * FROM my_table WHERE id > 100"; public SqlToEs() { // 初始化ES连接客户端 client = new RestHighLevelClient(); } public void insert() throws SQLException, IOException, SqlParseException { // 解析SQL查询语句 SqlParser sqlParser = new SqlParser(); QueryAction queryAction = sqlParser.parseSelect(sql); // 构建ES查询请求 SqlElasticRequestBuilder requestBuilder = queryAction.explain(); SearchSourceBuilder sourceBuilder = requestBuilder.getSourceBuilder(); sourceBuilder.query(QueryBuilders.matchAllQuery()); String query = sourceBuilder.toString(); // 执行SQL查询获取数据 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/my_database", "user", "password"); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(query); // 遍历查询结果,将数据插入ES BulkRequest request = new BulkRequest(); while (rs.next()) { // 创建一个ES文档 Map<String, Object> document = new HashMap<>(); document.put("id", rs.getInt("id")); document.put("name", rs.getString("name")); document.put("age", rs.getInt("age")); // 添加到批量请求中 request.add(new IndexRequest(INDEX_NAME).source(document)); } // 执行批量请求 BulkResponse bulkResponse = client.bulk(request, RequestOptions.DEFAULT); if (bulkResponse.status() == RestStatus.OK) { System.out.println("数据插入成功!"); } } } ``` 这里使用ES-SQL框架解析SQL查询语句,并构建ES查询请求。然后执行SQL查询获取数据,并遍历查询结果,将数据插入ES。最后使用ES客户端执行批量请求,将数据插入ES

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值