elasticsearch之Index Templates,Routing,Index Aliases

一、elasticsearch之Index Templates

前言

索引模板允许我们定义在创建新索引时自动应用的模板。模板包括设置和映射,以及一个简单的模式模板,该模板控制是否应该将模板应用于新索引。

为什么需要索引模板?

在开发中,elasticsearch很大一部分工作是用来处理日志信息的,比如某公司对于日志处理策略是以日期为名创建每天的日志索引。并且每天的索引映射类型和配置信息都是一样的,只是索引名称改变了。如果手动的创建每天的索引,将会是一件很麻烦的事情。为了解决类似问题,elasticsearch提供了预先定义的模板进行索引创建,这个模板称作为Index Templates。通过索引模板可以让类似的索引重用同一个模板。

模板只在创建索引时应用。更改模板不会对现有索引产生影响。当使用create index API时,作为create index调用的一部分定义的设置/映射将优先于模板中定义的任何匹配设置/映射。
环境:

  • win10
  • elasticsearch6.5.4
  • kibana6.5.4

本文所有的示例演示都在kibanaDev Tools中完成。

创建索引模板

我们针对前文中提出的问题,来实现一个索引模板:

Copy
PUT _template/2019
{
  "index_patterns": ["20*", "product1*"],   ①
  "settings":{   ②
    "number_of_shards": 2,
    "number_of_replicas": 1
  },
  "mappings":{  ③
    "doc":{
      "properties":{
        "ip":{
          "type":"keyword"
        },
        "method":{
          "type": "keyword"
        }
      }
    }
  }
}

①,index_patterns是索引模式,指当创建以20product1开头的索引时,使用该索引模板。注意,在elasticsearch的6.x版本中,索引模板模式index_patterns取代原来的template
②,在settings设置中,我们自定义为该索引分配3个主分片。复制分片不变。
③,mappings中指定映射关系。

查看索引模板

我们来查看一下刚才创建的索引模板。

Copy
GET _template/2019

我们还可以通过使用通配符来查询多个模板。

Copy
GET /_template/temp*
GET /_template/template_1,template_2

除此之外,我们可以查看所有可用的模板列表:

Copy
GET /_template

也可以查询某个模板是否存在:

Copy
HEAD _template/2019

注意,返回是以HTTP的状态表示模板是否存在,200表示存在,404表示不存在。

索引模板的使用

有了索引模板,我们就可以创建索引并且添加数据了。

Copy
PUT 20190101/doc/1
{
  "ip": "127.0.0.1",
  "method":"GET"
}

PUT 20190102/doc/2
{
  "ip":"192.168.1.1",
  "method":"POST"
}

PUT product1_log/doc/1
{
  "ip":"127.0.0.1",
  "method":"GET"
}

上例会按照模板自动生成3个索引2019010120190102product1_log
当然查询也是没有问题的:

Copy
GET 2019*/doc/_search
{
  "query": {
    "match_all": {}
  }
}

我们可以通过查询索引的信息来看一下是否应用上了模板。

Copy
GET 20190101
# 结果如下
{
  "20190101" : {
    "aliases" : { },
    "mappings" : {
      "doc" : {
        "properties" : {
          "ip" : {
            "type" : "keyword"
          },
          "method" : {
            "type" : "keyword"
          }
        }
      }
    },
    "settings" : {
      "index" : {
        "creation_date" : "1557889480975",
        "number_of_shards" : "2",
        "number_of_replicas" : "1",
        "uuid" : "FEuyT5aoTnGP3k_7hCtQFA",
        "version" : {
          "created" : "6050499"
        },
        "provided_name" : "20190101"
      }
    }
  }
}

由上例可以看出,索引20190101已经成功应用上了我们之前创建的模板。

多模板匹配

多个索引模板可以应用于同一个索引,顺序由order参数的大小决定。比如现在有着一样的一个索引模板:

Copy
PUT _template/2018_1
{
  "index_patterns": ["2018*"],
  "order":0,
  "settings":{
    "number_of_shards": 2
  }
}

上述索引模板将应用于以2018开头的所有索引,设置索引的主分片数量为2,order参数为0。

