Snapshot

Elasticsearch文档里对于snapshot有如下描述:

1
The index snapshot process is incremental. In the process of making the index snapshot Elasticsearch analyses the list of the index files that are already stored in the repository and copies only files that were created or changed since the last snapshot.

这里说snapshot是增量备份的,每次snapshot,Es会分析index文件,并且只备份增量部分。

  • snapshot是增量备份,对未发生变化的index重复备份几乎没有资源消耗;
  • 删除snapshot不会对其它snapshot产生影响;
  • snapshot是增量备份,对未发生变化的index重复备份几乎没有资源消耗
  • 删除某个snapshot不会对其它snapshot产生影响。

操作

建仓

ES是通过快照的方式来实现数据备份,并且是以增量的方式,所以一般第一次做的话会花费较长的时间。为了做快照,那么就需要注册一个快照仓库,告诉

ES我们的快照应该如何保存以及将快照保存到哪里.

在创建快照(snapshot)之前,首先需要创建快照仓库(repository),一个仓库里面可以有多个快照。而创建快照仓库需要在elasticsearch.yml配置仓库地址,配置之后,需要重启ES服务。

修改elasticsearch.yml

在elasticsearch.yml 中添加:

1
path.repo: ["/home/elasticsearch/snapshot"]  

也可以填多个仓库地址,后面添加快照时保存地址必须在下面配置地址之中

1
path.repo: ["/opt/elasticsearch-cluster/snapshot_repo", "/opt/elasticsearch-cluster/data_backups"]

验证是否添加 repo 目录成功

1
2
3
4
5
6
7
8
9
10
11
12
GET _cluster/settings?include_defaults&filter_path=*.path.repo

# 输出结果
{
"defaults" : {
"path" : {
"repo" : [
"/home/elasticsearch/snapshot"
]
}
}
}

创建一个名为es_bak_20210722的快照仓库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST _snapshot/es_bak_20210722
{
"type": "fs",
"settings": {
"location": "/home/elasticsearch/snapshot/dumpindex",
"max_restore_bytes_per_sec": "50mb",
"max_snapshot_bytes_per_sec": "50mb",
"compress": true
}
}

响应:
{
"acknowledged" : true
}

这样就创建了一个仓库地址,之后就可以在这个仓库之后添加快照。

快照仓库的其他操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
1.查看仓库:GET /_snapshot/_all
{
"es_bak_20210722" : {
"type" : "fs",
"settings" : {
"location" : "/Users/lihuan/Documents/opt/elasticsearch-cluster/es_repository",
"max_restore_bytes_per_sec" : "50mb",
"compress" : "true",
"max_snapshot_bytes_per_sec" : "50mb"
}
}
}

2.删除仓库:DELETE /_snapshot/es_bak_20210722

3.验证集群各个节点是否可以使用这个仓库:POST /_snapshot/es_bak_20210722/_verify
{
"nodes" : {
"0bCCNIOgT96vNrRVaTnXXQ" : {
"name" : "node-1"
}
}
}

4.查看某一个repository的具体setting:GET /_snapshot/{repo-name}

创建仓库还有其他的参数可以设置

1
2
3
4
5
6
location:仓库地址
compress:是否压缩,默认true,
chunk_size:是否将大文件分解成块,分解成块的单位,例子: 1GB, 10MB, 5KB, 500B,默认:null(不限制)
max_restore_bytes_per_sec:快照恢复的速度,默认:无限制
max_snapshot_bytes_per_sec:节点出入站的速率,默认:40mb/s
readonly:仓库快照是否时只读,默认:false

以上介绍的是基于本地存储,可以配置仓库插件就可以存储在其他地方,如:

1
2
3
4
repository-s3 支持S3 仓库
repository-hdfs 支持HDFS 仓库
repository-azure 支持Azure 仓库
repository-gcs 支持 Google Cloud 仓库

备份

