用自适应率限制保护后QL
#开源 #database #性能 #microservices

自成立以来,即使是三十年来,PostgreSQL仍在继续吸引人,在迅速发展的开源项目的环境中蓬勃发展。尽管某些技术出现并迅速消失,但其他技术(例如PostgreSQL数据库)证明了它们的寿命,这说明它们可以承受时间的考验。从一般数据存储到小行星跟踪数据库,许多组织已成为许多组织的首选选择。公司正在使用petabytes of data运行PostgreSQL群集。

在生产环境中大规模操作后Qupersql可能具有挑战性。公司遇到了停机时间和绩效问题,导致财务损失并减少了信任,尤其是在停电超过几个小时的情况下。一个典型的例子是2017年1月的GitLab database outage。尽管有很多属性,这是如何发生这种情况的发生,但要强调超负荷如何发挥重要作用,在时间表中,他们解释了控制超负荷发生的时间。时间,他们花费了数小时才能控制它。

- ±19:00 UTC: gitlab.com由于我们怀疑是垃圾邮件而引起的数据库负载的增加。在此次活动开始的一周中,Gitlab.com遇到了类似的问题,但并没有那么严重。该负载引起的问题之一是,许多用户无法就问题和合并请求发布评论。控制负载花费了几个小时。

在分析为什么吉特拉布(Gitlab)停下18个小时时,他们将原因之一称为:

数据库负载增加 - 这是由同时发生的两个事件引起的:垃圾邮件增加和试图删除Gitlab员工及其相关数据的过程。

可以通过适当的数据库保护来避免这些中断和性能问题。在此博客中,我们将探讨PostgreSQL所面临的常见问题,讨论PostgreSQL需要什么,并深入研究Fluxninja使用Aperture如何实现的。

PostgreSQL和微服务

PostgreSQL是一个关系数据库,构成了许多基于微服务的应用程序的骨干。但是,仔细管理其性能以及对相关服务的行为的理解对于保持系统的稳定性和弹性至关重要。

当今大多数基于微服务的应用程序都采用缓存机制来减少数据库的负载。仅当缓存中找不到数据时,该请求才能访问数据库。在正常操作下,这可以提高效率和性能。

但是,当数据库经历放缓或过载时,这种有效的和谐可能会被中断。沿着类似的线,过去,HoneyComb.io experienced a partial API outage因此。

数据库性能动摇后,缓存条目开始以更高的速度到期。反过来,这引发了goroutines的显着激增,试图同时刷新缓存,这导致了耗资资源耗尽的反馈回路,从而产生了全系统范围的应变。结果是传播到各种微服务的“火”。

在此事件中,这些是基本发现,

  • 缓存是一把双刃剑
  • 最好知道高速缓存刷新行为,即限制并发缓存刷新操作的数量可以防止反馈循环并保持系统稳定,即使在数据库上增加负载期间。
  • 设置数据库的主动警报和可观察性
  • 通过尽早确定潜在的问题,可以实施迅速的纠正措施,以防止小问题滚雪球。

让我们检查PostgreSQL在多租户设置中的表现;多租户体系结构封装在共享的PostgreSQL数据库集群中,封装了不同的租户,类似于单独的用户或应用程序。在应用程序级别隔离运行时,每个租户都争夺相同的系统资源 - 在数据库级别上,CPU周期,内存,磁盘I/O-。这里的挑战是绩效隔离:确保一位租户的资源密集型操作不会阻碍其他人所经历的表现。

在高负载方案或同时执行昂贵的查询期间,租户可以垄断共享资源,从而导致其他人的绩效降低。管理并发成为一项复杂的任务,需要仔细分配共享资源以维持系统性能。有关类似问题的更多信息,您可以研究Cloudflare面临的挑战。但是,这些问题可以通过PostgreSQL保护和每个租户的配额来解决。

我们将在下一节中探索更多有关PostgreSQL保护的信息。在此之前,让我们了解通常会降低性能的常见后Ql问题。

