021.Elasticsearch索引管理高级篇

1. 索引别名

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

  • 查询别名

    # 查询所有索引的别名
    GET _alias
    # 查询某个索引的别名
    GET /index_name/_alias
    
  • 新增别名

    # 方法一
    POST /_aliases
    {
      "actions": [
        {
          "add": {
            "index": "index_name",
            "alias": "index_name_v1.0"
          }
        }
      ]
    }
    
    # 方法二
    PUT /index_name/_alias/index_name_v1.0
    
  • 删除别名

    # 方法一
    POST /_aliases
    {
      "actions": [
        {
          "remove": {
            "index": "index_name",
            "alias": "index_name_v1.0"
          }
        }
      ]
    }
    
    # 方法二
    DELETE /index_name/_alias/index_name_v1.0
    
  • 重命名

    POST /_aliases
    {
      "actions": [
        {
          "remove": {
            "index": "index_name",
            "alias": "index_name_v1.0"
          }
        },
        {
          "add": {
            "index": "index_name",
            "alias": "index_name_v2.0"
          }
        }
      ]
    }
    
  • 多个索引指定一个别名

    POST /_aliases
    {
      "actions": [
        {
          "add": {
            "index": "index_name1",
            "alias": "alias_name"
          }
        },
        {
          "add": {
            "index": "index_name2",
            "alias": "alias_name"
          }
        }
      ]
    }
    
  • 一个索引指定多个别名

    POST /_aliases
    {
      "actions": [
        {
          "add": {
            "index": "index_name",
            "alias": "alias_name_1"
          }
        },
        {
          "add": {
            "index": "index_name",
            "alias": "alias_name_2"
          }
        }
      ]
    }
    
  • 通过别名查询索引,如果多个索引都是这个别名,那么就是查询多个索引

    GET /alias_name/_search
    {
      "query": {...}
    }
    
  • 通过别名写数据到索引

    PUT /alias_name/type_name[/id]
    {
      data...
    }
    
    # 如果多个索引都是这个别名,那么可以指定具体写入哪个索引
    # is_write_index设置了此索引是可以通过此别名被写入的索引
    POST /_aliases
    {
      "actions": [
        {
          "add": {
            "index": "index_name1",
            "alias": "alias_name",
            "is_write_index": true
          }
        },
        {
          "add": {
            "index": "index_name2",
            "alias": "alias_name"
          }
        }
      ]
    }
    

2. 索引重建

Elasticsearch的索引在创建好之后其数据结构和一些固有设置是不能修改的,如果一定要修改其数据结构和某些设置例如主分片的个数,那么就必须重建索引,ES提供了一些辅助工具来支持索引重建。

索引重建步骤:

  • index_old取一个别名index_service,index_service对外提供服务
  • 新创建一个索引index_new,数据结构与index_old一样,但是根据需求修改了某些字段或者修改了某些设置
  • 将index_old的数据同步到index_new
  • 给index_new取别名index_service,删除index_old的别名
  • 删除index_old

案例:

# 创建index_old
PUT index_old
{
  "mappings": {
    "_doc": {
      "properties": {
        "id": {"type": "long"},
        "name": {"type": "text"}
      }
    }
  }
}

POST /_bulk
{"index":{"_index":"index_old","_type":"_doc","_id":"1"}}
{"id": 1001, "name": "张三丰"}
{"index":{"_index":"index_old","_type":"_doc","_id":"2"}}
{"id": 1002, "name": "张无忌"}
{"index":{"_index":"index_old","_type":"_doc","_id":"3"}}
{"id": 1003, "name": "虚竹"}
{"index":{"_index":"index_old","_type":"_doc","_id":"4"}}
{"id": 1004, "name": "云中鹤"}
{"index":{"_index":"index_old","_type":"_doc","_id":"5"}}
{"id": 1005, "name": "杨过"}

# 此时我们要把name字段的类型由text修改为keyword,就必须重建索引
# 1.index_old取一个别名index_service,index_service对外提供服务
POST /_aliases
{
  "actions": [
    {
      "add": {
        "index": "index_old",
        "alias": "index_service"
      }
    }
  ]
}

# 2.新创建一个索引index_new,数据结构与index_old一样,但是name字段的类型为keyword
PUT index_new
{
  "mappings": {
    "_doc": {
      "properties": {
        "id": {"type": "long"},
        "name": {"type": "keyword"}
      }
    }
  }
}

# 3.将index_old的数据同步到index_new
# ES6.X需要指定旧索引的type和新索引的type
POST /_reindex?wait_for_completion=false
{
  "source": {
    "index": "index_old", 
    "type": "_doc"
  },
  "dest": {
    "index": "index_new", 
    "type": "_doc"
  }
}
# ES7.X不需要指定type
POST /_reindex?wait_for_completion=false
{
  "source": {
    "index": "index_old"
  },
  "dest": {
    "index": "index_new"
  }
}
# wait_for_completion=true,同步执行,会一直等待直到同步完成或者报错,默认
# wait_for_completion=false,异步执行,返回taskId,然后后台进行同步,数据量大的情况下建议使用

