为什么规划数据库容量这么难?那么如何简化呢?您可以使用开源的NoSQL数据库ScyllaDB来演示示例。调整数据库的大小似乎很简单:将数据集的大小和所需的吞吐量除以节点的容量。很简单,不是吗?任何曾经尝试过调整数据库大小的人都知道这有多难。即使进行粗略估计也可能具有挑战性。那为什么这么难呢?以下是估计集群大小的步骤:(1)对使用模式做出假设。(2)估算所需的工作量。(3)确定数据库的高级配置。(4)为数据库的性能模型提供工作负载、配置和使用模式。(五)收入。虽然这个过程很容易理解,但要在实践中实践起来却不是那么容易。例如,在做出有关复制因子和一致性级别等数据库配置的决策时,所做的决策会受到先入为主的答案的影响。当成本变得非常昂贵时,进行一些重复似乎有点矫枉过正。将数据库大小调整视为设计过程需要意识到它是迭代的并且支持发现和研究需求和使用。与任何设计过程一样,优化规模在经济上和操作上都是不可取的,并且其时间和资源投资是有限的。在设计过程中,简单性和成本与准确性之间存在固有的权衡。毕竟,复杂模型可以更好地预测数据库性能,但可能与构建数据库本身一样昂贵,并且需要如此多的输入,以至于其应用不切实际。出现什么问题?数据库的工作负载通常根据查询的吞吐量和它必须支持的数据集的大小来描述。这就提出了一些问题:?这个数字是最大吞吐量还是平均值?(工作负载通常有周期性变化和峰值)?某些类型的查询是否应该分开?(例如读和写)?如果还没有使用这个数据库,那么你如何估计查询的数量?数据集有多大??什么是流行的数据集?数据库存储的数据远远超过它们在任何给定时间点所能提供的数据。?数据模型如何?我们从经验中知道,数据模型对查询数量、性能和存储大小有很大影响。?预期增长是多少?想要构建一个可以随工作负载扩展的数据库。?查询的服务级别目标(SLO)是什么?设计的延迟目标是什么?有些人足够幸运,拥有一个可用的系统,也许还有一个功能正常的数据库,他们可以从中轻松提取或推断这些问题的答案。但通常情况下,有些人被迫使用猜测和粗略计算。这并不像听起来那么糟糕。例如,您可以使用蒙特卡洛工具查看此模型,如下图所示。估算工作量很有趣。但是出于简单的评分目的,人们对数据集的吞吐量和最大值感兴趣,并且只会区分两种查询:读取和写入,原因稍后解释。还可以假设对数据模型有高度的控制。对于任何NoSQL数据库,目标都是处理一次查询所需的所有数据——如果用户想要优化读取或写入,则需要做出决定。通常的基本步骤是:(1)估计峰值数据集大小和工作量。(2)初步绘制数据模型,对优化目标进行高层决策。(3)从数据模型中估计读写比。构建数据库的性能模型数据库的性能模型必须在一些有时相互冲突的需求之间取得平衡,它必须考虑足够的性能和容量安全裕度,但需要在成本、可靠性和性能、持续和峰值负载之间取得平衡实现平衡,但仍然简化,即使在没有特定应用程序的情况下使用,同时提供明确的结果。这确实是一项具有挑战性的任务。但它可以简单得多。例如,下面是它如何与Scylla一起工作,Scylla是一个与ApacheCassandra兼容的开源NoSQL数据库。查询与操作工作负载是根据查询(通常是CQL)指定的。CQL查询可能非常复杂,并生成数量可变的基本操作,但性能相对可预测。以下面的CQL查询为例:SELECT*FROMuser_statsWHEREid=UUIDSELECT*FROMuser_statsWHEREusername=USERNAMESELECT*FROMuser_statsWHEREcity="NewYork"ALLOWFILTERINGQuery#1将使用主键来定位记录,因此将立即在正确的分区上执行——取决于在一致性级别,它仍然可以分解为多个操作,因为将查询多个副本(稍后会详细介绍)。虽然查询#2看起来非常相似,但它根据二级索引查找记录,分解为两个子查询:一个子查询到全局二级索引以查找主键,另一个子查询从分区中检索行。此外,根据一致性级别,这可能会生成多个操作。查询#3更加极端,因为它跨分区扫描;它的表现将是可怕的和不可预测的。另一个例子是UPDATE查询:UPDATEuser_statsSETusername=USERNAME,rank=231,score=3432WHEREID=UUIDUPDATEuser_statsSETusername=USERNAME,rand=231,score=3432WHEREID=UUIDIFEXISTSQuery#1可能会直观地分解为读写操作——但在CQLUPDATE中查询是UPSERT查询,只会产生1次写操作。然而,查询#2,尽管看起来很相似,但不仅需要在所有副本上进行先写后读,还需要编排轻量级事务(LWT)。类似地,查询生成的磁盘操作数也可能相差很大。大多数数据库在排序字符串表(SSTable)存储格式中使用日志结构合并树(LSM)结构。它们从不修改磁盘上的SSTable文件,它们是不可变的。写入会持久保存到仅追加的提交日志中以供恢复,并写入内存缓冲区(memtable)。当memtable变得太大时,它会被写入磁盘上的新SSTable文件并从内存中清除。这使得写入路径非常高效,但在读取时引入了一个问题:数据库必须在多个SSTable中搜索一个值。为了防止这种读取失控放大,数据库会定期将多个SSTable压缩到更少的文件中,只保留最新的数据。这减少了完成查询所需的读取操作数。这意味着实际上不可能将磁盘操作归因于单个查询。磁盘操作的具体数量取决于SSTables的数量、它们的排列方式、压缩策略等。开源NoSQL数据库都是为了存储空间最大化而设计的,所以这个维度可以忽略,只要磁盘速度足够快,而不是太小。瓶颈。这就是推荐快速本地NVMe磁盘的原因。虽然此示例侧重于CQL,但预测查询成本并不是唯一的问题。事实上,查询语言越丰富、越强大,其性能的可预测性就越低。例如,由于SQL的强大功能,其性能可能无法预测。因此,复杂的查询优化器是RDBMS的重要组成部分,它确实可以提高性能,但代价是使性能更加不可预测。这是NoSQL做出的另一个妥协:优先考虑可预测的性能和可伸缩性,而不是功能丰富的查询语言。一致性问题一个分布式的、可用的数据库需要将数据可靠地复制到多个节点。每个keyspace可以配置replicas的数量,称为replicationfactor。从客户端的角度来看,这可以同步或异步发生,具体取决于写入的一致性级别。例如,当一致性级别为1时,数据最终会写入所有副本,但客户端只会等待一个副本确认写入。即使某些节点暂时不可用,数据库也应该在这些节点可用时缓存写入和复制(这称为隐式切换)。实际上,可以假设每个写查询将在每个副本上生成至少一个写操作。然而,对于读取,情况有些不同。一致性级别为ALL的查询必须从所有副本读取,产生与集群的复制因子一样多的读取,但一致性级别为1的查询只从单个节点读取,使其更便宜,读取更快。这允许用户以一致性为代价从集群中挤出更多的读取吞吐量,因为节点在读取时可能没有最新的更新。最后,需要考虑轻量级事务(LWT)。如上所述,轻量级事务(LWT)需要使用Paxos算法编排所有副本。轻量级事务(LWT)不仅强制每个副本读取然后写入值,还需要保持事务的状态直到Paxos回合结束。由于轻量级事务(LWT)的行为不同,因此需要将其视为每个内核可以支持的吞吐量的单独性能模型。所有操作都是平等的,但有些操作比其他操作更平等现在您已将CQL查询分解为其基本操作,您可以提出一些问题:每个操作需要多少时间(和资源)?一个CPU核能支持的容量是多少??同样,答案有点复杂。举个例子,考虑一个简单的读取,并遵循节点中的执行步骤:(1)在memtable中查找值。(2)查找缓存中的值。(3)在SSTables中逐层查找并合并值。(4)响应协调员。显然,如果它们位于基于内存的memtable或行级缓存中,读取将完成得更快,这不足为奇。此外,步骤#3和#4可能会产生更高的成本,具体取决于从磁盘读取、处理和通过网络传输的数据的大小。如果需要读取10MB的数据,这可能需要相当长的时间。这可能是因为存储在单个单元格中的值很大,也可能是因为范围扫描返回了很多结果。但是,对于大值,Scylla无法将它们分解成更小的块,必须将整个单元格加载到内存中。通常,建议对数据进行建模,使分区、行和单元格不会变得太大,以确保性能差异较小。然而,在现实中,总会有一些差异。当涉及分区访问模式时,这种差异尤其显着。许多数据库用于跨节点扩展和传播数据的策略是将数据分块到彼此独立的分区中。但独立性也意味着分区可能会经历不均匀的负载,从而导致所谓的“热分区”问题,即单个分区可能会达到节点容量限制,尽管集群的其余部分有足够的资源。这个问题的发生很大程度上取决于数据模型、分区键在数据集中的分布以及键在工作负载中的分布。由于预测热分区通常需要分析整个数据集和工作负载,这在设计阶段是不切实际的,因此可以提供一些已知分布作为模型的输入,或者可以对分区的相对热度做出一些假设。另外,nodetooltoppartitions命令可以帮助定位热分区。物化视图、二级索引和其他数据库具有自动二级索引和物化视图以及变更数据捕获(CDC)。这些表本质上是数据库自己维护的辅助表,只需要一次写操作就可以将数据写成多种形式。在幕后,Scylla根据用户提供的模式导出新值进行写入,并将这些新值写入不同的表。在这方面,Scylla和RDBMS之间的主要区别在于派生数据是异步写入的,并且作为索引和物化视图跨网络写入,而不是仅限于单个分区。这是要考虑的另一个写入来源。在Scylla中,它是可预测的,并且在性能上与用户生成的操作相似。对于写入的每个项目,CDC和从写入单元派生的每个物化视图或二级索引都将触发写入。在某些情况下,实体化视图和CDC可能需要额外的读取,这可能会发生,例如,如果启用了CDC的“原像”功能。要记住的另一件事是一个CQL查询可以触发多个写操作。CDC、二级索引和物化视图作为由Scylla自身管理的常规表实现,但这也意味着它们消耗的磁盘空间与用户表相当,因此在容量规划中必须考虑到这一点。峰值和数据库维护所有数据库都需要进行各种维护操作。RDBMS需要清理重做日志(例如PostgreSQLVACUUM)、将快照转储到磁盘(请参阅Redis)或执行内存中垃圾收集(Cassandra、Elastic、HBase)。使用LSM存储的数据库(如Cassandra、HBase、Scylla)也需要定期压缩SSTables并将memtables刷新到新的SSTables。如果数据库足够智能,可以将压缩和修复等维护操作推迟到以后的负载较少的时间,则可以在短时间内实现最佳性能。然而,最终,必须为这些维护操作预留资源。这对于负载在一天中分布不均的大多数系统很有用。但是,企业的计划应该为数据库的持续长期运行提供足够的容量。此外,仅基于吞吐量进行规划是不够的。在某种程度上,可以使数据库过载以获得更高的吞吐量,但代价是更高的延迟。基准往往具有误导性,因为它们往往太短而无法有意义地持续下去。延迟/吞吐量权衡通常更容易衡量,甚至可以在较短的基准测试中观察到。另一个重要但经常被忽视的问题是降级操作。作为本地冗余和高可用的数据库,Scylla旨在优雅地处理节点故障(根据用户设置的一致性约束)。然而,虽然故障在语义上是一致的,但这并不意味着它们是动态透明的,容量的显着损失会影响集群的性能,以及故障节点的恢复或更换。在调整集群大小时也需要考虑这些因素。选择适当缩放的节点由于可以通过添加节点或选择更大的节点来增加Scylla的容量,因此出现了一个有趣的问题:应该选择哪种缩放策略?一方面,更大的节点效率更高,因为它们可以独立为Scylla服务的内核分配CPU核心来处理网络和其他任务,并减少节点协调的相对开销。另一方面,您拥有的节点越多,当其中一个节点发生故障时,您损失的部分容量就越少——尽管丢失一个节点的概率略高。对于非常大的工作负载,解决这个问题没有意义,因为大节点是不够的。但对于许多较小的工作负载,3个大中型节点就足够了。这个决定与工作量有关。但是,对于可靠性降级操作,建议至少运行6-9个节点(假设复制因子为3)。结论容量规划和调整集群规模可能是复杂且具有挑战性的。本文讨论了如何考虑安全裕度、维护操作和使用模式。重要的是要记住,任何猜测都只是迭代的初始估计。一旦投入生产并获得真实数据,它就可以指导容量规划。