仓库配置好了,就可以开始备份了。snapshot是增量的,在创建snapshot的时候,Elasticsearch会分析已经存在snapshot,只备份自上一次快照以来创建或更改的文件, 那些没有更改的文件会直接引用到上一次的snapshot中。因此如果频繁备份,那么snapshot的文件会越来越小。

1
2
1.snapshot是某一个时间点的集群状态,因此开始创建快照之后所有对ES的修改都不会出现在这次snapshot中
2.创建snapshot的过程中,集群所有的工作都不会被block。但是同一时间,只能有一份snapshot在创建

保存集群下的所有索引快照(保存快照方式1)

1
2
# 格式:/_snapshot/仓库名称/快照名称
PUT /_snapshot/my_backup/snapshot_1

这个指令返回关于snapshot的基本信息,包括开始和结束时间、创建快照的elasticsearch版本、包含的索引列表、快照的当前状态和快照期间发生的故障列表。

保存集群下的指定索引快照(保存快照方式2)

指定索引,添加wait_for_completion参数,等于true时,将等待快照保存结束时才会返回结果。反之异步返回结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 假设你现在所要添加的snapshot名称为:`snapshot_kibana_sample_data_flights`
PUT /_snapshot/es_bak_20210722/snapshot_kibana_sample_data_flights?wait_for_completion=true
{
"indices": "kibana_sample_data_flights"
}

响应:
{
"snapshot" : {
"snapshot" : "snapshot_kibana_sample_data_flights",
"uuid" : "GmOEM-JeQ0KSC25yMXhjhw",
"version_id" : 7020199,
"version" : "7.2.1",
"indices" : [
"kibana_sample_data_flights"
],
"include_global_state" : true,
"state" : "SUCCESS",
"start_time" : "2021-07-22T07:15:07.028Z",
"start_time_in_millis" : 1626938107028,
"end_time" : "2021-07-22T07:15:07.758Z",
"end_time_in_millis" : 1626938107758,
"duration_in_millis" : 730,
"failures" : [ ],
"shards" : {
"total" : 1,
"failed" : 0,
"successful" : 1
}
}
}

# 格式:/_snapshot/仓库名称/快照名称
PUT /_snapshot/my_backup/snapshot_2?wait_for_completion=true
{
"indices": ".security-7,topic,indexName"
}

快照是增量的,有可能很多快照依赖于过去的段,在仓库下的文件要注意了不要手动删除。

获取所有快照信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
GET /_snapshot/es_bak_20210722/_all

响应:
{
"snapshots" : [
{
"snapshot" : "snapshot_kibana_sample_data_flights",
"uuid" : "GmOEM-JeQ0KSC25yMXhjhw",
"version_id" : 7020199,
"version" : "7.2.1",
"indices" : [
"kibana_sample_data_flights"
],
"include_global_state" : true,
"state" : "SUCCESS",
"start_time" : "2021-07-22T07:15:07.028Z",
"start_time_in_millis" : 1626938107028,
"end_time" : "2021-07-22T07:15:07.758Z",
"end_time_in_millis" : 1626938107758,
"duration_in_millis" : 730,
"failures" : [ ],
"shards" : {
"total" : 1,
"failed" : 0,
"successful" : 1
}
}
]
}

获取指定快照信息

获取snapshot_kibana_sample_data_flights 快照信息

1
GET /_snapshot/es_bak_20210722/snapshot_kibana_sample_data_flights

删除快照

1
2
3
4
5
6
7
8
9
DELETE /_snapshot/es_bak_20210722/snapshot_kibana_sample_data_flights

使用以上指令删除snapshot的时候:
*会将repository中所有和这个快照相关的文件都删除
*会保留下来那些被其他还存在的快照使用的文件(因为快照是增量的)

1.删除多个快照:DELETE /_snapshot/my_backup/snapshot_2,snapshot_3,snapshot_4
2.通配符删除:DELETE /_snapshot/my_backup/snaps*
3.过期快照清理:POST /_snapshot/my_backup/_cleanup

获取指定快照状态

