Elasticsearch & AWS S3 備份

我們的系統(tǒng)中大部分都是時序數(shù)據(jù),一些數(shù)據(jù)被清洗后,過期的數(shù)據(jù)意義已經(jīng)不大,但是保不齊哪天需要重新清洗或者查閱歷史,所以準(zhǔn)備將 Indices 的內(nèi)容備份在 AWS S3 當(dāng)中。

雖然我們采用 Elastic Cloud 的服務(wù),他每 30 分鐘會自動全盤用 Elastic 自己的 S3 系統(tǒng)備份一次,但是他只保留最近 48 小時左右的數(shù)據(jù),并且他做的是整個 ES 系統(tǒng)的全量備份。這個備份的目標(biāo)更像一個災(zāi)備的狀態(tài),而不是我們需要的歷史數(shù)據(jù)備份。所以我們準(zhǔn)備從 Elastic Cloud 每天備份過期數(shù)據(jù)到自己的 AWS S3 當(dāng)中。

基本概念

使用 Elasticsearch Snapshot 時需要有一些基本概念澄清,他不是拿指定的 Indices 文件做個壓縮包丟在 S3 完事,他是有控制的。

snapshot 結(jié)構(gòu)

Elasticsearch 的 snapshot 是由其自身控制的,整個系統(tǒng)保持了一個如下的從下到上的控制結(jié)構(gòu),他們具備包含關(guān)系:

snapshot --> repository --> single snapshot --> indices

  • snapshot
    這里將 snapshot 單獨列出,是因為在 Elasticsearch 中 snapshot 獨占性工作的,他更像是一個管道,任何一個 repository 在工作的時候是排他的,雖然他并不阻止 indices 的寫入。
  • repository
    這個倉庫應(yīng)該是一組 snapshot 備份的集合,也可以認(rèn)為是一個目標(biāo)的選擇。在一個 Elasticsearch 系統(tǒng)中你可以根據(jù)自己的意愿設(shè)定不同的 Repository,在我們使用的 Elastic Cloud 服務(wù)中,官方會自動提供一個他們 30 分鐘自動全量備份的 Repository。
GET _snapshot/_all
{
  "found-snapshots": {
    "type": "s3",
    "settings": {
      "bucket": "PRIVATE-UUID",
      "base_path": "snapshots/PRIVATE-UUID",
      "server_side_encryption": "true",
      "region": "us-west-2",
      "compress": "true"
    }
  }
}
  • Single snapshot
    這個指的是在 Repository 中我們進(jìn)行的每個備份內(nèi)容,他更像一個集合,包含了這次 snapshot 中所有的 Indices。
  • Indices
    在一個 snapshot 當(dāng)中,可以包含多個 Indices 文件內(nèi)容。他可以在執(zhí)行 snapshot 的時候用 pattern 識別,也可以一個一個的指定。

S3 插件

如果要想讓 Elasticsearch 備份到 S3 當(dāng)中需要單獨安裝一個插件 S3 Repository Plugin,在我們使用的 Elastic Cloud 當(dāng)中這個插件已經(jīng)默認(rèn)安裝了。我們并不知道安裝中是否會遇到什么阻礙。

Snapshot 配置

在 Elasticsearch 中進(jìn)行 snapshot 操作官方文檔已經(jīng)說得非常清楚了,整體結(jié)構(gòu)和控制并不復(fù)雜。

Repository

在正常執(zhí)行 _snapshot 以前,需要先建立好自己的 Repository。具體操作可以參考 S3 Repository Plugin 完成 S3 的配置操作。

其中 AWS 的賬戶口令控制不必非要寫在系統(tǒng)的 YAML 配置文件中,直接在創(chuàng)建 Repository 的設(shè)定用起來會更加靈活。獲取 Repository 的時候,系統(tǒng)會自動屏蔽賬戶信息部分。

AWS IAM

這個稍微復(fù)雜一點,也可能是我們對 AWS IAM 并不熟悉。按照 Elastic 官方給出的 Recommanded S3 Permissions 直接配置即可。

這里他需要獲取到 AWS S3 Bucket 的列表權(quán)限,因為他會放置自己的一些控制文件進(jìn)入,并且還需要進(jìn)行比對操作。