Copy
PUT 2018010101/doc/1
{
  "method":"GET"
}
GET 2018010101/_settings
# 结果如下
{
  "2018010101" : {
    "settings" : {
      "index" : {
        "creation_date" : "1557900456281",
        "number_of_shards" : "2",
        "number_of_replicas" : "1",
        "uuid" : "P53RDmT6RRCDY2DlHtpymg",
        "version" : {
          "created" : "6050499"
        },
        "provided_name" : "2018010101"
      }
    }
  }
}

可以看到20180101索引的主分片数量是2,已经成功应用了索引模板。
现在我们再来创建一个索引模板:

Copy
PUT _template/2018_2
{
  "index_patterns": ["201802*"],
  "order":1,
  "settings":{
    "number_of_shards": 3
  }
}

上述模板应用于以201802开头的索引,只是主分片的数量为3,order参数为1。
我们再来创建一个索引:

Copy
PUT 2018010201/doc/1
{
  "method": "POST"
}
GET 20180201/_settings
# 结果如下
{
  "20180201" : {
    "settings" : {
      "index" : {
        "creation_date" : "1557901225020",
        "number_of_shards" : "3",
        "number_of_replicas" : "1",
        "uuid" : "-B8_ZiK7QgesmGSzD_8xlQ",
        "version" : {
          "created" : "6050499"
        },
        "provided_name" : "20180201"
      }
    }
  }
}

可以看到20180201索引应用了order参数为1的索引模板。这在某些情况下非常有效,比如我们预料到2018年2月份的日志将会暴增,而其他月份不变,所以我们通过上述创建多个模板来完成索引创建,2月份每天的索引的主分片都多一片,过了2月,恢复正常。

删除索引模板

删除索引模板使用DELETE命令删除即可:

Copy
DELETE _template/2019

需要注意的是,删除索引模板并不会影响其通过该模板创建的索引。那么,如果想要删除相关的索引怎么办?比如我们删除以2019开头的索引该怎么办?

Copy
DELETE 2019*

可以使用通配符*来完成。


see also:[Elasticsearch 6 新特性与重要变更解读](https://blog.csdn.net/napoay/article/details/79135136) | [官网:Index Templates](https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-templates.html)

二、elasticsearch之Routing

前言

当索引一个文档的时候,文档会被存储到一个主分片中。那么,elasticsearch如何知道一个文档应该存放到哪个分片中呢?

首先这肯定不是随机的,否则在检索文档时就不知道该从哪去寻找它了。实际上这个过程是根据下面公式决定的:

Copy
shard = hash(routing) % number_of_primary_shards

routing是一个可变值,默认是文档的_id,也可以是自定义的值。hash函数将routing值哈希后生成一个数字,然后这个数字再除以number_of_primary_shards(主分片的数量)得到余数,这个分布在0number_of_primary_shards减一(计数从0开始,比如5个主分片,那么范围就是0~4)之间的余数,就是文档存放的分片位置。
比如一篇文档的id为123,那么它就应该存在:

Copy
>>> hash(123) % 5
3

这篇文档就存在P3主分片上。

这也就解释了为什么在创建索引时,主分片的数量一经定义就不能改变,因为如果数量变化了,那么之前所有的路由(routing)值都会无效,文档就再也找不到了。

一般的,elasticsearch的默认路由算法都会根据文档的id值作为依据将其哈希到相应的主分片上,该算法基本上会将所有的文档平均分布在所有的主分片上,而不会产生某个分片数据过大而导致集群不平衡的情况。
那么我们在向一个有100个主分片的索引发送查询某篇文档的请求时,该请求发送到集群,集群干了什么呢?

  • 这个请求会被集群交给主节点。
  • 主节点接收这个请求后,将这个查询请求广播到这个索引的每个分片上(包含主、复制分片)。
  • 每个分片执行这个搜索请求,并将结果返回。
  • 结果在主节点上合并、排序后返回给用户。

这里面就有些问题了。因为在存储文档时,通过hash算法将文档平均分布在各分片上,这就导致了elasticsearch也不确定文档的位置,所以它必须将这个请求广播到所有的分片上去执行。
为了避免不必要的查询,我们使用自定义的路由模式,这样可以使我们的查询更具目的性。比如之前的查询是这样的:

Copy
请求来了,你们(索引下的所有分片)都要检查一下自己是否有符合条件的文档

当能自定义路由后的查询变成了:

Copy
请求来了,分片3、5你俩把文档给我返回

自定义路由

所有的文档 API( getindexdeletebulkupdate 以及 mget )都接受一个叫做 routing 的路由参数 ,通过这个参数我们可以自定义文档到分片的映射。一个自定义的路由参数可以用来确保所有相关的文档——例如所有属于同一个用户的文档——都被存储到同一个分片中。

Copy
PUT r1/doc/1?routing=user1
{
  "title":"论母猪的产前保养"
}
PUT r1/doc/2?routing=user1
{
  "title":"论母猪的产后护理"
}

上例中,该文档使用user1作为路由值而不是使用_id。这样,具有相同user1的文档将会被分配同一个分片上。

通过路由查询文档

自定义路由可以减少搜索,不需要将搜索请求分发到所有的分片,只需要将请求发送到匹配特定的路由值分片既可。
我们来查询:

Copy
GET r1/doc/1?routing=user1
# 结果如下
{
  "_index" : "r1",
  "_type" : "doc",
  "_id" : "1",
  "_version" : 3,
  "_routing" : "user1",
  "found" : true,
  "_source" : {
    "title" : "论母猪的产前保养"
  }
}

也可通过这个路由值查询文档:

Copy
GET r1/doc/_search
{
  "query": {
    "terms": {
      "_routing":["user1"] 
    }
  }
}
# 结果如下
{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 2,
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "r1",
        "_type" : "doc",
        "_id" : "2",
        "_score" : 1.0,
        "_routing" : "user1",
        "_source" : {
          "title" : "论母猪的产后护理"
        }
      },
      {
        "_index" : "r1",
        "_type" : "doc",
        "_id" : "1",
        "_score" : 1.0,
        "_routing" : "user1",
        "_source" : {
          "title" : "论母猪的产前保养"
        }
      }
    ]
  }
}