常见的后Ql问题

  • 最大连接:超过最大允许的连接可能会导致性能滞后。太多的同时客户连接通常会引起这一点。连接池可以有所帮助,但连接耗尽可能仍会发生。
  • 记忆和CPU用法中的尖峰:几个因素可以导致高内存和CPU使用:
    • 大或复杂的查询。
    • 大量的同时连接。
    • 资源密集的背景过程。
    • 多个服务同时刷新其缓存。
  • 高响应延迟:高CPU使用情况会延迟PostgreSQL的响应时间,从而影响服务的可靠性和用户体验。当与CPU尖峰结合使用时,该延迟可能会导致系统故障并删除连接。
  • 优化的查询:这些可以垄断联系,从而导致连接饥饿。一个优化的查询不足以引起瓶颈,多个此类查询会加剧问题。 github outage in May 2023和Semaphoreci中断是效率低下的疑问的示例。
  • 损坏的索引:这可能导致查询结果不准确或减慢数据检索。它还可以触发不必要的全桌扫描,扭曲CPU和内存资源。
  • 嘈杂的邻居问题:在多租户的后QL设置中,当一个租户的高资源使用会影响他人的性能时,就会出现此问题。诸如手动并发限制和负载脱落之类的技术可以帮助管理这一点。 Cloudflare case是成功处理此问题的一个例子。

我们讨论过的性能问题在PostgreSQL中很常见,各种策略和工具可以帮助解决方案。一种这样有效的工具是光圈。现在,让我们探索Fluxninja如何成功地驾驶这些孔子。

Fluxninja与PostgreSql的战斗

FluxNinja弧是由Fluxninja设计的基于云的解决方案,可增强光圈平台的功能。它提供了一个直观的界面,可简化跨各个簇运行的光圈系统的管理。

fluxninja Arc是光圈的接口,提供了关键功能,包括用户友好的接口,流量洞察的流量分析,警报系统,可视化工具和简化的策略构建器UI。 Aperture本身是一个高级负载管理平台,强调可观察性,具有自适应服务保护,智能配额管理,工作负载优先级和基于负载的自动缩放的功能。

让我们专注于与PostgreSQL互动的服务,而不是深入研究整个云体系结构的广泛范围。

Fluxninja Cloud有两种服务,API服务和代理服务。与UI,组织,登录并注册以及类似功能的API服务。代理服务从不同群集上运行的代理和控制器收集心跳和最后同步状态。它还收集了控制者附加的政策的详细信息。但是,这两种服务都不会直接与PostgreSQL进行交互,而是Hasura介导了两者之间的连接。 Hasura在PostgreSQL和服务之间的存在带来了巨大的好处,因为它可以卸载各种任务,例如可观察性,授权和SQL Workflow的简化。话虽这么说,让我们陷入我们与Postgresql&Hasura遇到的问题。

FluxNinja Cloud Architecture

在调查绩效问题时,我们观察到API和代理服务的要求太多了。这导致

  • 请求激增和延迟增加,导致用户体验差
  • 虽然Hasura充当GraphQl引擎,但在某些情况下,它可以成为性能瓶颈,努力处理突然的请求涌入并且无法应付流量的数量。

Aperture Grafana Dashboard

该图来自Grafana仪表板,说明了单个工作负载的延迟和工作负载率。潜伏期尖峰超过250ms,在高负载条件下的接受率降低。

要解决这些问题,我们考虑了两个解决方案:

  1. 缩放: - 缩放可以消除hasura充当瓶颈。
    • 如果我们扩展太多而无法处理更多请求,我们最终可能会对PostgreSQL施加太大的压力。将相同的重载直接发送到Postgresql,可能会超载。
  2. 限制速率:
    • 为了防止PostgreSQL不知所措,我们可以限制传入的请求率。
    • 这种方法的缺点是:它将对PostgreSQL的所有请求进行惩罚,而无需工作负载优先级。它没有区分高优先级和低优位的工作量。
    • 这意味着:
      • 低优先级请求可能会干扰高优先级。
      • 如果低优先级请求更多,那么高优先级的工作负载可能会延迟。因此,可以更频繁地对高优先级任务进行惩罚。

可以在多租户的环境中设想这种工作负载优先级的概念,在这种环境中,每种服务的行为类似于租户,一个租户的优先级可能比另一个租户更高,类似于另一个服务。例如,由于代理服务,我们不希望API服务在PostgreSQL的请求资源中饿死。

API服务请求应优先于代理服务,以便为使用云产品的个人提供最佳的用户体验。

解决方案:孔径PostgreSQL保护

手头的挑战是制定一种策略,该策略可以优先考虑工作负载,减轻过载后Ql的风险,防止Hasura成为瓶颈,并保持用户体验一致。