如果你需要備份不同的 ES 系統(tǒng)到一個 AWS S3 Bucket 一定要分配到不同的目錄當(dāng)中,因為 Elasticsearch 的那些控制文件會導(dǎo)致他們之間沖突。這里可以參考這個 S3 Permissions 的說明中后面一個 IAM 配置說明即可。

snapshot 操作

當(dāng) Repository 做好后,就可以直接執(zhí)行 _snapshot 操作了。具體的操作方法可以參考 Snapshot and Restore 部分的說明了。指令比 _search 不知道簡單多少倍 ??

如果你執(zhí)行的時候,其他的 snapshot 正在執(zhí)行,你會得到一個 503: Service Unavailable 的錯誤信息。這就是上面說的 snapshot 執(zhí)行的獨占性,哪怕不同的 repository 之間也不能并行。

S3 中文件的作用

Elasticsearch 在 S3 中創(chuàng)建 snapshot 的時候,會形成一些輔助文件,幫他管理 snapshot 內(nèi)容。

最坑爹是 Elasticsearch 并不在自己的 Indices 當(dāng)中創(chuàng)建備份信息,而是將所有這些信息都放在了 S3 當(dāng)中。當(dāng)然熟悉后也明白,這么做的好處是,可以讓另一個 ES 系統(tǒng)通過同樣的 Repository 配置讀取這些 S3 中的內(nèi)容。

文件目錄結(jié)構(gòu)

下面是我們專門保存 Elasticsearch Snapshot 的 S3 Bucket 中的文件結(jié)構(gòu),"-" 表示這是一個文件,"+" 表示這是一個目錄。

S3 Bucket
+ Directory for each ES
  - incompatible-snapshots
  - index-A
  - index-B
  - index.latest
  - meta-SNAPUUID.dat
  - snap-SNAPUUID.dat
  + NO NAMED DIR
    + indices
      + INDEXUUID
        - meta-SNAPUUID.dat
        + 0
          - __*
          - index-0
          - snap-SNAPUUID.dat

其中目錄的情況如下,對于那些文件后面專門說。

  • Directory for each ES
    我們有多個 ES 系統(tǒng),他們的內(nèi)容全部備份在一個 AWS S3 當(dāng)中,所以我們每個 ES 系統(tǒng)在這個 S3 Bucket 中有一個獨立的目錄保存所有的 snapshot。
  • NO NAMED DIR
    這是一個沒有顯性名稱的目錄,我猜官方的意思就是“別動這里”,后面的實驗證明,確實別動這里,動了麻煩會有點大。
  • indices & INDEXUUID
    就是一個固定目錄,里面裝的是所有備份了的 Index,每個 Index 在備份的時候,系統(tǒng)會重新定義一個 UUID 標(biāo)記這個 Index。這里不用 Index 本身的 UUID 應(yīng)該是考慮到在其他的 ES 系統(tǒng)恢復(fù)時不造成混亂。
  • 0
    這也是一個固定的目錄,本來以為多個分片會有多個目錄,但是我們實驗系統(tǒng)中多個主分片的 Index 在這里也是一個目錄。
    大家可以驗一下,如果有錯誤請指正。要不設(shè)定這么一級目錄實在想不明白 Elastic 的研發(fā)是怎么考慮的。

UUID 的使用

在 snapshot 執(zhí)行的時候,他會給每個 snapshot 生成一個 UUID,我們叫他 SNAPUUID。

在 S3 文件系統(tǒng)說明中會在每個 meta-* & snap-* 的部分使用這個 SNAPUUID 生成相關(guān)文件。這些文件都是二進(jìn)制文件,應(yīng)該是內(nèi)部的一些 mapping 內(nèi)容。

并且他會給每個被 snapshot 的 Index 文件生成一個 UUID,我們叫他 INDEXUUID。雖然在 ES 系統(tǒng)中,每個 Index 都有自己的 UUID,但是在這里保存的時候,snapshot 并沒有復(fù)制這個 UUID,而是給每個 Index 起了新的 UUID。這樣的方式應(yīng)該可以有效的防止沖突問題。

這些 INDEXUUID 在 <indices> 目錄中標(biāo)記了每個獨立的 Index,其中保存這個 Index 所有數(shù)據(jù)內(nèi)容。這些 UUID 都在 <index-A> & <index-B> 中有重要作用,稍后再說。

