从可分割的数据库中删除数据

有关如何解决优化从分片系统删除文件的过程问题的文章。 这是一个用于共享和处理文件的项目。 该系统大约是8年前的一家初创公司,随后成功拍摄并售出了好几次。 该项目从一开始就有4个开发人员参与该项目,这非常有价值。 传统上,文档要么没有时间写,要么就没有太大关系。

你为什么要读这本书,为什么我要写所有这些书? 我想谈谈一个耙子,该耙子仔细地位于系统内部并进行搏动,以使星星从眼睛中溢出。

我要非常感谢Hanna_Hlushakova的共同努力,使项目结束并为准备本文提供了帮助。 基本上,您会找到我们所使用的问题描述和解决算法,没有代码,数据结构或其他必要内容的示例。 我不知道我的经验是否可以帮助您避免在家中抽水,但我希望您能从中受益。 也许这篇文章将是不可挽回的宝贵时间损失。



关于项目


该项目是Gartner广场的领导者之一,在美国和欧洲拥有超过30万员工的客户公司,并维护着数十亿个文件。

该项目中使用了以下技术:Microsoft,C#.net服务器,MS SQL数据库,14个活动服务器+ 14个处于数据镜像模式。

数据库的容量高达4 Tb,在工作时间内的恒定负载约为每分钟40万个请求。

数据库中有很多业务逻辑:
450桌
1000个存储过程
80,000行SQL代码
传统上,他们要么没有时间编写文档,要么不相关。

关于任务


任务是从客户端重做删除存储中的文件,并且要恢复的已删除文件的存储期限已到期,该操作是要重做。 在当前版本中,根据公司本身的计算,一年前删除的文件存储在服务器上,尽管根据业务条款,它们应该只存储1个月。 由于某些文件存储在S3上,因此公司为额外的数据付费,而使用本地存储的客户想知道为什么文件占用的空间超出了应有的空间。

公司客户的数据库资料库。

删除是如何进行的?





在具有有关系统中所有文件信息的全局服务器上,形成了1.5万个文件标识符的范围。

然后,根据时间表,针对一系列文件标识符启动了服务器调查。
范围的边界已传输到每个分片。

碎片响应发送了该范围内的找到的文件。

主服务器将丢失的文件添加到队列表中以在其数据库中删除。
然后,从队列表中,存储中的物理文件删除服务接收到一堆要删除的标识符,此后它发送确认即将删除文件的确认,并开始检查所有分片是否在这些文件中使用过这些文件。
随着文件数量的增加,这种方法开始工作的非常缓慢,因为有数十亿个文件,范围的数量也大大增加。 与已删除的文件总数相比,已删除的文件仍然少于5%,对数十亿个文件进行排序以查找数百万个已删除的文件效率非常低。

例如,通常在用户删除文件后,应将其存储1个月,以防需要恢复。 在此期间之后,程序应从存储库中删除文件。 使用当前的文件数,范围数和范围速度绕过,服务器完全绕过所有范围需要1年。
显然,这个地方没有被释放,这引起了用户的不满,因为他们的服务器存储的文件比应报告的多得多。 服务公司本身为S3支付了额外的费用,这是它的直接损失。

在工作开始时仅在S3上,存储了2 PB的已删除文件,并且仅在云上。 此外,有些客户端的文件存储在专用服务器上,这也存在相同的问题:服务器的空间被用户删除但未被从服务器删除的文件占用。

您决定做什么?


我们决定跟踪移除事件:

  • 客户端删除该文件,然后过期。
  • 用户立即删除了文件,无法恢复。

当从分片数据库中删除文件时,我们决定采用一种乐观的方法,并删除其中一项使用检查。 我们知道99%的文件仅在一个分片内使用。 我们决定立即将文件添加到删除队列中,并且不检查其余分片是否可使用该文件,因为当从存储中删除确认服务后,将再次进行检查。

此外,他们离开了当前的JOB,按范围检查已删除的文件,以便添加在发布新版本之前已删除的文件。

在分片上删除的所有内容都收集在一个表中,然后连同有关所有文件的信息一起转移到单个服务器上。

