一个常见的问题是:当数据模型需要修改schema的时候,需要重建索引。如果索引很大,这个需要较长时间,因此要暂停服务,这对应用程序来说是无法接受的。有没有不停服务直接修改mappoing的方法呢?可以有。
1:问题--为什么不能修改mapping?
你只能找到你在索引中存储的信息。为了使数据可查询,就需要知道每一个field包含的数据的数据类型以及它是如何索引的。如果你将一个field的数据类型从string修改为date,这这个字段所包含的数据将全部无用。你需要重建创建索引了!这条规则不仅仅针对es,任何一个可用于查询的数据库系统都是这样。如果不用索引,就是为灵活性牺牲速度。
ES索引是不可改变的segments,每一个segmeng都是一个小的倒排索引。segment是永远不会更新的。更新一个document只是创建了一个新的document,并且在原来的document上加了删除标记。当添加新的document或者更新document的时候,新的segment就会创建。后台运行merge进程,会不停的把一些小的segment合并成一个大的segment,同时会把打有删除标记的document彻底清理。通常情况下,一个index拥有很多type,每一个type都有不同的shema和mapping。一个segment可能包括多个type的document。所有,如果想修改一个type的一个field的定义,都需要重建所有索引。
2:添加字段是免费的
segment中只是包含了这个segment中存在的document中的包含的field的数据信息。因此增加一个新的field是完全免费的,只需要put_mapping即可,无需重建索引。
3:重建索引
重建索引步骤简单,首先,创建新索引的mapping和setting:
curl -XPUT localhost:9200/new_index -d ' { "mappings": { "my_type": { ... new mapping definition ...} } } '
之后,从旧索引中把数据读取出来放到新创建的索引中,用scrolled search+buck api。许多客户端api提供了reindex方法,将会替你做这些事情。索引做完就可以删除旧索引了。
注意:确保你使用了search_type=scan的方式读取数据。这样不会启用sort,并且使得“deep paging”更加高效。
这个方法的问题是:新索引的名字改变了,你需要更新你的应用程序到新的索引名字。
4:不停服务重建索引
alias机制给我们提供了灵活性,使得重建索引完全在后台执行,而且使得索引切换对应用程序来说是透明的。一个alias相当于一个软链接,可以指向一个或者多个索引。
典型的工作流程是这样的:首先,创建索引,索引名称后边可以添加一个version或者时间戳。
curl -XPUT localhost:9200/my_index_v1 -d ' { ... mappings ... } '
创建alias指向这个索引:
curl -XPOST localhost:9200/_aliases -d ' { "actions": [ { "add": { "alias": "my_index", "index": "my_index_v1" }} ] } '
现在应用程序对my_index进行操作,就相当于对my_index_v1进行操作。
需要重建索引时,可以新建一个索引,添加一个新的version
curl -XPUT localhost:9200/my_index_v2 -d ' { ... mappings ... } '
然后利用v1的数据来填充v2,将alias指向v2即可。
curl -XPOST localhost:9200/_aliases -d ' { "actions": [ { "remove": { "alias": "my_index", "index": "my_index_v1" }}, { "add": { "alias": "my_index", "index": "my_index_v2" }} ] } '
最后,删除v1
curl -XDELETE localhost:9200/my_index_v1
现在在后台完成了重建索引的操作,而无需停止服务,对应用程序来说这个过程是完全透明的。
以上是处理schema变化的标准步骤,还有一些其他的选项可用。
5:我并不关心老数据
如果你想修改一个field的数据类型,而且你并不关系之前的数据是否可以被搜索到。这种情况下,有以下选项
delete the mapping:
删除一个type的mapping,意味着你也删除了这个type下的所有数据。然后重新创建一个type的mapping。
这对一个含有较少数据的type去修改mapping比较有用。
rename the field:
添加新字段是free的,所以可以添加一个不同名称,不同定义的新field在将来出现的document中使用。当然,这也就意味着应用程序需要更新来使用新字段。
upgrade to a multi-field:
MultiField允许同一个字段用在不同的场景下。一点典型应用就是对同一个字段title以两种不同的方式索引:一个analyzed的string作为查询时使用,一个not_analyzed字段作为排序使用。任何一个纯字段(object和nested不属于这一类)可以升级为一个multi-field,而无需重建索引。比如,现在我们有一个string类型的字段:
{ "created":{"type":"string"}}
我们可以升级为一个multifield,添加一个sub-field是date类型;
curl -XPUT localhost:9200/my_index/my_type/_mapping -d ' { "my_type": { "properties": { "created": { "type": "multi_field", "fields": { "created": { "type": "string" }, "date": { "type": "date" } } } } } } '
前边的created的字段仍然存在,作为multi-field的主字段,可以用created或者created.created访问,而新的date类型的字段,可以用created.date来访问,注意,这个字段只对新添加的document有效。
6:运用alias来增加灵活性
有时上述方法仍然不足以满足需求。或许你的应用有100,000个user文档,10,000,000个blog文档(在同一个索引中,但是分为不同的type存储)。你想改变user的mapping而无需重新索引blog信息。
在不同的索引中可以存储不同的types。Es可以在不同索引中查询就像是在一个索引中一样。这样,你只需要重新索引你想改变的type所在的索引即可。有了alias,这个重新索引的过程对应用程序也是透明的。
运用以上方法,es为每一个type创建一个单独的alias。比如:不是统一对my_index创建索引,而是user数据对my_index_user创建索引,blog数据对my_index_blog创建索引。
curl -XPOST localhost:9200/_aliases -d ' { "actions": [ { "add": { "alias": "my_index_user", "index": "my_index_v2" }}, { "add": { "alias": "my_index_blog", "index": "my_index_v2" }} ] } '
查询这样做:
curl localhost:9200/my_index_blog,my_index_user/_search
如果现在想改变user的mapping怎么做?首先用新的mapping创建一个新的索引,这个索引只存储user数据:
curl -XPUT localhost:9200/my_index_users_v1 -d ' { "settings": { "index": { "number_of_shards": 1 } }, "mappings": { "user": { ... new user mapping ... } } } '
然后将旧数据索引过来:
curl 'localhost:9200/my_index_user/user?scroll=1m&search_type=scan'-d ' { "size": 1000 } '
更新alias:
curl -XPOST localhost:9200/_aliases -d ' { "actions": [ { "remove": { "alias": "my_index_user", "index": "my_index_v2" }}, { "add": { "alias": "my_index_user", "index": "my_index_user_v1" }} ] } '
现在可以用delete-by-query把旧索引中的数据删除:
curl -XDELETE localhost:9200/my_index_v1/user
这样将来任何时候想改变user的mapping,就可以用之前的方法了。
(其实本质就是把type数据用alias搞出来,作为单一存在的索引而已,没什么高端的)
7:运用alias不重建索引
如果想针对改变的mapping只需要应用在新进入的docement中,仍然可以用alias实现。创建两个alias,一个针对search,一个针对新数据的index
curl -XPOST localhost:9200/_aliases -d ' { "actions": [ { "add": { "alias": "my_index_user", "index": "my_index_user_v1" }}, { "add": { "alias": "my_index_users", "index": "my_index_user_v1" }}, { "add": { "alias": "my_index_users", "index": "my_index_v1" }}, ] } '
my_index_user指向新索引my_index_user_v1,用来索引数据。而my_index_users则包含旧索引my_index_v1和新索引my_index_users_v1,用来查询数据。这样旧索引让然用之前的mapping,新的mapping之会应用在新建立的数据中。
-------
总结一句:alias不神秘,现在只能支持索引之间的切换,上边所用的type的例子也只是把type搞到新的索引中而已。
1.4.x版本会支持带有filter的alias,这个将更有效,是否能有typefiler?这样就解放了type了。期待稳定版本。
http://m.blog.csdn.net/blog/jingkyks/41513063
http://blog.csdn.net/lvhong84/article/details/23936697