光圈似乎是解决这些挑战的理想解决方案,有几个令人信服的理由加强了我们对其适合性的信念。

  • 孔径可以在需要时进行并发性节流。
  • 它是围绕避免拥塞的类似观念而建立的;避免拥堵的人没有反应地降低负载。
  • 对于多租户环境,我们可以确保资源消耗并检测到配额超过配额的租户,并在配额管理的帮助下进行查询。

所有这些都是可能的,因为自适应负载计划。

什么是孔径自适应负载调度(ALS)?

ALS旨在通过动态调整请求率来保护服务。它通过分析诸如延迟和错误率(例如JMX和DB连接)等各种健康信号来做到这一点。

它还可以实现工作负载优先级。请求通过声明性规则进行分类和标记,使调度程序能够确定不同任务的关键性。算法(例如令牌存储桶和加权排队)用于优先考虑关键请求,而不是背景工作负载,确保系统稳定性和有效的资源利用率。

PID controllers的启发,Aperture是一个闭环系统,利用算法,例如TCP BBRAIMDCoDel。它与自动缩放和负载平衡系统无缝互动以确保最佳性能。

光圈政策

孔径由政策提供动力。这就是定义控制电路图的原因。

光圈策略
孔径中的政策是一种编程方式定义系统应遵循的条件和行动以保持其稳定性的方法。这些策略会定期评估,如果检测到与所需行为的任​​何偏差,则采取适当的措施来纠正它们。将其视为系统的rulebook,可以帮助它做出决策并保持顺利运行。了解有关官方documentation的政策的更多信息。

Hasura自动规模政策

要从瓶颈的图片中删除hasura,随着负载增加而缩放它很重要。除了自动刻度组件外,还可以通过Service Protection with Average Latency Feedback蓝图进行操作。使用此策略的原因是,它通过将实时延迟与指数移动平均值进行比较来检测流量过载和级联故障的积累。它可以充当精确的信号,即在何时进行自动尺度。该策略是用在源和操作类型请求上配置的标签匹配器配置的latency baseliner定义的。这意味着,如果延迟与EMA偏离,那么它将尝试发送一个信号以进行超载,自动刻度将作用。

以下是策略的电路图;基于电路,从自适应负载调度程序组件中将信号值传递给自动缩放组件以进行自动缩放决策。在服务保护巡回赛的同时,帮助我们获取所需的所有指标和信号。另一种方法是通过基于PROMQL的服务保护,但平均延迟反馈似乎与当前情况更相关。

在超负荷和突然的尖峰期间,光圈检测到与EMA的潜伏期偏差,该ema将充当信号(所需的负载乘数)以扩展到缩放,直到它返回应为状态。 <。 < /p>

Hasura自动规模策略
# yaml-language-server: $schema=../../../../../../aperture/blueprints/policies/service-protection/average-latency/gen/definitions.json
policy:
  policy_name: auto-scaling-hasura
  components:
    - auto_scale:
        auto_scaler:
          dry_run: false
          dry_run_config_key: dry_run
          scale_in_controllers:
            - alerter:
                alert_name: Periodic scale in intended
              controller:
                periodic:
                  period: 60s
                  scale_in_percentage: 10
          scale_out_controllers:
            - alerter:
                alert_name: Load based scale out intended
              controller:
                gradient:
                  in_ports:
                    setpoint:
                      constant_signal:
                        value: 1
                    signal:
                      signal_name: DESIRED_LOAD_MULTIPLIER
                  parameters:
                    slope: -1
          scaling_backend:
            kubernetes_replicas:
              kubernetes_object_selector:
                agent_group: default
                api_version: apps/v1
                kind: Deployment
                name: hasura
                namespace: cloud
              max_replicas: "10"
              min_replicas: "1"
          scaling_parameters:
            scale_in_alerter:
              alert_name: Hasura auto scaler is scaling in
            scale_in_cooldown: 40s
            scale_out_alerter:
              alert_name: Hasura auto scaler is scaling out
            scale_out_cooldown: 30s
  resources:
    flow_control:
      classifiers:
        - selectors:
            - service: hasura.cloud.svc.cluster.local
              control_point: ingress
          rego:
            labels:
              source:
                telemetry: true
              operation:
                telemetry: true
            module: |
              package hasura_example
              source = input.attributes.source.source_fqdns[0]
              operation = graphql.parse_query(input.parsed_body.query).Operations[_].Operation
  service_protection_core:
    dry_run: true
    adaptive_load_scheduler:
      load_scheduler:
        selectors:
          - control_point: ingress
            service: hasura.cloud.svc.cluster.local
        scheduler:
          workloads:
            - label_matcher:
                match_labels:
                  source: "api-service.cloud.svc.cluster.local"
              parameters:
                priority: "250"
              name: "api-service"
            - label_matcher:
                match_labels:
                  source: "agent-service.cloud.svc.cluster.local"
                  operation: "mutation"
              parameters:
                priority: "100"
              name: "agent-service-mutation"
            - label_matcher:
                match_labels:
                  source: "agent-service.cloud.svc.cluster.local"
                  operation: "query"
              parameters:
                priority: "50"
              name: "agent-service-query"
  latency_baseliner:
    latency_tolerance_multiplier: 1.1
    flux_meter:
      selectors:
        - control_point: ingress
          service: hasura.cloud.svc.cluster.local
          label_matcher:
            match_labels:
              operation: "query"
              source: "api-service.cloud.svc.cluster.local"