在此服务器上,它被发送到删除队列表。

在删除前删除表中,检查该文件未在所有分片上使用。 这部分检查在代码更改之前就在这里,他们决定不去修改它。

您需要在代码中进行哪些更改?


在每个分片上,添加了一个表,已删除文件的标识符应写入该表中。

我们发现从数据库删除文件的所有过程只有2个。用户删除文件后,文件仍然在数据库中保留了一段时间。

在从数据库删除文件的过程中,我们向此本地表添加了一个条目,其中包含已删除的文件。

在带有文件的全局服务器上,他们创建了一个JOB,可从分片数据库下载文件列表。 只需从可拆分数据库中调用过程,它就会在内部进行DELETE并在OUTPUT中显示文件列表。 在MS SQL Server拉中-从远程服务器中拉取比插入到远程服务器中要快得多。 所有这些都是在块中完成的。
然后将这些文件添加到全局服务器上的删除队列表中。
将具有分片标识符的表添加到队列表中,以了解删除事件的来源。

大家如何测试?


有3种环境:

Dev是一个开发环境。 该代码取自gith的develop分支。 可以在IIS上部署不同版本的代码,并进行业务流程的多个版本。 它将从vpn内的客户端连接到开发环境。 直到最近,不便之处仅在于数据库,因为对数据库的所有更改都可能破坏系统其他部分的工作。 然后基地在当地。 可以将运行中的代码倒入带有数据库的开发服务器上,以免破坏每个人的工作。 在开发环境中,产品上有3个分片,而不是12个,但这通常足以测试交互。

暂存-根据代码的版本,产品与产品的环境相同(几乎相同,这很少见,但管理员直接在产品上进行更改)。 来自master分支的代码副本。 有时,数据库中会发现与产品代码上的某些差异,但是通常它们是相同的。 在分期和未婚上也有3个分片。 暂存和开发都没有负担。 在这里,您可以完全运行集成测试,因为代码与产品匹配。 所有测试都必须通过,这是进入部署之前的先决条件。

性能实验室,在负载下进行测试。 负载是使用jmeter创建的,比产品上的负载少10倍,并且只有一个分片,有时会带来不便。 取得销售数据,然后将其匿名化并在性能实验室中使用。 与产品相同配置的所有服务器。

负载减少了10倍,因为假定这是一个大约1个分片的负载。 不利的一面是,与销售不同,全球基础利用率极低。 而且,如果更改主要涉及带有文件的全局数据库,那么您可以仅大致依赖测试结果-在产品上可能无法那样工作。 尽管perf实验室不能理想地将负载与产品匹配,但是在负载下进行测试的能力已经有助于在部署到产品之前捕获许多错误。

还有一个备用服务器,您可以在其中查看销售中的数据,以发现某些情况。 通常,公司的营业执照禁止开发人员访问销售数据,管理和支持团队(运营)禁止开发人员访问,因此您需要寻求数据库管理员的帮助。 销售数据使测试变得非常容易,因为某些情况仅发生在产品数据上,在真实系统中研究数据以了解系统对用户的工作方式非常有用。

在perf实验室进行测试期间,事实证明,从单词开始根本没有实现从存储中删除文件的负担。 实施负载测试时,我们从客户端软件中选择了更受欢迎的请求,其中一些未包含在清理存储中。 由于这是一个数据库,因此可以通过对不同数据手动调用更改过的过程来对所有更改过的对象进行简化的测试。 (根据我所知道的选项)。
此外,在集成和性能测试中,主要重点放在了最流行的文件存储类型上。

性能实验室的另一个功能(尚未立即发现)是产品和性能某些表中的数据量不匹配。 从某种意义上说,所有销售作业都在性能上起作用,这些性能会生成数据,但是并不总是有什么东西来处理表中生成的数据。 并且,例如,上述用于在性能上删除的表队列要比在产品上大得多-性能上有2000万条记录,产品上有20万条记录。

部署过程


