0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

误删ElasticSearch生产数据库后的复盘

倩倩 来源:AI前线 作者:Hugo Rocha 2022-09-22 14:14 次阅读

项目背景

这件事情发生在几年前时,当时我在一家初创的电子商务公司就职,主要负责领导两支团队开发几项核心后台功能。后台的作用是管理在前端当中向全球用户开放的信息,这些信息又分别由不同的团队维护。虽然这家公司历史不长,但已经在全球市场上建立起影响力,坐拥数十万用户群体。

其中一支团队开发了支持大部分后台流程和工具的主要后台产品目录,存放着库存、产品信息管理、订单履行流程等大量内容。这个组件相当关键,大多数后台服务、应用程序和业务流程都会以某种方式进行访问。具体情况可以参考下图:

0892c20e-3a39-11ed-9e49-dac502259ad0.png

图一:非规范化读取模型的简化架构示意

该平台采用的是微服务架构,其中产品目录属于读取模型,包含由多个不同领域事件流建立而成的非规范化信息,再由其他微服务加以管理。产品目录本身由一个 ElasticSearch 数据库支持,其中容纳共 1700 万种产品,具体涉及产品元数据、库存、生产信息、可用性、定价等,而且全部都向 REST API 开放。我们之所以使用 ElasticSearch,主要是因为需要配合大量不同种类的过滤器(共有 50 多种不同过滤器,其中一些还带有文本搜索功能)。

再谈 ElasticSearch

正常来讲,没人能直接向数据库发起写入(我们在不同用例中使用到了多种技术,包括 SQLServer、MongoDB 和 Cassandra 等),但 ElasticSearch 却是个例外。毕竟在传统上,ElasticSearch 应该是由工程团队,而非基础设施或 DBA 团队进行管理。与其他数据库技术不同,ElasticSearch 是通过 REST 接口访问的。通常,URL 具有以下格式(当时我们使用的是 ElasticSearch 版本 5):{cluster_endpoint}/{index_name}/{type}/{document_id}(例如: elastic.com/productIndex/product/152474145)这种类型在后续版本中被删除了。

其中任何类型的操作都是通过 HTTP 调用或者 SQL 脚本完成的。就是说在 ElasticSearch 当中,我们肯定要用到 HTTP 请求。比如说根据 REST 指南,如果用户拥有一套产品目录索引(ElasticSearch 中的索引基本相当于 SQL 表)并想获取特定产品,则需要执行 GET elastic.com/productIndex/product/152474145。更新的时候,需要使用 PUT 或 PATCH 操作操作这个端点,删除的时候用 DELETE,创建的时候则是用 POST 或 PUT。另外,这些操作还可以指向 URL 中的不同部分,比如对 elastic.com/productIndex/product 执行 GET 可以获取类型信息,创建、删除或者更新等操作也是同理。如果指向的是 elastic.com/productIndex,则代表获取索引信息、更新、删除或创建索引。

事件回溯

那是一个普通的礼拜五,一整天大家都在不停地开会,反正上班的日常就那个样子。为了处理临时任务,比如帮助同事解决问题或者根据团队申请帮他们完成无权进行的操作,我抓住了会议之间的一点点小闲暇。这时候,我看到一条请求希望通过 API 中本不可用的过滤器导出一些数据。这操作挺少见的,但考虑到对方团队很着急、理由又充分,我们还是决定出手相助。

于是趁着下场会议还有 15 分钟,我迅速连上另一位高级管理员,想要快速访问实时环境并执行查询。由于对 ElasticSearch 的直接访问在本质上就是对接 REST API,所以我们习惯性地使用 Postman 来执行请求。

这位同事通过远程屏幕共享向我开放了操作平台。其实我的工作习惯还好,一般会对实时操作先进行一番代码审查。所以我想先测试一下连接,确保自己拿到的 URL 正确无误。于是我复制了实时端点和索引名称(类似于我们前文讨论过的 cluster_endpoint/index_name),并提交了一条 GET 请求。如果大家熟悉 Postman 界面,应该会记得在下拉列表中选择 HTTP 操作的过程:

08aaf130-3a39-11ed-9e49-dac502259ad0.png

图二:在 Postman 界面中选择 HTTP 操作

很遗憾,我在提交了请求之后,才注意到自己把操作错选成了 DELETE,而不是 GET。操作的结果根本不是检索索引信息,而是直接将其删除。

这条请求要花几秒钟才会确认,所以我立刻按下了取消按钮。取消操作立即提示成功,我的心里又涌起一丝希望,天真地认为事情已经过去、刚刚那些都是幻觉。

08c3b1d4-3a39-11ed-9e49-dac502259ad0.png

图三:Postman 界面似乎可以取消尚未完成的请求

但很遗憾,知道我要取消的就只有 Postman 的客户端;实际操作仍然一路狂奔,抵达了 ElasticSearch 服务器端。我试着用不加过滤条件的常规搜索确认了索引总数,而期待中的 1700 万结果并没有出现,查询返回的结果只有几百条(我们的服务每秒大约发生 70 个事件,剩下的这几百条应该是删除同时发生的产品创建 / 编辑操作)。

情况就是这么个情况,我不小心把产品目录里 1700 万条产品记录、来自整个平台数十项微服务的信息还有自己的职业声誉,全都搞砸了……

事情仍有转机

跟老板通话之后,我们很快组织起作战指挥室,处理各个服务区上报的问题。由于这套系统的本质就是个读取模型,而非任何特定信息的真实来源,所以我们“只需要”从其他服务那边获取信息就行。

所以摆在面前的选项就只有:

ElasticSearch 无法在发生重大变更时随之调整 schema,它的基本策略还是将所有信息重新导入新的索引当中。为此,我们设计了一款组件,能够同步 REST API 以从其他微服务处获取数据,重新构建每款产品。在它的帮助下,我们能够解决上游服务发生的错误,应对突发事件引起的一致性冲突。但是,获取全部 1700 万种产品的所有数据大概要花六天时间。管不了那么多了,我们决定马上跑起来。

08de2f64-3a39-11ed-9e49-dac502259ad0.png

图四:Catalog Updater 架构——目录重建组件

另外一个选择就是使用事件流。大多数服务都能在必要时重新发布事件,某些关键区域还具备数据重播功能,这些数据可以跟正常使用中的变更顺畅合流、为用户服务。

而最大的希望也在于这里。在此之前的几天,我们刚刚在 schema 当中做了一次重大变更,所以需要创建新的索引版本来重新索引全部信息。因为需要同时在新旧两个版本中接纳新近变更,所以重新索引过程相当漫长。我们此前已经对旧索引做好了分析,而需要进行重大变更的新功能其实不怎么重要,就是说现在我们手头已经有了一套完全可以接受的旧索引版本。虽然数据还是延迟了几天,但毕竟要比空空如也好得多。所以在综合讨论了几种方案之后,我们最终成功解决了这场突发危机。

经验教训 备份与重建速度

备份的必要性已经无需多言。我们的大多数数据库都有备份,但却没有给 ElasticSearch 数据库做好相应的保护。另外,该数据库本身属于读取模型,所以根据定义并不作为任何真实来源。理论上,读取模型就不该需要备份,因为可以快速重建,确保在发生重大事件时也不会造成太过严重的影响。由于读取模型所容纳的基本都是从其他来源推断出的信息,所以很难确定到底值不值得做定期备份。但在实践中,我们发现要在不影响用户体验的同时重建模型,绝对是个令人头痛的大麻烦。如果是只有几百或几千条记录的小模型还好,但像我们这种覆盖几十个不同来源、承载上千万条信息的读取模型就完全是另一码事了。