FluxNinja Auto Scaling Policy Circuit

PostgreSQL服务保护政策

为了屏蔽PostgreSQL免受超负荷和突然的尖峰的影响,我们创建了一个PostgreSQL Protection Blueprint。蓝图已经通过预先配置的远程计算机进行遥控器来进行繁重的工作。 INFRAMETER配置为添加OpenTelemetry Collector,请阅读更多有关Fedding custom metrics in Aperture.

的信息

蓝图围绕两个关键指标旋转:

  • max postgresql上的连接:
    • promql查询: (sum(postgresql_backends) / sum(postgresql_connection_max)) * 100
    • 此查询计算PostgreSQL当前正在使用的最大连接的百分比,从而提供了对连接负载的实时见解。
  • CPU超负荷确认:
    • promql查询: avg(k8s_pod_cpu_utilization_ratio{k8s_statefulset_name="hasura-postgresql"})
    • 该查询用于跟踪位于Kubernetes statefulSet中的kude7中的PostgreSQL服务的CPU利用率。当检测到潜在的CPU超载时,该度量将用作激活自适应负载调度程序的信号。

可以根据问题要求重写这些查询;例如,如果您使用部署而不是statefulset,请使用k8s_deployment_name指标来配置它。

下面是该策略将如何工作的电路图。它非常简单,它将使用PromQL查询来评估确认信号和设定点,该点是最大连接的百分比,以启用自适应负载调度程序。在正常条件下,所有工作负载都得到了应有的份额。

FluxNinja PostgreSQL Protection Policy

PostgreSQL保护政策
# yaml-language-server: $schema=../../../../../../aperture/blueprints/policies/service-protection/postgresql/gen/definitions.json
policy:
  policy_name: workload-prioritization-postgres
  setpoint: 70
  postgresql:
    endpoint: hasura-postgresql.cloud.svc.cluster.local:5432
    username: postgres
    password: DevPassword
    collection_interval: 1s
    tls:
      insecure: true
  resources:
    flow_control:
      classifiers:
        - selectors:
            - service: hasura.cloud.svc.cluster.local
              control_point: ingress
          rego:
            labels:
              source:
                telemetry: true
              operation:
                telemetry: true
            module: |
              package hasura_example
              source = input.attributes.source.source_fqdns[0]
              operation = graphql.parse_query(input.parsed_body.query).Operations[_].Operation
  service_protection_core:
    dry_run: false
    cpu_overload_confirmation:
      query_string: avg(k8s_pod_cpu_utilization_ratio{k8s_statefulset_name="hasura-postgresql"})
      threshold: 2.1
      operator: gte
    adaptive_load_scheduler:
      load_scheduler:
        selectors:
          - control_point: ingress
            service: hasura.cloud.svc.cluster.local
        scheduler:
          workloads:
            - label_matcher:
                match_labels:
                  source: "api-service.cloud.svc.cluster.local"
              parameters:
                priority: "255"
              name: "api-service"
            - label_matcher:
                match_labels:
                  source: "agent-service.cloud.svc.cluster.local"
                  operation: "mutation"
              parameters:
                priority: "100"
              name: "agent-service-mutation"
            - label_matcher:
                match_labels:
                  source: "agent-service.cloud.svc.cluster.local"
                  operation: "query"
              parameters:
                priority: "50"
              name: "agent-service-query"