删除文档

我们来删除文档。

Copy
DELETE r1/doc/1   
# 结果如下
{
  "_index" : "r1",
  "_type" : "doc",
  "_id" : "1",
  "_version" : 1,
  "result" : "not_found",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 2,
  "_primary_term" : 1
}

由上例可见,不提供路由,无法删除文档。

Copy
DELETE r1/doc/1?routing=user1
# 结果如下
{
  "_index" : "r1",
  "_type" : "doc",
  "_id" : "1",
  "_version" : 2,
  "result" : "deleted",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 4,
  "_primary_term" : 1
}

给上路由就OK了。
由此可见,在查询、删除、更新文档时都要提供相同的路由值。

查询多个路由

除了指定查询单个路由值之外,还可以指定多个路由值查询:

Copy
PUT r2/doc/1?routing=user1
{
  "title":"母猪产前保养重点在多喂饲料,辅以人工按摩"
}

PUT r2/doc/2?routing=user2
{
  "title":"母猪产后护理重点在母子隔离喂养"
}

此搜索请求将仅在与user1和user2路由值关联的分片上执行。

Copy
GET r2/doc/_search?routing=user1,user2
{
  "query": {
    "match": {
      "title": "母猪"
    }
  }
}
# 结果如下
{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 2,
    "successful" : 2,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 2,
    "max_score" : 0.68324494,
    "hits" : [
      {
        "_index" : "r2",
        "_type" : "doc",
        "_id" : "2",
        "_score" : 0.68324494,
        "_routing" : "user2",
        "_source" : {
          "title" : "母猪产后护理重点在母子隔离喂养"
        }
      },
      {
        "_index" : "r2",
        "_type" : "doc",
        "_id" : "1",
        "_score" : 0.5753642,
        "_routing" : "user1",
        "_source" : {
          "title" : "母猪产前保养重点在多喂饲料,辅以人工按摩"
        }
      }
    ]
  }
}

忘了路由值怎么办?

由之前的示例可以看到,在自定义的路由中,索引、查询、删除、更新文档时,都要提供路由值。但是我们有可能会忘记路由值,导致文档在多个分片建立索引:

Copy
PUT r3/doc/1?routing=u1
{
  "title":"小猪仔真可爱"
}

PUT r3/doc/2
{
  "title":"可爱可爱一盘菜"
}

正如上例所示,我们在创建文档2的时候,忘记路由了,导致这篇文档被默认分配到别的分片上了。那我们想通过u1路由查询就会发现:

Copy
GET r3/doc/_search
{
  "query": {
    "terms": {
      "_routing":["u1"]
    }
  }
}
# 结果如下
{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "r3",
        "_type" : "doc",
        "_id" : "1",
        "_score" : 1.0,
        "_routing" : "u1",
        "_source" : {
          "title" : "小猪仔真可爱"
        }
      }
    ]
  }
}

