Elasticsearch 2.0以上版本根据条件批量删除Java如何实现

Elasticsearch在2.0以前版本,删除操作有两种方式,一种是通过id来进行删除,但是这种方式一般不常用,因为id不容易得到;另一种方式是通过先查询操作,然后删除,也就是通过client.prepareDeleteByQuery这种方式来根据条件批量删除数据:

[java]  view plain  copy
  1. DeleteByQueryResponse response = client.prepareDeleteByQuery("library")  
  2. .setQuery(QueryBuilders.termQuery("title""ElasticSearch"))  
  3. .execute().actionGet();  

但是Delete by Query在2.0版本及其以上的版本已经被移除了,因为这种方式会自动强制刷新,所以在大量索引并发的情况下,会很快造成内存溢出。

详情可查看:https://www.elastic.co/guide/en/elasticsearch/client/java-api/1.7/delete-by-query.html

那么在2.0以后的版本,我们如何来进行批量的删除呢?

我们可以先通过Search API查询,然后得到需要删除的批量数据的id,然后再通过id来删除,但是这种方式在大批量数据的删除的时候,依然是行不通的。

具体实现代码:

[java]  view plain  copy
  1. public void deleteByTerm(Client client){  
  2.     BulkRequestBuilder bulkRequest = client.prepareBulk();  
  3.     SearchResponse response = client.prepareSearch("megacorp").setTypes("employee")  
  4.             .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)  
  5.             .setQuery(QueryBuilders.termQuery("first_name""xiaoming"))  
  6.             .setFrom(0).setSize(20).setExplain(true).execute().actionGet();  
  7.     for(SearchHit hit : response.getHits()){  
  8.         String id = hit.getId();  
  9.         bulkRequest.add(client.prepareDelete("megacorp""employee", id).request());  
  10.     }  
  11.     BulkResponse bulkResponse = bulkRequest.get();  
  12.     if (bulkResponse.hasFailures()) {  
  13.         for(BulkItemResponse item : bulkResponse.getItems()){  
  14.             System.out.println(item.getFailureMessage());  
  15.         }  
  16.     }else {  
  17.         System.out.println("delete ok");  
  18.     }  
  19.       
  20. }  

同样通过delete-by-query插件,我们还可以根据type来批量删除数据,这种方式能够删除大批量的数据,他是现将要删除的数据一个一个做标记,然后再删除,于是效率会比较低。下面是官网的说明:https://www.elastic.co/guide/en/elasticsearch/plugins/2.3/plugins-delete-by-query.html

Queries which match large numbers of documents may run for a long time, as every document has to be deleted individually. Don’t use delete-by-query to clean out all or most documents in an index. Rather create a new index and perhaps reindex the documents you want to keep. 

可见这种删除方式并不适合大批量数据的删除,因为效率真的是很低,我是亲身体验过了。


这种方式需要先引入delete-by-query插件包,然后使用插件的api来删除:

[java]  view plain  copy
  1. <dependency>  
  2.     <groupId>org.elasticsearch.plugin</groupId>  
  3.     <artifactId>delete-by-query</artifactId>  
  4.     <version>2.3.2</version>  
  5. </dependency>  


具体实现代码:

[java]  view plain  copy
  1. import java.net.InetAddress;  
  2. import java.net.UnknownHostException;  
  3. import java.util.ResourceBundle;  
  4. import java.util.Stack;  
  5.   
  6. import org.elasticsearch.action.deletebyquery.DeleteByQueryAction;  
  7. import org.elasticsearch.action.deletebyquery.DeleteByQueryRequestBuilder;  
  8. import org.elasticsearch.action.deletebyquery.DeleteByQueryResponse;  
  9. import org.elasticsearch.action.search.SearchRequestBuilder;  
  10. import org.elasticsearch.action.search.SearchResponse;  
  11. import org.elasticsearch.action.search.SearchType;  
  12. import org.elasticsearch.client.Client;  
  13. import org.elasticsearch.client.transport.TransportClient;  
  14. import org.elasticsearch.common.settings.Settings;  
  15. import org.elasticsearch.common.transport.InetSocketTransportAddress;  
  16. import org.elasticsearch.plugin.deletebyquery.DeleteByQueryPlugin;  
  17. import org.slf4j.Logger;  
  18. import org.slf4j.LoggerFactory;  
  19.   
  20. import com.xgd.log.common.ExceptionUtil;  
  21.   
  22. public class EsDeleteByType {  
  23.   
  24.     private static final Logger logger = LoggerFactory.getLogger(EsDeleteByType.class);  
  25.     private Client client;  
  26.       
  27.     private static ResourceBundle getEsConfig(){  
  28.         return ResourceBundle.getBundle("elasticsearch");  
  29.     }  
  30.       
  31.     private void getClient(){  
  32.         String clusterName = getEsConfig().getString("clusterName");  
  33.         String hosts = getEsConfig().getString("hosts");  
  34.         if (hosts == null || clusterName == null) {  
  35.             throw new IllegalArgumentException("hosts or clusterName was null.");  
  36.         }  
  37.         Settings settings = Settings.settingsBuilder().put("cluster.name", clusterName).build();  
  38.         client = TransportClient.builder()  
  39.                 .addPlugin(DeleteByQueryPlugin.class)  
  40.                 .settings(settings).build();  
  41.         String[] hostsArray = hosts.split(",");  
  42.         for(String hostAndPort : hostsArray){  
  43.             String[] tmpArray = hostAndPort.split(":");  
  44.             try {  
  45.                 client = ((TransportClient)client).addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(tmpArray[0]), Integer.valueOf(tmpArray[1])));  
  46.             } catch (NumberFormatException e) {  
  47.                 logger.error(ExceptionUtil.getTrace(e));  
  48.             } catch (UnknownHostException e) {  
  49.                 logger.error(ExceptionUtil.getTrace(e));  
  50.             }  
  51.         }  
  52.     }  
  53.       
  54.     /** 
  55.      * 判断一个index中的type是否有数据 
  56.      * @param index 
  57.      * @param type 
  58.      * @return 
  59.      * @throws Exception 
  60.      */  
  61.     public Boolean existDocOfType(String index, String type) throws Exception {  
  62.         SearchRequestBuilder builder = client.prepareSearch(index).setTypes(type)  
  63.                 .setSearchType(SearchType.QUERY_THEN_FETCH)  
  64.                 .setSize(1);  
  65.         SearchResponse response = builder.execute().actionGet();  
  66.         long docNum = response.getHits().getTotalHits();  
  67.         if (docNum == 0) {  
  68.             return false;  
  69.         }  
  70.         return true;  
  71.     }  
  72.   
  73.     /** 
  74.      * 根据type来删除数据 
  75.      * @param index 
  76.      * @param types 
  77.      * @return 
  78.      */  
  79.     public long deleteDocByType(String index, String[] types) {  
  80.         getClient();  
  81.         long oldTime = System.currentTimeMillis();  
  82.         StringBuilder b = new StringBuilder();  
  83.         b.append("{\"query\":{\"match_all\":{}}}");  
  84.         DeleteByQueryResponse response = new DeleteByQueryRequestBuilder(client, DeleteByQueryAction.INSTANCE)  
  85.         .setIndices(index).setTypes(types)  
  86.         .setSource(b.toString())  
  87.         .execute().actionGet();  
  88.         Stack<String> allTypes = new Stack<String>();  
  89.         for(String type : types){  
  90.             allTypes.add(type);  
  91.         }  
  92.         while(!allTypes.isEmpty()){  
  93.             String type = allTypes.pop();  
  94.             while(true){  
  95.                 try {  
  96.                     if (existDocOfType(index, type) == false) {  
  97.                         break;  
  98.                     }  
  99.                 } catch (Exception e) {  
  100.                     logger.error("queryError: " + e.getMessage());  
  101.                 }  
  102.             }  
  103.         }  
  104.         System.out.println(System.currentTimeMillis() - oldTime);  
  105.         return response.getTotalDeleted();  
  106.     }  
  107. }  



那么当我们在开发中,使用到elasticsearch的时候,总会涉及到大批量数据的删除,我们要怎么办呢?

经过很长时间的纠结,我发现使用elasticsearch存储数据的时候,千万不要把所有数据都存储于一个index,这样一个是不利于查询的效率,一个是不利于后面的删除,既然我们不能index中去删除部分的大批量数据,那么我们为啥不改变一种思路呢,就是分索引,然后通过索引来删除数据,例如:我在生产上面,每天有5亿的数据,那么我每天在集群中生成一个index用于存储这5亿的数据,如果我们的elasticsearch集群对数据只要求保存7天的数据,超过7天的数据就可以删除了,这样我们可以通过index直接删除7天以前的数据,这种方式,我们在查询的时候不会在所有数据中查询,只需要在所要查询的时间段内查询,便提高了查询的效率,同时删除效率的问题也解决了,能够很快删除不需要的数据,释放掉磁盘空间。

针对于elasticsearch大批量数据删除效率的问题,目前官网上面也没有一个特别好的解决办法,这种方式算是目前还算能行得通的方式了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值