除了上边加的wait_for_completion 参数,可以知道快照是否完成之外,可以使用如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
GET /_snapshot/es_bak_20210722/snapshot_kibana_sample_data_flights/_status

响应:
{
"snapshots" : [
{
"snapshot" : "snapshot_kibana_sample_data_flights",
"repository" : "es_bak_20210722",
"uuid" : "GmOEM-JeQ0KSC25yMXhjhw",
"state" : "SUCCESS",
"include_global_state" : true,
"shards_stats" : {
"initializing" : 0,
"started" : 0,
"finalizing" : 0,
"done" : 1,
"failed" : 0,
"total" : 1
},
"stats" : {
"incremental" : {
"file_count" : 13,
"size_in_bytes" : 6739545
},
"total" : {
"file_count" : 13,
"size_in_bytes" : 6739545
},
"start_time_in_millis" : 1626938107185,
"time_in_millis" : 454
},
"indices" : {
"kibana_sample_data_flights" : {
"shards_stats" : {
"initializing" : 0,
"started" : 0,
"finalizing" : 0,
"done" : 1,
"failed" : 0,
"total" : 1
},
"stats" : {
"incremental" : {
"file_count" : 13,
"size_in_bytes" : 6739545
},
"total" : {
"file_count" : 13,
"size_in_bytes" : 6739545
},
"start_time_in_millis" : 1626938107185,
"time_in_millis" : 454
},
"shards" : {
"0" : {
"stage" : "DONE",
"stats" : {
"incremental" : {
"file_count" : 13,
"size_in_bytes" : 6739545
},
"total" : {
"file_count" : 13,
"size_in_bytes" : 6739545
},
"start_time_in_millis" : 1626938107185,
"time_in_millis" : 454
}
}
}
}
}
}
]
}

分片的状态有:

INITIALIZING:分片在检查集群状态看看自己是否可以被快照。这个一般是非常快的

STARTED:数据正在被传输到仓库

FINALIZING:数据传输完成;分片现在在发送快照元数据

DONE:快照完成

FAILED:快照处理的时候碰到了错误,这个分片/索引/快照不可能完成了。检查你的日志获取更多信息

创建日期表达式的快照

1
2
3
# 创建格式为:snapshot-2020-09-28 转为表达式:<snapshot-{now/d}>   转为URI: %3Csnapshot-%7Bnow%2Fd%7D%3E
# 创建以当前日期保存所有索引的快照
PUT /_snapshot/my_backup/%3Csnapshot-%7Bnow%2Fd%7D%3E

获取通配符快照信息

获取模糊快照信息,多个用逗号隔开

1
2
3
GET /_snapshot/my_backup/snap*
GET /_snapshot/my_backup/snap*,test*
GET /_snapshot/my_backup/snapshot_2,snapshot_1

恢复

_restore

一旦你备份过了数据,恢复它就简单了:只要在你希望恢复回集群的快照ID后面加上_restore即可:

snapshot_kibana_sample_data_flights为所要恢复快照的名称

1
POST /_snapshot/es_bak_20210722/snapshot_kibana_sample_data_flights/_restore

替换恢复索引名称

如上,就会恢复快照中的snapshot_kibana_sample_data_flights索引与数据,如果集群中已有快照的索引那就会报索引已存在的错误。如果索引再快照之前有别名,那么恢复快照时,会一同恢复别名。

如果你想在不替换现有数据的前提下,恢复老数据来验证内容,或者做其他处理。可以从快照里恢复单个索引并提供一个替换的名称:

1
2
3
4
5
6
7
8
9
10
11
12
POST /_snapshot/es_bak_20210722/snapshot_kibana_sample_data_flights/_restore
{
"indices": "kibana_sample_data_flights",
"ignore_unavailable": true,
"include_global_state": false,
"rename_pattern": "kibana_sample_data_flights",
"rename_replacement": "restored_kibana_sample_data_flights",
"include_aliases": false
}

备注:kibana_sample_data_flights在恢复之前是有一个别名flights的,那么在恢复快照时,会排除别名恢复。
恢复后索引名:restored_kibana_sample_data_flights