數(shù)據(jù)文件

在 <indices.INDEXUUID.0> 目錄當(dāng)中,所有這個 Index 的數(shù)據(jù)文件都在這里了。數(shù)據(jù)文件采用 "__" 開頭,后面使用 [0-9, a-z] 進(jìn)行順序編號。在我們的系統(tǒng)中單個文件好像被限制在 1GB 大小,如果一個 "__" 數(shù)據(jù)文件超過這個限制,會形成類似 "__1q.part0" & "__1q.part1" 這樣的分片文件。

這些數(shù)據(jù)文件的數(shù)目多少應(yīng)該跟 Index 在系統(tǒng)中的 Segment 數(shù)量有關(guān)系,但是并不是 Segment 有多少這里就有多少。我們并沒有通過實驗查出來其中的關(guān)聯(lián)關(guān)系。

這里單獨談數(shù)據(jù)文件是因為我們雞賊了一下,后面再說。

控制文件

在根目錄中有幾個控制文件:

  • incompatible-snapshots
    這個文件的作用應(yīng)該是處理沖突問題的,我們并沒有針對的做實驗看這個內(nèi)容,我們的系統(tǒng)中他一直都是空的 JSON 文件。
  • meta-SNAPUUID.dat & snap-SNAPUUID.dat
    這兩個文件是成對出現(xiàn)的,他們都是二進(jìn)制文件,并不知道他們保存了什么內(nèi)容。但是在 snapshot 執(zhí)行后,meta-SNAPUUID.dat 文件會先創(chuàng)建,而當(dāng) snapshot 執(zhí)行完畢后,對應(yīng)的 snap-SNAPUUID.dat 文件才會創(chuàng)建。

根目錄 index* 文件

這些文件比較重要,著重做說明。

index-A & index-B

這里的 A & B 只是個標(biāo)記,您的系統(tǒng)中可能叫做 index-18 & index-19。這個 index- 開頭的文件在系統(tǒng)中會一直保持有兩個。

他保存了一次 snapshot 操作開始執(zhí)行時的 S3 當(dāng)中 snapshot 的情況,和執(zhí)行后的 snapshot 情況。這些文件是 JSON 文件,有興趣完全可以打開看看。

{
  "snapshots": [
    {
      "name": "snap-2017-10-14",
      "uuid": "ilVKxRUxRxyJRmfnps6HDA",
      "state": 1
    },
    {
      "name": "snap-2017-10-18",
      "uuid": "T4rGaActS6-dJW6Pz6KGHA",
      "state": 1
    }
  ],
  "indices": {
    "metric-2017-10-24": {
      "id": "1smmvcuATkuy-hokdARDPw",
      "snapshots": [
        "nhFg0A3QT4yHOAhzDMY_3w"
      ]
    },
    "metric-2017-10-25": {
      "id": "pjFu2jmKSNGpp1L-W55iHw",
      "snapshots": [
        "sFtZ2JGRTqmWzK3wH9vVlg"
      ]
    }
  }
}

我們的這個文件比較長,我摘取了其中一段,上下并不能對應(yīng),不好意思。

這個文件的結(jié)構(gòu)很清晰,兩個部分:snapshots & indices,其中包含了所有的 S3 中剛才提到的 UUID 內(nèi)容。

后面談到數(shù)據(jù)出現(xiàn)問題恢復(fù)的時候,就靠這里的 UUID 來解決問題了。

index.latest

這是用文本文件存儲的 64bits 數(shù)字,他說明當(dāng)前上面的 index-* 文件后面的最新編號應(yīng)該是什么。

通過修改這個文件中的編號,然后自己仿造一個 index-XX 文件后,完全可以讓 ES 系統(tǒng)只讀取你希望他看到的 snapshot 或者 indices 記錄內(nèi)容。后面文件損壞后的恢復(fù)中會用到這個文件。

AWS S3 存儲格式選擇

AWS S3 提供了 4 中存儲文件的類型:

類型 Remark
Standard 標(biāo)準(zhǔn)的文件存儲
Standard_IA 不頻繁調(diào)用的文件存儲,價格比 Standard 便宜 50% OFF
Glacier 存檔文件,必須手動恢復(fù)才能調(diào)用,價格比 Standard 便宜 75% OFF
Reduced_Redundancy 可靠性降低的存儲,讀取速度也會慢一點,但是價格比 Standard 貴一點點,我都懷疑是不是我眼花了,實在不明白他存在的意義 ??

