在使用Elasticsearch之前,先给大家聊一点干货。
1. ES和solr都是作为全文搜索引擎出现的。都是基于Lucene的搜索服务器。
2. ES不是可靠的存储系统,不是数据库,它有丢数据的风险。
3. ES不是实时系统,数据写入成功只是trans log成功(类似于MySQL的bin log),写入成功后立刻查询查不到是正常的。因为数据此刻可能还在内存里而不是进入存储引擎里。同理,删除一条数据后也不是马上消失。写入何时可查询?ES内部有一个后台线程,定时将内存中的一批数据写入到存储引擎,此后数据可见。默认后台线程一秒运行一次。该线程运行的越频繁,写入性能越低。运行的频率越低,写入的性能越高(不会无限高)。
4. 目前已知的单ES集群可以存储PB级别的数据,不过这个就非常费劲了。TB级别数据没压力。
5. 如果使用ES官方提供的jar包访问,需要JDK1.7及以上。
6. 使用对应的版本访问ES server。如果ES server端的版本是1.7,那么请使用ES 1.7的client。如果ES server是2.1,请使用2.1的client。
7. ES索引存在Linux服务器的文件系统之上(背后是文件系统,不是类似于HDFS的分布式文件系统)
8. ES Java client是线程安全的,全局构建一个即可满足读写需求,不要每次都创建ES client。每次访问ES都构建新的es client即会抛出次异常。
9. 非常不建议使用ES的动态识别和创建的机制,因为很多情况下这并非你所需要。推荐的做法是在写数据之前仔细的创建mapping。
10. 强烈不建议在ES中使用深分页。可能会导致集群不可用。
11. ES是静态分片,一旦分片数在创建索引时确定那么后继不能修改。
12. ES里提供了type,很多人以为type是物理表,一个type的数据是独立存储的;但是在ES内部并不是这样,type在ES内部仅仅是一个字段。所以在很多数据能分为独立index的情况下,不要放到一个index里用type去分。只有嵌套类和父子类的情况下使用type才是合理的。
13. ES并不提供原生的中文分词的能力。有第三方的中文分词的插件,比如ik等。Ik是个toy分词器,有严肃的分词需求的话,请在使用ES之前使用独立的分词器分好词后向ES写入。
14. ES中的index,首先会进行分片,每一个分片数据一般都会有自己的副本数据,ES分配分片的策略会保证同一个分片数据和自己的副本不会分配到同一个节点上。当集群中的某一节点宕机后,ES的master在ping该节点时通过一定的策略会发现该节点不存活;会开启ES的恢复过程
15. ES没有update的能力。所有的update都是标记删除老文档,然后重新insert一条新文档。
好了,回归正题。
首先:
增加我们的spring配置
<bean id="client" factory-bean="esClientBuilder" factory-method="init" destroy-method="close"/> <bean id="esClientBuilder" class="com.***.EsClientBuilder"> <property name="clusterName" value="集群名称" /> <property name="nodeIpInfo" value="集群地址" /> </bean></div>
其次:
编写我们的EsClientBuilder类初始化我们的ES参数
package ***; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.HashMap; import java.util.Map; import org.elasticsearch.client.Client; import org.elasticsearch.client.transport.TransportClient; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.InetSocketTransportAddress; public class EsClientBuilder { private String clusterName; private String nodeIpInfo; private TransportClient client; public Client init(){ //设置集群的名字 Settings settings = Settings.settingsBuilder() .put("client.transport.sniff", false) .put("cluster.name", clusterName) .build(); //创建集群client并添加集群节点地址 client = TransportClient.builder().settings(settings).build(); Map<String, Integer> nodeMap = parseNodeIpInfo(); for (Map.Entry<String,Integer> entry : nodeMap.entrySet()){ try { client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(entry.getKey()), entry.getValue())); } catch (UnknownHostException e) { e.printStackTrace(); } } return client; } /** * 解析节点IP信息,多个节点用逗号隔开,IP和端口用冒号隔开 * * @return */ private Map<String, Integer> parseNodeIpInfo(){ String[] nodeIpInfoArr = nodeIpInfo.split(","); Map<String, Integer> map = new HashMap<String, Integer>(nodeIpInfoArr.length); for (String ipInfo : nodeIpInfoArr){ String[] ipInfoArr = ipInfo.split(":"); map.put(ipInfoArr[0], Integer.parseInt(ipInfoArr[1])); } return map; } public String getClusterName() { return clusterName; } public void setClusterName(String clusterName) { this.clusterName = clusterName; } public String getNodeIpInfo() { return nodeIpInfo; } public void setNodeIpInfo(String nodeIpInfo) { this.nodeIpInfo = nodeIpInfo; } }</div>
最后:
下面我们就可以写自己的service类了,此类就可以通过es的原生api来操作我们的es(这里我们展示的2.X版本的)
indexName相当于数据库名,typeName相当于表名
请参考EsServiceImpl.Java文件
package ***; @Service("esService") public class EsServiceImpl{ @Autowired private Client client; /** * 用docId获取document * @param indexName * @param typeName * @param docId */ private static void getWithId(String indexName, String typeName, String docId) { //get with id GetResponse gResponse = client.prepareGet(indexName, typeName, docId).execute().actionGet(); System.out.println(gResponse.getIndex()); System.out.println(gResponse.getType()); System.out.println(gResponse.getVersion()); System.out.println(gResponse.isExists()); Map<String, Object> results = gResponse.getSource(); if(results != null) { for(String key : results.keySet()) { Object field = results.get(key); System.out.println(key); System.out.println(field); } } } private static void indexWithBulk(String index, String type) { //指定索引名称,type名称和documentId(documentId可选,不设置则系统自动生成)创建document IndexRequest ir1 = new IndexRequest(); String source1 = "{" + "\"user\":\"kimchy\"," + "\"price\":\"6.3\"," + "\"tid\":\"20001\"," + "\"message\":\"Elasticsearch\"" + "}"; ir1.index(index).type(type).id("100").source(source1); IndexRequest ir2 = new IndexRequest(); String source2 = "{" + "\"user\":\"kimchy2\"," + "\"price\":\"7.3\"," + "\"tid\":\"20002\"," + "\"message\":\"Elasticsearch\"" + "}"; ir2.index(index).type(type).id("102").source(source2); IndexRequest ir3 = new IndexRequest(); String source3 = "{" + "\"user\":\"kimchy3\"," + "\"price\":\"8.3\"," + "\"tid\":\"20003\"," + "\"message\":\"Elasticsearch\"" + "}"; ir3.index(index).type(type).id("103").source(source3); BulkResponse response = client.prepareBulk().add(ir1).add(ir2).add(ir3).execute().actionGet(); BulkItemResponse[] responses = response.getItems(); if(responses != null && responses.length > 0) { for(BulkItemResponse r : responses) { String i = r.getIndex(); String t = r.getType(); System.out.println(i+","+t); } } } private static void sumCountSearch(String indexName, String typeName, String sumField, String countField, String searchField, Strin