恢复完成后,当前集群与快照同名的索引、模板会被覆盖。在集群中存在,但快照中不存在的索引、索引别名、模板不会被删除。因此恢复并非同步成与快照一致。

1
2
3
4
5
partial: false可选,默认:false,如果快照包含一个或多个索引没有所有主碎片可用,则整个还原操作将失败。 如果为true,则允许恢复具有不可用碎片的索引的部分快照。将只恢复快照中成功包含的碎片。所有丢失的碎片将重新创建为空。
include_global_state: false 将还原快照中的所有数据流和索引,但不还原群集状态。
ignore_unavailable: false,false表示当缺少kibana_sample_data_flights索引时报错
include_aliases: 是否需要恢复别名,true恢复,false不恢复别名
rename_pattern与rename_replacement: 支持正则表达式也可以像上面直接写死。

使用正则表达式

1
2
3
4
5
6
7
8
9
POST /_snapshot/my_backup/snapshot_1/_restore
{
"indices": "test-01",
"ignore_unavailable": true,
"include_global_state": false,
"rename_pattern": "test-0(.+)",
"rename_replacement": "restored_test_$1",
"include_aliases": false
}

还原数据的时候,修改索引配置

1
2
3
4
5
6
7
8
9
10
11
POST /_snapshot/my_backup/snapshot_1/_restore
{
"indices": "index_1",
"ignore_unavailable": true,
"index_settings": {
"index.number_of_replicas": 0
},
"ignore_index_settings": [
"index.refresh_interval"1s
]
}

跨ES集群恢复

源集群执行备份操作之后,将在目标集群上将快照导入索引

  • 执行源集群备份操作,假设备份后的快照名称为:snapshot_1

  • 迁移源备份数据到目标集群所在机器的位置,比如也是/home/elasticsearch/snapshot

1
scp -r /home/elasticsearch/snapshot/dumpindex/*  目标集群用户@目标集群ip:/home/elasticsearch/snapshot/dumpindex/
  • 在目标集群上将快照导入索引

在目标集群上查看快照信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
GET /_snapshot/es_bak_20210722/snapshot_1

# 输出结果
{
"snapshots" : [
{
"snapshot" : "snapshot_1",
"uuid" : "cTvmz15pQzedDE-fHbzsCQ",
"version_id" : 7110199,
"version" : "7.11.1",
"indices" : [
"dumpindex"
],
"data_streams" : [ ],
"include_global_state" : false,
"state" : "SUCCESS",
"start_time" : "2021-03-17T14:33:20.866Z",
"start_time_in_millis" : 1615991600866,
"end_time" : "2021-03-17T14:33:21.067Z",
"end_time_in_millis" : 1615991601067,
"duration_in_millis" : 201,
"failures" : [ ],
"shards" : {
"total" : 1,
"failed" : 0,
"successful" : 1
}
}
]
}
  • 将快照导入目标集群的 dumpindex 索引中
1
2
3
4
POST /_snapshot/es_bak_20210722/snapshot_1/_restore
{
"indices": "dumpindex"
}

思考

Elasticsearch 跨集群数据迁移方案对比

参考:https://developer.aliyun.com/article/787479

方案elasticsearch-dumpreindexsnapshotlogstash
基本原理逻辑备份,类似mysqldump将数据一条一条导出后再执行导入reindex 是 Elasticsearch 提供的一个 API 接口,可以把数据从一个集群迁移到另外一个集群从源集群通过Snapshot API 创建数据快照,然后在目标集群中进行恢复从一个集群中读取数据然后写入到另一个集群
网络要求集群间互导需要网络互通,先导出文件再通过文件导入集群则不需要网络互通网络需要互通无网络互通要求网络需要互通
迁移速度一般
适合场景适用于数据量小的场景适用于数据量大,在线迁移数据的场景适用于数据量大,接受离线数据迁移的场景适用于数据量一般,近实时数据传输
配置复杂度中等简单复杂中等