部署过程非常标准。 此任务的应用程序代码中没有更改,所有更改仅在数据库中。 始终将DBA滚动到数据库中;此过程不是自动化的。 准备了2个版本的脚本-用于应用更改和回滚更改,并编写了DBA指令。 总是制作2个版本的脚本,并且必须对它们进行回滚和更改回滚的测试。 这些相同的脚本用于在启动集成和负载测试之前将更改应用于登台和性能实验室。

部署后发生了什么?


部署后的前5个小时,有100万个事件发生,客户端软件在尝试下载文件时收到错误消息。 “文件损坏”事件。 这意味着客户端正在尝试下载文件,但是在存储库中找不到该文件。 通常这些事件根本没有发生,或者以每天1-2千来衡量。

我必须立即说,至少要花1个星期的时间才能找到3人,有时5人(包括我)的团队失败的原因。

我们收集了发生“文件损坏”事件的文件的完整列表。

尽管有超过一百万个事件,而且这些事件都是来自不同的用户,不同的公司,但此列表中只有250个唯一文件。

事件到达时,备份服务器上的DBA得以提升以调查数据库备份。 项目库中有很多表格,其中包含各种日志,这些表格有助于分析。 即使从数据库中删除信息,也会将日志添加到已删除的内容和事件中。 在产品上,此类记录将存储1周,然后合并到存档服务器上。

带有日志的表也有助于分析发生的情况:
每个分片上都维护有包含客户端事件的完整日志

在全局服务器上:

  • 用户下载所有文件的请求日志
  • 从用户日志上传文件到系统
  • FileCorrupt事件日志
  • 日志文件取消从存储中删除
  • 从数据库中删除文件的日志

此外,还有带有应用程序日志的ELK。

我们设法在开发环境上重复该错误,这证实了假设的正确性。 最初,没有人认真对待这个假设,因为很难相信有这么多因素同时发生,并且有多少用户在这个特定时间来过。

怎么了?


事实证明,该系统具有约250个(相对于该系统中的数十亿个文件)超级骗子超级流行文件。 250是!

这些文件仍然很旧。 在将这些文件上载到系统时,使用了另一个系统来生成存储的文件密钥。

事实证明,对于这种类型的密钥,从物理存储中物理删除的方法的行为与其他文件不同。

在带有删除的类中,有一个代码块,其条件专门针对具有旧密钥的文件。 在删除文件之前,系统会先检查该文件是否位于碎片上,然后再将该旧文件移动到另一个位置。 好吧,那没有奏效。

事实证明,在移动文件的那一刻(我会提醒它很受欢迎),如果其中一位用户试图赋予他新的用户权限,则客户端软件将转到该文件的存储位置,但该文件不在正确的位置。 由于它已移动,所以无法解决。 然后客户端软件发送一条消息,指出文件已损坏。 在数据库中,它被标记为已损坏。 并且所有信息都将从数据库中删除(嗯,几乎所有信息)。

同时,我们的分片检查例程发现文件正在使用中。 并发送您需要返回的响应。 但是所有信息都已从数据库中删除,无法返回。

有趣吧?

也就是说,在删除文件时,用户处于该文件移动的那段时间,检查了碎片,并且正是在这个时间点,用户发送了下载请求。

这就是-当您匹配最令人难以置信的比赛时,高负荷动作。



从意外中恢复并回滚所有内容后,我们确保来自用户的文件仍然有效,因为它们是从其他客户端的磁盘上还原的。

自然,在测试中一切都很好,因为在测试过程中,使用了最近5年使用的新密钥类型删除了较新的文件,这些文件在删除时不会转移到另一个存储位置。

我们的乐观情绪减弱了,我们决定不走最乐观的道路。

回顾性


我们决定需要为不同类型的存储添加测试
将负载添加到性能实验室,该性能在从存储中删除时使用调用
关闭著名的比赛条件
添加监视(尽管将在计划中,但不适合原始范围)

关于监控


他们决定立即进行监视,但是由于需要更快地部署,他逐渐淡出了背景。

为了进行监视,该项目使用Zabbix,ELK,Grafana,NewRelic,SQL Sentry和AppDynamics的测试版本。

在pef实验室中,有NewRelic和SQL Sentry。

