之前进行了Es集群的搭建,这次总结下Es的IndexApi,这里主要会和Solr进行部分对比来描述,如果对Solr不熟悉的可以自行略过。
RESTFUl API####
Es采用REST FULL的Api,这让Es具有很好的可读性,调用一目了然,当然RESTFuL的API不建议用到生产环境,原因是这种API接口基本都是短链接,这样消耗比较大,最好使用的时候,用长连接的接口进行包装,这样每次查询都是基于长连接的请求,能很好的减小服务端的开销。
ES的索引和Solr索引的一点疑惑####
一个索引请求:
curl -XPUT 'http://localhost:9200/twitter/tweet/1' -d '{ "user" : "kimchy", "post_date" : "2009-11-15T14:12:12", "message" : "trying out Elasticsearch"}'
这是一个ES建索引的Http接口的请求。首先,我们能很好的理解这种RESTFUL的接口,PUT代表将数据put到ES上,后面的twitter是指索引的名字,和Solr中的Collection类似。tweet是一种数据结构的描述,一个索引可能有多个type,在一个索引中所有的type的并集为这个索引的Schema。这一点是Solr中所不具有的,因为Solr是采用严格的Schema格式去限制索引的,而Es中则比较松散。
上面请求返回:
{"_index":"twitter","_type":"tweet","_id":"1","_version":1,"created":true}
_index为索引名
_type为索引中type名称
_id为索引主键id,id是在最后指定的。如果不指定ID,ES会抛出错误,如果希望自动生成,那么需要将PUT替换为POST。如果在二级索引这种场景下,id可以和落地存储(Mongo、Mysql)中数据Id一致。
_version:为版本号,版本号标识数据的版本,因为Lucene的数据merge是在Segment合并的时候进行的,所以需要version保证不会检索到过期的数据。另一方面Version可以用来避免数据并发问题,对于版本号大于之前的数据可以增加,对于小于之前数据的version会抛出异常。
created为索引标识,标识是否成功。
此时我们看一下tweet的Mapping:
curl -XGET 'http://localhost:9200/twitter/_mapping/tweet?pretty=true'
{
"twitter" : {
"mappings" : {
"tweet" : {
"properties" : {
"message" : {
"type" : "string"
},
"post_date" : {
"type" : "date",
"format" : "dateOptionalTime"
},
"user" : {
"type" : "string"
}
}
}
}
}
}
此时的Mapping中我们能看到,有type,post_date和user,当然这不是绝对的,因为mapping在后面改变后会发生改变,我们重新索引下另一条doc:
curl -XPUT 'http://localhost:9200/twitter/tweet/1' -d '{ "user" : "kimchy", "post_date" : "2009-11-15T14:12:12", "message" : "trying out Elasticsearch","sex":"m"}'
我们再看下tweet的mapping:
curl -XGET 'http://localhost:9200/twitter/_mapping/tweet?pretty=true'
{
"twitter" : {
"mappings" : {
"tweet" : {
"properties" : {
"message" : {
"type" : "string"
},
"post_date" : {
"type" : "date",
"format" : "dateOptionalTime"
},
"user" : {
"type" : "string"
}
}
}
}
}
}
可以看见神奇的增加了一个sex列,惊讶~~~
这一点与Solr是完全不同的,Solr是基于配置的,Solr创建好Schema之后便不能再扩充,但是ES能够灵活的改变Schema,我个人认为这是优势也是劣势,优势在于可以灵活的变更,劣势是没有约束性,可以无限的进行改变,不容易维护。
其实看到这里,熟悉Solr的人总会有个疑问,Solr和ES都是基于Lucene的。但是ES是如何抽象出type这个概念的呢?
我们在同一个索引下再创建一个Type:
curl -XPUT 'http://localhost:9200/twitter/tweet1/1' -d '{ "user" : "kimchy", "post_date" : "2009-11-15T14:12:12", "mobile":"13573333333"}'
这里我们创建了tweet1,同时改变了tweet1的索引结构,接下来我们看看存储结构:
Es的存储路径默认在es文件下的data目录,我们查看:data/elasticsearch/nodes/0/indices
发现一个twitter文件夹,那么这就证明不同的type是在一个索引下的,接下来我们任意选一个Shard查看:
我们发现这和Lucene的索引结构没什么区别,那么Type应该是在索引内部抽抽象出来的。
我们接下来用Head看一下,索引结构到底是什么样的。
除了Head之外,也可以用luke,但是Luke更新很慢,需要自己编译。
看到这个视图,就一目了然了,整个索引在内部抽象出了一个type索引字段,整个index的Schema是左右的type的所有字段的并集。Solr的索引实际上相当于Es的index+一个type。
但是我感觉这种机制不能应用在生产环境上,用type划分索引会导致索引结构庞大会影响检索效率,最好能一个index只含有一个type最好。
这种自动创建索引Schema的机制,可以通过下面方式关闭:
Automatic index creation can be disabled by setting action.auto_create_index to falsein the config file of all nodes. Automatic mapping creation can be disabled by setting index.mapper.dynamic tofalse in the config files of all nodes (or on the specific index settings).
JAVA Index API####
ES对于Java提供了两种Client的接入:
- Node Client
- Transport Client
其中NodeClient思想是启动一个Node加入到现有的ES集群中,但是此Node不存储数据,不接收HTTP的请求,这样的好处是能够及时的发现集群中节点的状态变化,同时避免请求的转发(因为Client就是一个Node)。但是坏处是要求节点必须能加入到集群,所以必须在同一个局域网内。另一点是,Client Node的停起会对集群有干扰。、
而Transport Client的方式则是作为一个独立的Client对ES的集群进行请求,类似于SolrJ。但是这种模式不能很好的确定集群中的节点当前是否可用(不是节点宕机,有可能是在recover)。同时还可能引发二次跳转(目标是请求Node2,但是请求发到Node1上,需要从Node1跳转到Node2上)。
因为NodeClient在生产中不是很常用,所以学习下Transport Client:
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* 对ElasticSearch建立索引
*/
public class IndexDocumentClient {
private Client client;
private String indexName;
private String indexType;
public IndexDocumentClient(String host, int port){
//client.transport.sniff配置目的是自动嗅探活跃的节点
Settings setting = ImmutableSettings.settingsBuilder().put("client.transport.sniff",true).build();
client = new TransportClient(setting).addTransportAddress(new InetSocketTransportAddress(host,port));
}
public void index(String indexName,String indexType){
Map jsonMap = new HashMap();
jsonMap.put("user","kimchy1");
jsonMap.put("post_date",new Date());
jsonMap.put("message","trying out Elasticsearch");
jsonMap.put("mobile","13888888888");
IndexResponse response = client.prepareIndex(indexName,indexType,"3").setSource(jsonMap).execute().actionGet();
System.out.print(response.getId());
}
public static void main(String args[]){
IndexDocumentClient client = new IndexDocumentClient("10.0.2.161",9300);
client.index("twitter","tweet");
}
}
这里的Source是一个重载方法,既可以使用Map还可以使用String(Json)。利用jsonBuilder也是一个比较好的方法。这样就可以用Client进行索引了,但是Client里面的机制还不清楚,但是这种Client和Server的实现总是很吸引人的,接下来再慢慢学习!