# 4.将index_old的别名删除,index_new的别名设置为index_service
POST /_aliases
{
  "actions": [
    {
      "remove": {
        "index": "index_old",
        "alias": "index_service"
      }
    },
    {
      "add": {
        "index": "index_new",
        "alias": "index_service"
      }
    }
  ]
}

# 5.删除index_old
DELETE /index_old

3. refresh、flush和merge

3.1 document写入原理一

  • 一个document先被写入一个内存buffer中
  • 当Buffer被写满或者到达一定时间后,执行commit操作,将buffer中的所有数据写入到segment中,一个index在物理上就是由多个segment文件组成的,但此时将buffer中的数据写到segment中,并非是写入磁盘,而是先写到页面缓存中,也就是OS Cache中
  • 如果是删除操作,那么在commit的时候,会往.del文件中将此document标记为"delete",搜索的时候,比如搜索到id=1的文档,发现在.del文件中已经被标记为"delete"了,就不会返回这条数据
  • 如果是更新操作,那么commit的时候,那么在.del文件中将旧数据标记为"delete"(document是有版本的),然后将新数据写入到新的segment中,查询的时候返回最新版本的document
  • 然后OS cache将segment刷写到磁盘上,形成segment文件,然后对这个segment文件执行一个"open"操作,这个这个segment文件就可以用于搜索了,同时刷写到磁盘之后,还会清空内存buffer

3.2 document写入原理二:refresh

上述流程是有问题的,从写入一条document到OS Cache将segment刷写到磁盘,并且该segment被打开允许搜索这个过程是有延迟的,可能达到min级别,这就不是近实时的搜索了,上述流程的主要瓶颈在于OS Cache将segment刷写到磁盘的过程,于是,我们想,只要数据被写入OS Cache中了,就可以被搜索,而不用刷写到磁盘后才能搜索,这样就可以提高搜索效率,索引刷新(refresh)功能就是让某条数据在写入OS Cache后就可以被搜索。

refresh默认时间间隔是1s,也就是说每隔一秒,将buffer中的数据写入到OS Cache中,并且允许该数据被搜索。

# 数据写入就强制刷新
PUT /index_name/type_name/id?refresh
{
  "data": "..."
}

# 设置index的刷新时间
PUT /index_name/_settings
{
  "index": {
    "refresh_interval": "5s"
  }
}

3.3 document写入原理三:translog和flush

在原理二的优化之后,ES就可以满足近实时的查询效率,但是还有一个问题是可靠性,因为document在真正的写入磁盘之前都是在内存中的,那么ES宕机就可能会丢失数据,Translog文件可以解决这个问题:

改进后得流程如下:

(1) document写入buffer缓冲,同时写入Translog日志文件

(2) 每隔1s,buffer中的数据被写入新的segment,并进入OS Cache,此时segment被打开并可以用于搜索

(3) buffer被清空

(4) 重复(1)-(3),新的segment不断添加,buffer不断被清空,而translog中的数据不断累加

(5) 当translog的大小达到一定程度,触发flush操作:

​ (5-1) buffer中的所有数据写入一个新的segment,并写入OS Cache,segment被打开并可以用于搜索

​ (5-2) buffer被清空

​ (5-3) 一个commit ponit文件被写入磁盘,其中标明了index当前所有的segment有哪些

​ (5-4) OS Cache中的所有segment缓存数据,被强行刷写到磁盘上

​ (5-5) 现有的translog被清空,创建一个新的translog

基于Translog如何进行数据恢复?

ES宕机之后,内存中的数据全部丢失,重启之后,上一次flush之后的全部数据都在Translog中保存着,将这些数据重新加载到内存buffer中并形成一个个segment然后写入OS Cache中即可。

相关设置参数如下:

index.translog.durability:新增、删除,更新或批量操作之后是否写入Translog并触发flush操作

  • request(默认):每次请求都写入Translog并触发flush操作
  • async:按照时间间隔来写入Translog并触发flush操作

index.translog.sync_interval:写数据请求提交到Translog并触发flush操作的时间间隔,默认5s,最小不能低于100ms

index.translog.flush_threshold_size:Translog触发flush操作的文件大小阈值,默认512mb

PUT /index_name/_settings
{
    "index.translog.durability": "async",
    "index.translog.sync_interval": "5s"
}

# 手动触发flush操作
POST /index_name/_flush

3.4 document写入原理四:segment merge

在上述写入流程中,每秒生成一个segment,segment数量太多,每次search都要搜索所有的segment,性能不好,ES默认会在后台执行segment merge操作,在merge的时候,被标记为deleted的document也会被彻底物理删除,每次merge操作的执行流程:

  • 选择一些有相似大小的segment,merge成一个大的segment

  • 将新的segment flush到磁盘上去

  • 写一个新的commit point,其中记录着新的segment的元数据,删除那些旧的segment的元数据

  • 将新的segment打开用于搜索

  • 将旧的segment删除

手动执行merge的API:

# max_num_segments指定了合并之后的segments个数
POST /index_name/_optimize?max_num_segments=1
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,271评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,725评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,252评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,634评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,549评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,985评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,471评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,128评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,257评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,233评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,235评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,940评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,528评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,623评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,858评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,245评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,790评论 2 339