他們具體的價格可以參考 AWS S3 存儲類別 而對于 Reduced Redundancy Price 是單列的。有懂得朋友給解釋解釋 Reduced Redundancy 的類型工作目標(biāo)到底是啥?存在即合理嗎,我實在想不明白。

S3 操作

在 AWS S3 的控制臺操作文件轉(zhuǎn)換的時候,可以設(shè)定 Standard / Standard_IA (SIA) / Reduced_Redundancy (RRS) 為文件轉(zhuǎn)換目標(biāo)格式。而針對 Glacier 格式,只能通過 “生存周期” 控制中指定創(chuàng)建天數(shù)后進(jìn)行轉(zhuǎn)換。

不明白 AWS 這樣的設(shè)定意義是什么,不過系統(tǒng)控制臺操作有這么個限制。大家別說在控制臺找不到轉(zhuǎn)換為 Glacier 的位置。

服務(wù)端壓縮與加密

在設(shè)定 Repository 的時候,建議設(shè)定 "compress": "true" 選項。我們的數(shù)據(jù)內(nèi)容大概壓縮的比例是 43% OFF 的狀態(tài)。

AWS S3 存儲和執(zhí)行命令收錢 CPU 又不收錢,讓他服務(wù)端去壓縮去吧。這個存儲容量大概省一半銀子。

而對于加密選項我們沒有測試,看過說明應(yīng)該會使用到一些證書的配置。我們采用獨立的 S3 Bucket 進(jìn)行訪問權(quán)限限制來進(jìn)行一定的安全限制,就沒有添加這個加密選項,省的其他 ES 從 S3 恢復(fù)數(shù)據(jù)時給自己找麻煩。

多省點銀子 ??

我們的系統(tǒng)選擇了 Standard_IA 模式存儲。 "storage_class": "standard_ia"

Standard_IA 模式有兩個限制:

  • 不足 128KB 的文件按照 128KB 計算容量;
  • 每個文件收費時間顆粒度為 30天,就是說如果你只存了一個小時也按照 30 天收費;

我們的每個 Index 文件大概在 10GB - 20GB 左右。統(tǒng)計了一下一個 Index 一共有 119 個文件,小于 128KB 的只有 46 個,容量有 9.1MB。這個狀態(tài)怎么算都是用 SIA 模式更便宜。并且在數(shù)據(jù)備份后,應(yīng)該從 S3 恢復(fù)到 ES 系統(tǒng)的頻率會比較低,所以我們選擇了 Standard_IA 模式,來降低存儲的成本。50% OFF 哦 :-)

并且針對所有存儲的文件我們設(shè)定創(chuàng)建 390 天后,自動轉(zhuǎn)換為 Glacier 模式,再省 50% 銀子。因為 SIA 模式 30 天的限制,365 天肯定是虧的 ??

但是在轉(zhuǎn)換 Glacier 模式的時候,請注意要添加一個 "__" 前綴的模式匹配。

  • 數(shù)據(jù)文件都是 __ 開頭的,這些大容量文件轉(zhuǎn)換后也就完成了主要的成本節(jié)約;
  • 如果不限制這個文件意味著 meta-* & snap-* 文件也會被轉(zhuǎn)換,就會影響一些指令的執(zhí)行,后面會有介紹;

前前后后在 AWS S3 的存儲中,這一系列的操作可以節(jié)省大概 85% OFF 的成本。下面是我們的 Repository 的設(shè)定,供參考:

{
  "my-repository": {
    "type": "s3",
    "settings": {
      "bucket": "MY-BUCKET",
      "endpoint": "s3-us-west-2.amazonaws.com",
      "protocol": "http",
      "server_side_encryption": "false",
      "storage_class": "standard_ia",
      "max_retries": "3",
      "readonly": "false",
      "compress": "true",
      "base_path": "ES-DIRECTORY/"
    }
  }
}

傳輸速率