可以发现,那个文档2通过这个路由值查询不到,但是可以通过普通的查询:

Copy
GET r3/doc/_search

这样,两篇文档都会有被返回。
为了避免类似上述的情况出现,我们必须采取安全措施,加个套!在自定义映射关系时,使用_routing参数生成那个安全套

Copy
# 以下是6.5.4版本的写法
PUT r4
{
  "mappings": {
    "doc":{
      "_routing":{
        "required": true
      }
    }
  }
}
# 以下是7.0官方文档的的写法
PUT my_index2 
{ 
  “mappings”:{ 
    “_ usting”:{ 
      “required”:true
    } 
  } 
} 

_routing参数内,将required:true就表明在对文档做CURD时需要指定路由。不然就会抛出一个routing_missing_exception错误。就像下面的示例一样。

Copy
PUT r4/doc/1
{
  "title":"母猪不怀孕怎么办?"
}
# 结果是报错
{
  "error": {
    "root_cause": [
      {
        "type": "routing_missing_exception",
        "reason": "routing is required for [r4]/[doc]/[1]",
        "index_uuid": "_na_",
        "index": "r4"
      }
    ],
    "type": "routing_missing_exception",
    "reason": "routing is required for [r4]/[doc]/[1]",
    "index_uuid": "_na_",
    "index": "r4"
  },
  "status": 400
}

有了这种规范,我们在自定义路由时,就可以避免一些不必要的情况发生了。

自定义路由唯一ID

索引指定自定义_routing的文档时,不能保证索引中所有分片的_id唯一性。 事实上,如果使用不同的_routing值索引,具有相同_id的文档可能最终会出现在不同的分片上。
我们应确保ID在索引中是唯一的。

路由到索引分区

问题来了,在实际开发中,可能由于业务场景问题碰到某个路由的文档量非常大,造成该分片非常大,而某些路由的文档却非常小,这就会造成数据偏移而导致集群不平衡。我们该如何办呢?
我们可以配置索引,使得自定义路由值将转到分片的子集而不是单个分片。这有助于降低上述问题导致集群不平衡的风险,同时仍然可以减少搜索的影响。

这是通过在索引创建时提供索引级别设置index.routing_partition_size来完成的。随着分区大小的增加,数据分布越均匀,代价是每个请求必须搜索更多分片。

Copy
PUT r6
{
  "mappings": {
    "doc":{
      "_routing":{
        "required": true
      }
    }
  },
  "settings": {
    "index.routing_partition_size": 3 
  }
}

通俗的说,这是限制文档分布到指定个数分片上,而不是默认的所有分片上,既提高了请求效率,也减小单一分片数据量过大的问题。
当此设置存在时,计算分片的公式变为:

Copy
shard_num = (hash(_routing) + hash(_id) % routing_partition_size) % num_primary_shards

也就是说,_routing字段用于计算索引中的一组分片,然后_id用于选择该集合中的分片。

要启用此功能,index.routing_partition_size应具有大于1且小于index.number_of_shards的值。

启用后,分区索引将具有以下限制:

  • 无法在其中创建具有join field关系的映射。
  • 索引中的所有映射都必须将_routing字段标记为必需。

欢迎斧正,that's all see also:[路由一个文档到一个分片中](https://www.elastic.co/guide/cn/elasticsearch/guide/current/routing-value.html) | [官方5.6版本的_routing field](https://www.elastic.co/guide/en/elasticsearch/reference/5.6/mapping-routing-field.html) | [官方7.0的_routing field](https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-routing-field.html) | [路由文档到分片](https://www.cnblogs.com/bonelee/p/6055340.html) | [Elasticsearch分片、副本与路由(shard replica routing)](https://www.cnblogs.com/kangoroo/p/7622957.html) | [Elasticsearch路由机制介绍](https://blog.csdn.net/wwd0501/article/details/78109617)

三、elasticsearch之Index Aliases

别名解决了哪些问题?