我们已经测量了所有系统指标,因此,我们想测量业务指标。 我有通过Zabbix组织此类监视的经验-我们决定做同样的事情。

该方案在数据库中非常简单,可以创建一个表,供JOB在该表中收集必要的指标,以及一个将收集的指标上载到Zabbix的过程。

指标:

  • 全局删除队列中的文件数
  • 服务器排队的文件数
  • 从存储库发送到卸载程序的文件数
  • 删除文件数
  • 文件损坏事件的数量
  • 每个分片上要删除的文件数

在他们开始部署删除的新实现之前,已实施监视并将其分别部署到产品。

新解决方案


总的来说,我们认为暴饮暴食比不睡觉更好,并制定了新的计划。

  1. 检查没有人再使用该文件的同一碎片,并仅将未使用的文件传输到服务器;
  2. 传输到服务器时,请收集表中的所有文件,并在将文件放置在出队队列表中之前确认文件未在分片上使用;
  3. 使用文件并在系统中搜索时,将表中的出队队列标记为需要验证的文件;
  4. 仅返回没有搜索的文件;
  5. 搜索过的文件,重新检查碎片;
  6. 通常,请删除删除文件的过程中的检查,因为它应该很快就能解决-原则上使用的文件不应到达该文件;
  7. 考虑到在删除过程中,所有内容都将被拍打的文件删除,而不删除其中的信息。

部署期间的第6点包括在多个阶段中删除支票。 首先,他们离开了支票,然后一周后,公司雇员的文件上的支票全部关闭,两周后他们又关闭了支票。

您需要在代码中进行哪些更改?


同样,所有更改仅适用于数据库。

更改的规模是全球服务器上最大的:
添加一个中间表,该表中添加从分片下载的所有文件。
创建一个JOB来检查中间表上未在分片上使用的文件。

在已删除文件的队列中,添加带有上次访问文件的日期的字段,并添加索引。

找到可以访问该文件的所有过程-原来有5个过程。 向他们添加一个块,以更改队列表中的上次使用日期。 该日期每次都会更改,无论是否已填写。

将删除程序添加到文件发布过程中,以便仅显示使用日期为空的文件。

添加一个JOB来收集使用日期和检查的所有文件(需要10分钟的延迟,这是必需的,以便客户端软件可以将文件添加到分片中,通常最多需要2分钟,但决定安全播放)在所有分片上使用该文件。检查完成后,如果找不到文件,则重置使用日期,否则从队列中删除文件。如果在验证过程中使用日期已更改,则数据不会更改,因为假定在进行验证时可以将文件上传到已完成验证的分片中,并且需要新的文件验证周期。

在分片上:

在从带有已删除文件的表中删除文件的过程中,有必要添加一个检查文件是否未使用。该过程并没有太多失去其简单性和美观性-在DELETE输出中,他们只是添加了NOT EXISTS。

我们添加了一个JOB,它在后台与服务器上使用的表文件相撞。

测验


有关使用所有存储选项的方案已添加到集成测试中。
他们还编写了新案例以测试新的文件删除功能。

Perf Lab向全局服务器添加了负载。另外,我们添加了一个负载,该负载对应于从存储中删除文件。

部署


为应用程序准备了脚本,并为数据库的更改回滚了脚本。DBA滚动了脚本,结果表明,在负载测试期间,它们没有注意队列表上用于删除文件的锁。结果,我们没有固定索引,这不是最佳选择。

因此,有必要按范围禁用JOB检查,并根据部署新代码之前在系统中删除的文件手动分析和添加已删除文件的标识符。

结果


结果,作为部署的结果,新的已删除文件将在24小时内从存储中删除。
在启动新系统之前删除的文件是在备份时创建的,并已添加到生产队列中。

结果,删除了S3上2 PB的多余数据。专用客户端服务器上的文件也发生了同样的事情,现在它们在服务器上的位置与客户端上显示的位置一致。

队列表上的弯曲索引仍然保留在prod上,即在积压中更改索引的任务,但由于优先级更高的任务而稍有推迟。

Source: https://habr.com/ru/post/zh-CN427895/


All Articles