在我們測試的過程中,同一個 AWS Region 傳送的速率在 90Mbps - 40Mbps 之間,不知道是否因為雖然同一個 Region 而不同的賬戶會導(dǎo)致速率下降。因為 Elastic Cloud 自己做的全量備份,明顯速度比這個快很多。大家可以通過這個速度預(yù)估自己的 snapshot 執(zhí)行的時間長度。

并且,在備份的過程中,我們發(fā)現(xiàn)有時候一個 snapshot 中的不同 Index 是同時并行發(fā)送的,而有時候是串行同步的。感覺上應(yīng)該是如果 Index 有 Replicas 那么就可以并行,否則串行,這個沒有特別認(rèn)真的核對過。我們對現(xiàn)在的速度已經(jīng)很滿意了。畢竟一個 snapshot 是 10 秒執(zhí)行完還是 10分鐘執(zhí)行完,沒太多人關(guān)心 ??

傳輸異常情況

操作過程中我們發(fā)生過一次卡死在指定位置不動,一個十來分鐘的 snapshot,我們等了超過一個小時。并不知道這個問題出現(xiàn)在哪里。

這時候,我們通過 DELETE 這個正在操作的 snapshot 內(nèi)容,中斷了這個操作。但是,在后一次的操作過程中有點奇怪,不過我們再也模擬不出這種卡死情況,就只剩一個奇怪的感覺了。后面 snapshot & restore 的所有過程中,并沒有發(fā)現(xiàn)有什么不對的地方。

Snapshot 操作監(jiān)控

我們通過 AWS Lambda 啟動 Elasticsearch Snapshot 后,2 個小時添加了一個 Elasticsearch Watcher 程序用于檢查備份是否完成。

監(jiān)控通過 GET _snapshot/my-repository/_all 獲取所有 snapshot 的列表,然后根據(jù) <snapshots.snapshot> 這個位置的內(nèi)容判斷是否存在指定的 snapshot。若有,則判斷 <snapshots.state> 位置的內(nèi)容,如果正在備份中會標(biāo)記 "IN_PROGRESS",如果已經(jīng)完成會標(biāo)記 "SUCCESS"。

我們整體備份過程中沒有發(fā)現(xiàn)出現(xiàn)其他的錯誤狀態(tài),哪位大蝦碰到過給個范例,萬分感謝。

個別文件類型不對

有些 snapshot 生成的文件,應(yīng)該是 Elasticsearch 自己控制的,他們并不受你指定的存儲類型限制。默認(rèn)這些文件都是 standard 格式的。

  • incompatible-snapshots
  • index-A
  • index-B
  • index.latest
  • indices.INDEXUUID.0.index-0

上面這些文件不管你怎么搞,都會是 standard 格式。

文件損壞和修復(fù)

AWS S3 從來沒有保證自己 100% 的文件可靠性。當(dāng)一些文件出現(xiàn)問題的時候,需要進(jìn)行一些修復(fù)才能正常使用 _snap 的相關(guān)指令內(nèi)容。

當(dāng)然我們關(guān)心這個是希望降低存儲成本,定期會將文件轉(zhuǎn)換為 Glacier 格式,這種格式將導(dǎo)致系統(tǒng)會認(rèn)為這個文件不存在了。

當(dāng)一些文件出現(xiàn)問題時,我們將遇到如下的一些問題。

對狀態(tài)指令的影響

在執(zhí)行相關(guān)指令的時候大概經(jīng)歷了如下的過程:

  • GET _snapshot/my-repository/_all
    • 得到 S3 index.latest 文件內(nèi)的編號
    • 得到 S3 index-XX 文件內(nèi)的內(nèi)容
    • 檢查文件標(biāo)記中每個 UUID 對應(yīng)的 meta-* & snap-* 文件內(nèi)容
    • 給出結(jié)果
      也就是只要保證 <index.latest> <index-A> <index-B> <meta-SNAPUUID.dat> <snap-SNAPUUID.dat> 文件是完整的,這個指令不會出現(xiàn)問題。
      同樣對于 GET _snapshot/my-repository/snap-YYYY-MM-DD 這樣的指令也不會出現(xiàn)問題。
  • GET _snapshot/my-repository/snap-YYYY-MM-DD/_status
    • 同上面指令的過程;
    • 獲取 indices.INDEXUUID 目錄中的 <meta-SNAPUUID.dat> <0.snap-SNAPUUID.dat> 文件內(nèi)容
    • 給出結(jié)果
      也就是需要一個 snapshot 備份的具體內(nèi)容時,兩對 meta-* 和 snap-* 文件都要是正常的。