在开发中,随着业务需求的迭代,较老的业务逻辑就要面临更新甚至是重构,而对于es来说,为了适应新的业务逻辑,可能就要对原有的索引做一些修改,比如对某些字段做调整,甚至是重建索引。而做这些操作的时候,可能会对业务造成影响,甚至是停机调整等问题。由此,es提供了索引别名来解决这些问题。
索引别名就像一个快捷方式或是软连接,可以指向一个或多个索引,也可以给任意一个需要索引名的API来使用。别名的应用为程序提供了极大地灵活性,别名允许我们做下面这些操作:

  • 在运行的集群中可以无缝的从一个索引切换到另一个索引。
  • 可以给多个索引分组。
  • 给索引的一个子集创建视图,没错我们可以简单将es中的索引理解为关系型数据库中的视图。
  • 可以与路由搭配使用。

当然,既然是别名,就不能与索引同名,并且别名仅支持查询(暂时可以这么说)操作。因为如果有多个索引指向同一个别名的话,es不知道你要对哪一个具体的索引做操作。

别名的相关操作

准备数据

首先,先准备两个索引:

Copy
PUT l1/doc/1
{
  "title":"我想要睡你"
}

PUT l2/doc/1
{
  "title":"你却拿我当兄弟"
}

PUT l3/doc/1
{
  "title":"不过,我不介意"
}

创建别名

我们来为一个索引起一个别名:

Copy
POST /_aliases
{
  "actions": [
    {
      "add": {
        "index": "l1",
        "alias": "a1"
      }
    }
  ]
}

上例,我们使用add为索引l1添加一个别名a1

查看别名

来查看一下刚才添加的别名:

Copy
GET l1/_alias
# result
{
  "l1" : {
    "aliases" : {
      "a1" : { }
    }
  }
}

删除别名

删除别名使用remove

Copy
POST /_aliases
{
  "actions": [
    {
      "remove": {
        "index": "l1",
        "alias": "a1"
      }
    }
  ]
}

这样就删除了指向索引l1的别名a1,再查l1的别名,你会发现aliases为空。注意,重复删除别名会报aliases_not_found_exception错误。

重命名别名

重命名别名是一个简单的remove操作,然后执行add操作,无需担心短时间内别名不指向索引,因为这个操作原子性的:

Copy
POST /_aliases
{
  "actions": [
    {"remove": {"index": "l1", "alias": "a1"}},
    {"add": {"index": "l2", "alias": "a1"}}
  ]
}

上例,首先索引l1又要有别名a1(别名都没有,删除等着报错呢)。这个原子性的操作,首先删除l1的别名a1,然后添加索引l2的别名a1。这个操作很有爱啊,因为如果要淘汰掉旧的索引l1并将所有数据迁移到了新索引l2上后,这时,我们如果一直使用a1别名处理请求,经过这个操作,在用户毫无感知的情况下就完成了索引的变更。

为多个索引指向同样的别名

为多个索引指向同样的别名只需要几个add操作就OK了:

Copy
POST /_aliases
{
  "actions": [
    {"add": {"index": "l1", "alias": "a1"}},
    {"add": {"index": "l2", "alias": "a1"}},
    {"add": {"index": "l3", "alias": "a1"}}
  ]
}

上例,我们将l1、l2、l3三个索引都指向别名a1

使用indices数组语法在一个操作中为多个索引指向同一个别名

可以使用indices数组语法在一个操作中为多个索引指向同一个别名,也是上面示例的另一种写法:

Copy
POST /_aliases
{
  "actions": [
    {"add": {"indices": ["l1", "l2", "l3"], "alias": "a2"}}
  ]
}

当然,这个套路同样适用于在一个操作中为一个索引指向多个别名

Copy
POST /_aliases
{
  "actions": [
    {"add": {"index": "l1", "aliases": ["a1", "a2", "a3"]}}
  ]
}

对于上面的示例,也可以使用glob pattern将别名关联到拥有公共名称的多个索引:

Copy
POST /_aliases
{
  "actions": [
    {"add": {"index": "l*", "alias": "f1"}}
  ]
}

上例指将所有以l开头的索引都指向别名f1。这个操作非常的有用,比如我们处理日志的逻辑是以日期为索引名,每天创建一个索引,那么我们如果想以月为单位分析日志,就可以采用这种方式,比如将2月份每一天的索引都指向一个别名,便于操作。

别名交换

在一个操作中使用别名交换:

Copy
POST /_aliases
{
  "actions": [
    {"add": {"index": "l1", "alias": "a1"}},
    {"remove_index":{"index":"a1"}}
  ]
}