我们最终决定把两种选项结合起来,成功将重建流程从六天缩短到了几个小时。但由于这套数据库太过重要,所以这几个小时的宕机还是会给用户造成重大影响,特别是在销售季等特定活动期间。我们也可以想办法进一步缩短重建时长,但具体方案感觉有点过度设计,而且会产生大量额外的基础设施成本。所以我们决定只在风险较高的时段内进行备份,例如在促销季活动或其他关键业务执行期间。

横向扩展根本指望不上

大家常说,选择微服务的一大核心优势就是良好的横向扩展能力。但从图四能够看到,这种扩展只能依赖于同步 API,所以横向扩展可以说根本指望不上。负责重建读取模型的组件需要整整六天才能执行完成,虽然理论上可以通过横向扩展把时间大大缩短,但问题是它仍然要靠 REST API 来检索信息。它通过 REST 请求从其他各项微服务处请求数据,构建起非规范化视图和持久化状态。所以直接横向扩展会触发大量指向其他服务的请求,而那些服务并没有做好处理高强度额外负载的准备,所以可能还需要再各自扩展。这必然引发连锁反应,最终令整个平台走向崩溃的边缘。另外,其中大多数微服务还高度依赖数据库,所以微服务的扩展又会引发相应数据库的扩展。

我们确实进行了扩展,只是把扩展量控制在很保守的水平。而即使是这样,其他服务也有点招架不住,出现了可以感知到的影响。现在来看,整个微服务架构并不像我们想象中那样高度解耦,反而很像是当初的单体架构。更要命的是,它没有分布式的优势、却得了分布式的病,管理起来异常麻烦。

所以在重建组件时,我们选择了纯事件流的方法。这种方式虽然也有问题,但至少能让系统真正具有解耦性。就是说组件的扩展只影响对应资源,这才是真正具备横向扩展能力的设计。这里还有另一个设计难题,就是事件应该大一些还是小一些。至少对读取模型来说,事件还是越大越好。我们还用到一项有趣的策略,就是使用了带有 Kafka 压缩主题的文档,借此大大提升速度和扩展能力。这种方法能把重建策略从批处理转化成流处理。与通过 HTTP 请求获取数据不同,事件流上的数据有着更低的获取难度和更快的获取速度,原因就是它的网络延迟更低,而且不用靠中间服务从数据库内获取数据,一切就在事件流上。另外,事件流的真解耦性也让整个过程实现了横向扩展,再不用担心对其他服务产生意外影响。

基于角色的访问机制

事件发生之后,我们开始全力推行基于角色的访问控制。之前我们使用的是旧版 ElasticSearch,它只提供非常基础的用户身份验证,而更靠谱的 XPack 在这个版本里是要收费的。不过在后续更新中,XPack 也被加入了免费许可证套餐,真正是好用又不贵了。

所以,我们迁移到了 ElasticSearch 版本 7 并创建了不同的读写角色。最终,只有应用程序能够定期直接写入数据库,用户最多只能直接读取。

责任不在人,而在流程

每当出现问题,我总会向技术团队强调,最大的责任不在于人,而在于糟糕的流程。我们需要分析流程中的哪个环节出了问题并找到解决办法,避免任何人——无论是刚刚入职的新员工,还是经验丰富的老伙伴——再犯同样的错误。

我一直秉持这样的管理思路,也时时处处用这样的方式管理工作、处理事件。虽然这事已经过去几年了,虽然我还是会偶尔想起这一切并尴尬地苦笑,但这个契机确实也给我们带来了更合理的操作流程。我们调整了实时数据的访问方式,消除了直接进行写入操作的权限。甚至对于读取访问,我们也开始采取更审慎的态度,毕竟恶意查询很可能对 ElasticSearch 资源产生的可怕的影响,某些极端复杂的查询(例如高深度分页)甚至会引发集群崩溃(例如超过客户端节点的内存上限)。我想再强调一句,这不是要剥夺团队的自主权,而是帮助大家尽量少犯错。

