[译]elasticsearch-不停服务修改mapping

一个常见的问题是:当数据模型需要修改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

你可能感兴趣的:([译]elasticsearch-不停服务修改mapping)