上例的例子的意思是,开始,我们错误的添加了索引a1,而是l1才是我们想要添加的索引,所以这里我们可以先为索引l1创建一个别名a1,然后再把原来的索引a1干掉。remove_index就像删除索引一样(是的,索引a1就这样被干掉了)。

过滤器别名

带有过滤器的别名提供了创建相同索引的不同视图的简单方法,过滤器可以使用查询DSL定义,并应用与所有的搜索、计数、按查询删除以及诸如此类的操作。
要创建一个带过滤器的别名,首先要确保映射字段已存在与mapping中:

Copy
PUT l4
{
  "mappings": {
    "doc":{
      "properties":{
        "year":{
          "type":"integer"
        },
        "method":{
          "type":"keyword"
        }
      }
    }
  }
}

PUT l4/doc/1
{
  "year":2019,
  "method":"GET"
}
PUT l4/doc/2
{
  "year":2018,
  "method":"POST"
}
PUT l4/doc/3
{
  "year":2019,
  "method":"POST"
}

我们来看过滤器别名是如何创建的:

Copy
POST /_aliases
{
  "actions": [
    {
      "add": {
        "index": "l4",
        "alias": "a4",
        "filter": {"term": {
          "year": 2019
        }}
      }
    }
  ]
}

上例,我们为索引l4year字段建立了别名a4。我们来看一下它与普通的别名有什么区别:

Copy
{
  "l4" : {
    "aliases" : {
      "a4" : {
        "filter" : {
          "term" : {
            "year" : 2019
          }
        }
      }
    }
  }
}

如果你够细心的话,你会发现这个带过滤器的别名相较于之前的别名多了过滤条件,普通的别名aliases的字典是空的。
那么有啥用呢?我们如果以索引l4查询,一切没啥变化:

Copy
GET l4/doc/_search

上例查询返回该索引内的所有文档。
而如果以别名a4查询则仅返回过滤器过滤后的结果:

Copy
GET a4/doc/_search

上例仅返回year为2019的文档。

与路由连用

除了单独使用别名,还可以将别名与路由值关联,以避免不必要的分片操作。

Copy
POST /_aliases
{
  "actions": [
    {
      "add": {
        "index": "l1",
        "alias": "a1",
        "routing": "1"
      }
    }
  ]
}

上例为索引l1创建了一个别名a1,在创建完a1后,所有与此别名的操作都将自动修改使用routing值进行路由。
除此之外,还可以为搜索和索引操作指定不同的路由值:

Copy
POST /_aliases
{
  "actions": [
    {
      "add": {
        "index": "l4",
        "alias": "a4",
        "search_routing": "1,2",
        "index_routing": "1"
      }
    }
  ]
}

如上例所示,搜索路由(search_routing)可能包含多个(以英文逗号分隔)值,但索引路由(index_routing)就只能包含一个值。
如果使用路由别名的搜索也有路由参数,则使用在该参数中指定的搜索别名路由和路由的交集:

Copy
GET /a4/_search?q=year:2019&routing=2,3

上例中,搜索操作中有路由参数2、3,而搜索路由设置的是1、2,所以取交集2

写索引

在之前的介绍中,我们说过,我们仅能使用别名进行查询操作,不能做写入操作,因为如果一个别名关联多个索引的话,es不知道你要将文档写入到哪个索引中,而现在,我们就来解决这个问题,我们要告诉es,要将文档写入到哪个索引中。

Copy
POST /_aliases
{
  "actions": [
    {
      "add": {
        "index": "l1",
        "alias": "a1",
        "is_write_index": true
      }
    },
    {
      "add": {
        "index": "l2",
        "alias": "a1"
      }
    }
  ]
}

注意,上述的l1l2索引必须存在。
在上例中,is_write_index:true表示当通过别名a1做写入操作的时候,将文档写入到索引l1中。

Copy
PUT a1/doc/2
{
  "title": "hi gay"
}

上例中,PUT a1/doc/2相当于向l1/doc/2写入。

Copy
GET l1/doc/_search
GET l1/doc/2

那么如果有些文档的写入要从l1切换到l2该怎么办呢?要交换哪个索引是别名的写入操作,可以利用别名API进行原子交换,交换不依赖于操作的顺序。

Copy
POST /_aliases
{
  "actions": [
    {
      "add": {
        "index": "l1",
        "alias": "a1",
        "is_write_index": false
      }
    },
    {
      "add": {
        "index": "l2",
        "alias": "a1",
        "is_write_index": true
      }
    }
  ]
}