临时请求会被提交给专管这类请求的实时工程团队,所以正常来讲大家根本不需要直接访问数据库。手动重复任务已经被整合进对应服务的功能当中,并通过应用层加以适当验证,这就消除了出现意外删除或大量查询的可能性。总体来讲,我们的调整就是要确保人们能够用适当的工具完成工作、响应业务请求,而且始终保持安全稳定。

写在最后

其实在闹出这事之前,我在很多文章里都读到过类似的情景,但从没想过有一天自己会成为故事的主角。那时候我的想法很简单,“我做事是讲套路的,绝对不会轻易下手。”但有时候,难以挽回的错误可能只需要一瞬间的分心、一瞬间的疏忽。这段经历教会了我要永远保持谦卑,我也大大方方把这个故事讲给每位团队成员听,让他们知道技术负责人也一样可能会犯低级错误。所以最重要的还是给自己加上点约束,避免我们“愚蠢的一面有机可乘”。

审核编辑 :李倩

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • HTTP
    +关注

    关注

    0

    文章

    501

    浏览量

    31013
  • 数据库
    +关注

    关注

    7

    文章

    3754

    浏览量

    64255
  • 微服务
    +关注

    关注

    0

    文章

    131

    浏览量

    7322

原文标题:误删ElasticSearch生产数据库后的复盘