如果這些文件出現(xiàn)問題,就會得到一個 500: no_such_file_exception 的錯誤信息。這時候除非恢復(fù)這些關(guān)聯(lián)的文件,則 DELETE 指令和 _restore 指令都無法執(zhí)行。

對 restore 的影響

  • POST _snapshot/my-repository/snap-YYYY-MM-DD/_restore
    • 執(zhí)行上面類似的過程;
    • 在對應(yīng)的幾個 INDEXUUID 目錄的 0 目錄中獲取每個數(shù)據(jù)文件;

若其中個別文件出現(xiàn)問題,則會給出一個 503: concurrent_snapshot_execution_exception 的錯誤信息。請注意,在進(jìn)行 _restore 操作的時候,建議設(shè)置 "ignore_unavailable": false 參數(shù),避免出現(xiàn)無法恢復(fù)而得到一個正確結(jié)果的假象。

這時執(zhí)行 DELETE _snapshot/my-repository/snap-YYYY-MM-DD 指令一樣會得到 503: concurrent_snapshot_execution_exception 的錯誤信息。

必須對 AWS S3 上的控制文件進(jìn)行修復(fù)后,才能繼續(xù)執(zhí)行這些實際的操作指令。

修復(fù)

當(dāng)這些文件有問題的時候,只能屏蔽指定的 Index 文件或者指定的 snapshot 了。

就跟前面說的一樣,這時需要打開根目錄中的 <index-B> 文件,編輯其中的內(nèi)容。將出現(xiàn)問題的內(nèi)容刪除即可,當(dāng)然如果是跳過 snapshot,需要改 snapshot & indices 兩個部分的內(nèi)容。

在修改的時候,就需要通過 UUID 對應(yīng)目錄結(jié)構(gòu)中的內(nèi)容和 <index-B> 中的內(nèi)容處理了。當(dāng)然,你也可以創(chuàng)建一個新的 <index-XX> 文件,然后修改 <index.latest> 文件指向他,來進(jìn)行這個恢復(fù)。這樣如果不行,起碼還有個回退的機(jī)會。??

當(dāng)然還有一個更加保險一些的方案,就是當(dāng)接收到一個 snapshot 后,可以將根目錄的 <meta-SNAPUUID> <snap-SNAPUUID> <indices.INDEXUUID> 這些文件弄個 ZIP 包,在備份到另外一個 AWS S3 區(qū)域當(dāng)中。不過這個花費就不僅僅是存儲了,還有傳輸?shù)雀鞣N費用問題。也可以利用 AWS Glacier 的獨立控制臺生成這些內(nèi)容,他跟 AWS S3 上的限定日期轉(zhuǎn)換為 Glacier 是不同的。這個部分我們沒有這么高的要求,就沒有詳細(xì)看了。

如果有損壞文件的備份,只要將其恢復(fù)到指定位置中,受影響的 snapshot 指令立刻就可以執(zhí)行了。在 Elasticsearch 內(nèi)部沒有保存對應(yīng)信息的好處就在這里了,如果他保存了一個什么狀態(tài)機(jī),那就等著哭吧。

Snapshot 指令的一些說明

官方在 Snapshot and Restore 已經(jīng)介紹的非常詳細(xì)了。一般就這些指令:

  • POST _snapshot/my-repository/snap-YYYY-MM-DD
    異步指令,可以通過添加 ?wait_for_completion=true 參數(shù)讓他變成同步的,不過建議采用異步方式,不然一般都會超時,可以啟動后采用 GET _snapshot/my-repository/snap-YYYY-MM-DD 或者 GET _snapshot/my-repository/_status 進(jìn)行執(zhí)行狀態(tài)的監(jiān)控。
  • POST _snapshot/my-repository/snap-YYYY-MM-DD/_restore
    異步指令,恢復(fù)的過程中 Elasticsearch 會有短暫的紅色報警。
  • DELETE _snapshot/my-repository/snap-YYYY-MM-DD
    同步指令,而且刪除速度很快,也用于 snapshot 備份過程中中斷當(dāng)前備份操作使用。

相關(guān)參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容