科学数据规模化迁移:Benchling的Schema模型性能优化
Benchling是一个统一的科学数据平台,允许科学家在复杂科学研究中协作、自动化工作并赋能AI。客户在我们的平台上存储大量数据,并在Benchling内部和自有基础设施中的许多应用程序中利用这些数据。客户数据必须以高性能和可扩展的方式访问,这一点至关重要。
在本文中,我们将探讨最近在存储和检索客户数据方式上的转变。通过迁移到更紧凑的结构,我们解决了与数据量增加相关的关键性能挑战。这种转变需要在速度和灵活性之间取得谨慎平衡,并采用分阶段方法以最小化对用户的干扰。
Benchling Schemas
Benchling系统的核心是Schemas,这是一个允许Benchling内部团队和客户配置各种数据形状的产品,定义了实体必须遵循的字段、属性和约束。这些数据结构代表了设备、存储、生物分子、工作流程、任务、实验室笔记和科学测试记录结果等实体。Schemas驻留在我们称之为定义层的部分。
每个schema实例(称为可模式化项目)代表科学家输入的实际数据。我们称之为实例层。这些项目填充了符合schema定义字段的字段值。随着Benchling用户群的扩大和每年进入平台的模式化数据量的增加,优化字段值的存储变得至关重要。
挑战:规模与性能
历史上,Benchling见证了从科学家手动上传数据到与实验室设备集成的转变,从而实现了自动化数据收集。这显著提高了数据摄取的速度和量级。
检测结果(捕获实验数据)是Benchling中最常见的模式化项目。检测是用于测量样品中特定目标(如分子或生物实体)的存在、数量或活性的实验室程序。到2021年,检测结果摄取性能的下降表明,我们需要一种更可扩展的方法来存储字段值,以(1)提高数据摄取速度,(2)避免数据库扩展限制,特别是为了在不采用分片的情况下保持在PostgreSQL大小限制内。
旧世界:实体-属性-值模型(EAV)
Benchling最初采用了实体-属性-值(EAV)模型,这是一种灵活的数据模型,非常适合存储稀疏数据。在Benchling早期,数据量可控且访问模式不太明确时,这种模型非常有效。
然而,随着数据量的增加,EAV模型的几个缺点显现出来:
- 稀疏行:在EAV表中,每种数据类型都表示为一个单独的列,但每行通常只填充一个列。这导致了具有未使用列的稀疏行。
- 元数据开销:每行的Postgres元数据在规模上造成了低效率。每个属性都需要自己的行,导致n个实体和k个属性需要O(n*k)行。Postgres每行23字节的开销加剧了空间低效率。
- 低效的访问模式:大多数读取需要一次获取实体的所有属性。将这些属性分散在许多行中要求我们查询并返回许多行来读取单个实体。
- 不必要的连接:由于每个实体的属性存储在单独的行中,查询多个属性的匹配需要对EAV表进行多次连接。例如,在“名称”、“公式”和“重量”上查找实体匹配需要EAV表自身连接两次。
这些限制使得EAV模型对Benchling的需求不太实用。
新世界:使用PostgreSQL的JSONB
为了解决EAV的缺点,我们采用了PostgreSQL的JSONB数据类型,它支持高效的键值查询和索引。这使我们能够将实体的所有字段值压缩成存储在单行中的单个JSON blob。
这个新模型有几个优点:
- 紧凑的数据存储:JSONB格式通过将实体的所有属性存储在一行中,显著减少了行数。
- 改进的读写性能:由于大多数读取涉及检索实体的所有属性,查询单行比查询跨多行的属性要快得多。同样,上传实体时,向数据库写入一个大行比写入许多稀疏行更快。
然而,这个模型也引入了新的挑战:
- 查询字段信息:回答某些问题效率较低。例如,检查字段是否有非空值需要更复杂的查询来深入JSON结构。键值索引可以缓解这个问题。
- 锁争用:由于实体的所有字段都存储在一个JSONB文档中,如果一个进程更新“公式”字段,另一个进程更新“重量”字段,它们都在修改同一行。这可能会产生瓶颈,因为一次只能有一个进程锁定该行。相比之下,在旧的EAV模型中,不同的字段存储在不同的行中,因此争用较少。这种权衡被认为是可接受的,因为在Benchling当前的使用模式中,这种同时写入很少见。
为了缓解其中一些挑战,我们还考虑了替代方案,如GIN索引,以便在JSONB字段内进行更快的键值查找。具体来说,我们需要快速遍历实体链接,并回答诸如“哪些其他实体链接到给定实体?”等问题。
然而,虽然GIN索引加快了读取速度,但它们减慢了插入速度,因为每次插入新的JSONB文档时,PostgreSQL都需要更新索引。由于我们存储数百万条记录,不断更新GIN索引会成为瓶颈。由于我们已经在数据摄取速度上遇到瓶颈,我们选择了一种更简单的方法:添加一个表来跟踪实体之间的链接。
为每个链接插入一行确实会导致高开销,并且空间效率不如GIN索引,但这并不比我们之前的模型差,在之前的模型中,我们也在EAV表外部的单独表中存储链接。链接表允许我们对键进行微小更新,而不会触发整个JSONB结构的重新索引,减少了不必要的开销。此外,通过在链接表中记录属性名称,我们不仅可以高效查询哪些项目链接在一起,还可以查询建立链接的特定属性。
性能改进
推出新模型后,正如预期的那样,我们看到了批量读写性能的提升。这在我们数据仓库和笔记本表格中的结果摄取等领域都带来了性能改进。
以下是我们内部测试和生产环境汇总数据的指标样本:
- 检测结果摄取速度最高提升7倍
- 从内部数据库映射项目到数据仓库模型的速度提高33%
- 当序列更新时,在数据仓库中查找需要更新的项目的速度提高60%
- 在我们的Analysis产品中查询实体速度大约提高2倍
性能结果可能因数据量、工作负载模式和系统配置而异。这里强调的改进在数据量较大的环境中最为显著。
数据完整性的渐进式推出
Schema字段值在Benchling中无处不在,需要新旧系统之间的数据一致性。我们首先将推出阶段应用于我们的结果产品,因为这是Benchling扩展最多并开始遇到摄取速度变慢的地方。为了克服书面测试的局限性(例如无法考虑生产中遇到的所有可能边缘情况),我们在三年内分几个阶段推出了这个重构。
阶段1:双重写入和完整性检查:每个值更新都写入旧表和新表。但是,我们仍然从旧表读取。在此阶段,我们每晚从旧表到新表进行回填,记录沿途发现的任何不一致之处。使用夜间完整性检查,我们修补了要么遗漏写入新表要么导致不一致写入的代码路径。
阶段2:切换到JSONB读取(仍进行完整性检查):在确信新表包含正确的字段值后,我们切换到从新表读取。在此阶段,我们仍然写入两个表,并继续在两个表之间进行夜间完整性检查。
阶段3:弃用旧EAV表:这是不归路。我们弃用了旧的字段值表并停止向其写入。任何访问旧字段值表的尝试都会引发错误,因为这比可能遗漏写入新表或从旧表读取损坏数据更可取。
挑战与解决方案
在整个迁移过程中,我们遇到了各种挑战,特别是在维护数据完整性和确保系统边界清晰方面。一些关键问题包括:
- 事务隔离:使用READ-COMMITTED隔离级别有助于防止大多数竞争条件,而不会导致进程阻塞。然而,一些竞争条件需要在Postgres的行级锁之上添加额外的咨询锁。
- 死锁管理:在新系统中,我们必须管理当多个进程更新重叠实体范围上的字段和相关数据时,由锁争用引起的潜在死锁。
- 类型系统灵活性:JSONB模型中不太严格的类型系统在输入验证和数据强制方面引入了新的复杂性。确保不同字段类型正确表示需要 meticulous 关注细节、更彻底的测试,以及产品与工程团队之间的密切合作,以就预期行为达成一致。
- 范围蔓延:对于如此重大的重构,存在超越一对一迁移来增强现有架构的诱惑。为了避免无休止地扩大项目范围,我们专注于少数几个能带来重大收益的改进。一个例子是重构我们如何检查可模式化项目之间的唯一约束违规。这种重构将重复检查的时间从几分钟减少到仅10-20毫秒,显著提高了拥有大型数据集的客户的性能。
为了克服这些挑战中的大多数,我们强调了彻底测试和清晰错误日志记录的重要性,以便及时识别和修复问题。
展望未来:构建弹性和规模
为了跟上我们平台日益增长的需求,我们专注于几项增强功能,这些功能将使Benchling更加高效、有弹性并为未来做好准备。以下是我们如何准备:
- 更清晰的性能监控:关于产品性能的透明度是打造卓越用户体验的第一步。我们的当务之急之一是实施更好的指标,这些指标可以让我们更深入地了解系统性能,无论是在细粒度级别(如单个字段值访问时间)还是在更广泛的应用程序中。这将帮助我们快速识别任何瓶颈并主动解决它们。
- 重构字段访问模式:尽管我们的数据层和应用程序逻辑之间存在分离,但更改数据层的实现确实会影响应用程序层。随着将模式化字段值存储为JSON blob的转变,我们有机会优化Benchling不同团队访问这些值的方式,以利用新的表示形式,这可以减少锁争用并提高整体系统性能。
- 加强系统边界:模块化代码是在不断增长的团队和快速扩展的产品中实现快速开发的关键。我们计划构建更清晰的接口并在模块之间强制执行更严格的契约。这将有助于减少未来迁移的复杂性并确保更可维护的代码。
关键要点
从这个迁移项目中,我们学到了几个宝贵的经验:
- 数据建模中的权衡:虽然新的基于JSONB的模型解决了许多性能和存储问题,但它在查询和锁争用方面引入了挑战。仔细评估这些权衡以确保收益大于缺点非常重要。
- 渐进式推出最小化风险:分多个阶段推出新的数据模型使我们能够处理边缘情况并确保数据完整性。这种分阶段的方法对于触及系统关键部分的大规模迁移至关重要。
- 健壮测试和监控的重要性:单元测试、集成测试和实时完整性检查的结合是识别和解决系统问题的关键。
- 跨团队协作至关重要:由于字段值在各种团队和工作流程中被访问和更新,与产品经理和其他工程团队合作至关重要。确保新模型顺利集成到系统的所有部分需要清晰的沟通和协调。
为规模构建
从实体-属性-值模型迁移到更紧凑的基于JSONB的表示,确保了Benchling能够随着其不断增长的数据摄取需求而扩展。虽然新模型显著提高了读写性能,但它也需要仔细规划以管理所涉及的权衡,如锁争用和查询复杂性。我们分阶段的迁移方法使我们能够在问题出现时捕获并解决问题,确保了平稳过渡,对用户的影响最小。
展望未来,我们将通过改进性能指标、优化数据访问模式并确保我们的基础设施能够支持Benchling的持续增长来继续完善系统。在此迁移过程中学到的经验不仅加强了我们的工程实践,还为未来更可扩展和高效的数据模型奠定了基础。通过专注于协作和持续改进,我们已准备好应对未来的挑战。
更多精彩内容 请关注我的个人公众号 公众号(办公AI智能小助手)
对网络安全、黑客技术感兴趣的朋友可以关注我的安全公众号(网络安全技术点滴分享)
公众号二维码

公众号二维码