文章出处:【微信号:AI前线,微信公众号:AI前线】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    数据库数据恢复—通过拼接数据库碎片恢复SQLserver数据库

    一个运行在存储上的SQLServer数据库,有1000多个文件,大小几十TB。数据库每10天生成一个NDF文件,每个NDF几百GB大小。数据库包含两个LDF文件。 存储损坏,数据库
    的头像 发表于 10-31 13:21 120次阅读
    <b class='flag-5'>数据库</b><b class='flag-5'>数据</b>恢复—通过拼接<b class='flag-5'>数据库</b>碎片恢复SQLserver<b class='flag-5'>数据库</b>

    Oracle数据恢复—异常断电Oracle数据库报错的数据恢复案例

    Oracle数据库故障: 机房异常断电,Oracle数据库报错:“system01.dbf需要更多的恢复来保持一致性,数据库无法打开
    的头像 发表于 09-30 13:31 224次阅读
    Oracle<b class='flag-5'>数据</b>恢复—异常断电<b class='flag-5'>后</b>Oracle<b class='flag-5'>数据库</b>启<b class='flag-5'>库</b>报错的<b class='flag-5'>数据</b>恢复案例

    数据库数据恢复—SQL Server数据库出现823错误的数据恢复案例

    SQL Server数据库故障: SQL Server附加数据库出现错误823,附加数据库失败。数据库没有备份,无法通过备份恢复数据库
    的头像 发表于 09-20 11:46 268次阅读
    <b class='flag-5'>数据库</b><b class='flag-5'>数据</b>恢复—SQL Server<b class='flag-5'>数据库</b>出现823错误的<b class='flag-5'>数据</b>恢复案例

    数据库数据恢复—SQL Server数据库所在分区空间不足报错的数据恢复案例

    Server数据库故障: 存放SQL Server数据库的D分区容量不足,管理员在E中生成了一个.ndf的文件并且将数据库路径指向E
    的头像 发表于 07-10 13:54 422次阅读

    数据库数据恢复—数据库所在分区空间不足导致sqlserver故障的数据恢复案例

    数据。服务器上部署sql server数据库数据库存放在C数据库故障: 工作人员发现服务器的C
    的头像 发表于 05-22 13:16 384次阅读
    <b class='flag-5'>数据库</b><b class='flag-5'>数据</b>恢复—<b class='flag-5'>数据库</b>所在分区空间不足导致sqlserver故障的<b class='flag-5'>数据</b>恢复案例

    数据库数据恢复—raid5阵列上层Sql Server数据库数据恢复案例

    数据库故障: 数据库文件丢失,主要涉及3个数据库,数千张表。数据库文件丢失原因未知,不能确定丢失的数据库文件的存放位置。
    的头像 发表于 05-08 11:43 471次阅读
    <b class='flag-5'>数据库</b><b class='flag-5'>数据</b>恢复—raid5阵列上层Sql Server<b class='flag-5'>数据库</b><b class='flag-5'>数据</b>恢复案例

    数据库数据恢复】Oracle数据库ASM实例无法挂载的数据恢复案例

    oracle数据库ASM磁盘组掉线,ASM实例不能挂载。数据库管理员尝试修复数据库,但是没有成功。
    的头像 发表于 02-01 17:39 460次阅读
    【<b class='flag-5'>数据库</b><b class='flag-5'>数据</b>恢复】Oracle<b class='flag-5'>数据库</b>ASM实例无法挂载的<b class='flag-5'>数据</b>恢复案例

    数据库数据恢复—未开启binlog的Mysql数据库数据恢复案例

    。 mysql数据库故障: 工作人员使用Delete命令删除数据时未添加where子句进行筛选,导致全表数据被删除,删除未对该表进行任何操作。
    的头像 发表于 12-08 14:18 1072次阅读
    <b class='flag-5'>数据库</b><b class='flag-5'>数据</b>恢复—未开启binlog的Mysql<b class='flag-5'>数据库</b><b class='flag-5'>数据</b>恢复案例

    关于JSON数据库

    如何理解JSON数据库?作为NoSQL数据库的一种类型,JSON数据库有哪些优势呢?JSON数据库如何运作,它为应用程序开发者带来了哪些价值呢?
    的头像 发表于 12-06 13:46 830次阅读
    关于JSON<b class='flag-5'>数据库</b>

    mysql数据库基础命令

    使用以下命令: mysql -u -p 其中,username是您的MySQL用户名。执行此命令,系统会提示您输入密码。成功输入密码,您将登录到MySQL命令行界面。 创建数据库 创建数据
    的头像 发表于 12-06 10:56 538次阅读

    oracle数据库的基本操作

    Oracle数据库是一种关系数据库管理系统(RDBMS),广泛应用于企业级应用中。它具有强大的功能和灵活的配置选项,可以满足复杂的数据处理需求。本文将介绍Oracle数据库的基本操作,
    的头像 发表于 12-06 10:14 648次阅读

    聊聊日志即数据库

    Pool尽量的合并并推迟真正的数据修改落。如果发生故障,可以通过日志的重放恢复故障发生前未刷的修改信息。也就是说Log中包含数据库恢复所需要的全部信息。
    的头像 发表于 12-06 09:30 665次阅读
    聊聊日志即<b class='flag-5'>数据库</b>

    什么是JSON数据库

    如何理解JSON数据库?作为NoSQL数据库的一种类型,JSON数据库有哪些优势呢?JSON数据库如何运作,它为应用程序开发者带来了哪些价值呢?文章速览:什么是JSON什么是JSON
    的头像 发表于 12-02 08:04 812次阅读
    什么是JSON<b class='flag-5'>数据库</b>

    NoSQL 数据库如何选型

    什么是NoSQL数据库?为什么要使用NoSQL数据库?键值数据库内存键值数据库文档数据库列式数据库
    的头像 发表于 11-26 08:05 435次阅读
    NoSQL <b class='flag-5'>数据库</b>如何选型

    数据库数据恢复—SQLserver数据库被加密如何恢复数据

    一台服务器上的SQLserver数据库被勒索病毒加密,无法正常使用。该服务器上部署有多个SQLserver数据库,其中有2个数据库及备份文件被加密,文件名被篡改,数据库无法使用。
    的头像 发表于 11-23 14:42 877次阅读
    <b class='flag-5'>数据库</b><b class='flag-5'>数据</b>恢复—SQLserver<b class='flag-5'>数据库</b>被加密如何恢复<b class='flag-5'>数据</b>?