上例中,我们首先取消l1的写索引权限,然后将该权限交给l2。这样,再写入的话,就是向l2索引了。

Copy
PUT a1/doc/3
{
  "title": "hi man"
}
GET l2/doc/3

注意,如果一个别名仅有一个索引指向它时,那么该索引是有写权限的:

Copy
PUT l5/doc/1
{
  "title":"肉肉的猪仔真可爱"
}

POST /_aliases
{
  "actions": [
    {
      "add": {
        "index": "l5",
        "alias": "a5"
      }
    }
  ]
}
PUT a5/doc/2
{
  "title":"可爱可爱真可爱"
}

GET l5/doc/_search

如上例所示,此时的别名a5仅有索引l5指向它。所以可以通过该别名做写入操作。
但是,当又有新的索引l4指向该别名时,如果没有显示的设置is_write_index:true,此时就不能通过该别名做写索引操作了。

Copy
POST /_aliases
{
  "actions": [
    {
      "add": {
        "index": "l4",
        "alias": "a5"
      }
    }
  ]
}


PUT a5/doc/3     
{
  "title":"可爱可爱真可爱"
}

上例中,当PUT a5/doc/3时会报错illegal_argument_exception,因为此时没有为哪个索引指定is_write_index:true,es就不知道往哪个索引做写索引操作了。

添加单个别名

除了之前添加别名的方法,我们还可以使用端点添加别名:

Copy
PUT /{index}/_alias/{name}
PUT /{index}/_alias/{name}?routing=user1
  • index,要为哪个索引添加别名。
  • name,别名的名称。
  • routing,可以与别名关联的路由。

来个示例,基于时间的别名:

Copy
PUT /logs_201905/_alias/2019

再来一个示例,为用户添加别名:

Copy
PUT user
{
  "mappings": {
    "doc":{
      "properties":{
        "user_id":{
          "type":"integer"
        }
      }
    }
  }
}
PUT /user/_alias/user_1
{
  "routing": "1",
  "filter": {
    "term": {
      "user_id": "1"
    }
  }
}

PUT /user/doc/1?routing=1
{
  "user_id": "1"
}
GET /user/doc/_search
GET /user/doc/1

索引期间指定别名

我们除了单独的为一个或多个索引指定别名,也可以在索引创建期间指定别名:

Copy
PUT l6
{
  "mappings": {
    "doc":{
      "properties":{
        "year":{
          "type":"integer"
        }
      }
    }
  },
  "aliases": {
    "current_day": {},
    "2019":{
      "filter": {
        "term": {
          "year": 2019
        }
      }
    }
  }
}
PUT l6/doc/1
{
  "year": 2018
}
PUT l6/doc/2
{
  "year": 2019
}
GET 2019/_search

删除别名

通过使用DELETE删除别名:

Copy
DELETE /l1/_alias/a1
DELETE /l2/_aliases/a*

检索现有别名

使用GET索引别名API检索别名:

Copy
GET /{index}/_alias/{alias}
  • index,获取别名的索引名称。通过通配符支持部分名称,也可以使用逗号分隔多个索引名称。还可以使用索引的别名。
  • alias,要在响应中返回的别名的名称。与index选项一样,此选项支持通配符,并且选项指定由逗号分隔的多个别名。
  • ignore_unavailable,如果指定的索引名称不存在该怎么办。如果设置为true则那些索引将被忽略。

来一些示例:
查询索引指定别名。

Copy
GET l1/_alias/a*    # 查询索引l1指向以a开头的所有别名
GET l1/_alias/*		# 查询索引l1所有的别名

查询所有别名是a12019的索引:

Copy
GET /_alias/a1
GET /_alias/2019

查询所有别名是以20开头的索引:

Copy
GET /_alias/20*

HEAD

通过HEAD检测某个别名是否存在:

Copy
HEAD /_alias/2019
HEAD /_alias/20*
HEAD /l2/_alias/*

上例中,HEAD以状态码的形式返回是别名是否存在,200 - OK是存在,404 - Not Found是不存在。


欢迎斧正,that's all see also:[Elasticsearch 技术分析(三): 索引别名Aliases问题](https://www.cnblogs.com/jajian/p/10152681.html) | [官网:Index Aliases](https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-aliases.html)
原文地址:https://www.cnblogs.com/bubu99/p/13593867.html