当过载在PostgreSQL上发生时,策略会通过工作负载优先级执行自适应加载计划,同时确保最大连接的%不应超过70(策略中的定义设置点)。

工作负载优先级将确保API服务请求优先于对PostgreSQL的代理服务请求,这是使用Label Matcher来实现的,Label Matcher在策略Source中定义了。

基于操作类型的工作负载优先级也是类似的,它使用标签匹配器Operation来确定代理服务请求的请求优先级。当多个请求来自代理服务时,突变请求将优先于查询请求。最重要的是,API服务获得了最高优先级

要注意的一件事:使用策略中定义的分类器提取操作类型标签;策略中定义了使用rego模块提取操作类型的分类器。

因此,按照优先级的顺序。 API服务请求>代理服务Mutation Operation>代理服务Query

为什么要通过查询请求突变?

查询操作是读取操作,而突变操作可以写入,更新或删除数据。在Fluxninja的情况下,代理服务的突变操作发生在一系列查询操作后。为了防止在上述查询期间丢弃已经完成的工作,突变操作比查询更优先。此优先级确保了在上述查询上投入的努力不会浪费。

这样,光圈可确保在高负载时系统应如何反应。策略中定义的所有组件共同努力,在过载情况下提供后GresQL保护,同时确保尊重高优先级的工作量。

所有这些都是在请求到达PostgreSQL或Hasura之前完成的,这可以在过载情况下节省资源。

注意:在策略的行动时,可以根据控制点轻松识别它。

行动中的政策

FluxNinja Arch with Aperture Policy

Hasura的政策在EMA边界内保持延迟。如果延迟偏离,它将扩展Hasura以管理请求的涌入。第二项政策监视最大连接使用情况不会超过70%。如果确实如此,并且CPU使用率很高,则Aperture通过工作负载优先级启动自适应负载调度,以确保维持用户体验。

Grafana Dashboard

该图来自Grafana仪表板,显示了实施的孔径策略的单个工作负载的延迟和工作负载率。潜伏期保持在所需的范围内,即使在高负载期间,也要接收率仍然很高。

通过利用孔径的功能并使用这些政策,Fluxninja实现了有效的后QL保护,解决了性能问题,并确保了用户的体验,即使在突然的交通峰值期间也是如此。

挑战

  • 识别适当的验证性信号,以在不同情况下有所不同。在我们的情况下,事实证明有用的常见是获得了使用的最大连接和跟踪CPU使用的百分比。
    • 高CPU使用情况可能表明执行昂贵的查询或许多开放连接。
  • 收集两个指标,即连接数量和CPU的使用数量,在很大程度上阐明了情况。尽管这些不是最详尽的指标,但它们为我们的目的提供了充分的目的。
  • 确定正确的指标是一项任务,尤其是当将这些指标用作策略中的信号时。对我们来说,使用遥测收集器的使用简化了这种情况,该收集器收集了所有所需的指标。但是,例如,对于更高级的方案,确定哪个查询价格昂贵或耗时,可能需要进行其他思想和计划。

结论

孔径在Fluxninja基础架构中运行,从而保护了突然的尖峰和过载场景。尽管目前专注于保护PostgreSQL,但将来我们肯定会有更多的机会解决其他问题。我们在为下一代可靠性团队设计的产品上的工作中,光圈的有效性已经很明显。

在撰写此博客并研究各种验尸和报告的过程中,很明显公司需要像光圈这样的解决方案。这些见解强调了孔径在与许多组织博客中突出的问题面对面的重要性。

要了解有关光圈的更多信息,请访问我们的GitHub repositorydocumentation site。您也可以加入我们的Slack community,讨论最佳实践,提出问题并进行有关可靠性管理的讨论。

有关PostgreSQL保护和相关主题的进一步阅读,我们建议探索以下资源:

  1. Aperture Documentation:更深入地深入了解孔的功能和功能,以进行有效的负载管理和保护。
  2. Postmortem: RDS Clogs & Cache-Refresh Crash Loops | Honeycomb
  3. Addressing GitHub’s recent availability issues
  4. Performance isolation in a multi-tenant database environment