欢迎阅读新一期的数据库内核杂谈。这期杂谈,不聊数据库,咱们插入一个番外篇,聊一下多租户(multi-tenant)应用的系统设计。对于多租户系统的思考,源于最近的工作,再结合内核杂谈前几期阅读的关于 AWS DynamoDB 的论文。我的工作主要是构建云端的服务来支持整个自动驾驶产品的迭代,可以认为是为用户提供 SAAS 服务。这些服务的质量和稳定性,本质上都可以通过引入正确的多租户系统架构来解决。
什么是多租户(multi-tenant)系统架构
多租户系统是一种软件架构:一个服务实例会同时为多个客户或用户(称为租户)提供服务。这种方法允许在租户之间高效共享计算资源、基础设施和服务,同时保持数据隔离和安全性。多租户架构的好处主要在:1)节约成本,资源的共享可以降低供应商和租户的基础设施和维护的成本;2)简化管理,集中执行更新、安全补丁和维护任务,从而使所有租户一次受益并减少管理开销。此外,多租户应用程序还为可扩展性而设计,使系统能够随着租户数量及其资源需求的增长而增长。这确保了在适应多样化客户群体的不断变化需求的同时,能够实现最佳性能和资源利用。
绝大部分(如果不是全部的话)公有云的 SAAS 服务基本都属于多租户应用。比如 AWS 的 DynamoDB 服务,是一款非常受欢迎的 key-value 的 NoSQL 系统。对每个用户而言,DynamoDB 服务就是一堆简单的调用接口。不同用户之间完全不会感知到彼此的存在,这些用户也不会互相影响(参考 https://www.infoq.cn/article/aEUY5kcI1a3iqGUyGzUy)。
设计和开发多租户系统的挑战
天下没有免费的午餐。虽然能带来成本节约和简化管理,但设计多租户系统仍面临着很多挑战。因为开发人员必须在确保效率、安全性和可扩展性的同时,平衡多个租户的需求。主要的挑战在于:
数据隔离:确保数据的隔离(安全)在多租户应用中至关重要,因为租户的数据必须保持独立且无法被其他租户访问。设计一个能够有效隔离每个租户数据的同时仍允许高效资源共享的系统会很复杂。这需要实施严格的权限控制、面向租户的数据访问和加密,来保护敏感信息。
保持性能稳定:多租户应用程序必须为所有租户提供一致的性能,无论用户数量或资源消耗程度如何。这需要设计一个可扩展且稳健的架构,能够处理可变的工作负载并根据需求自动调整资源。必须采用监控和性能优化技术(如负载均衡和缓存)来确保系统顺畅运行。
合理管理资源:和保持性能相关,有效分配和管理资源(如计算、存储和网络容量)对多租户应用至关重要。开发人员必须实施资源配额、限流和监控策略,以确保公平分配资源并防止单个租户占用过多资源。
提供定制:不同的租户可能在功能、外观或配置方面有独特的要求。设计一个支持定制和可扩展性的多租户应用可能具有挑战性。开发人员必须创建模块化和灵活的架构,允许租户根据其特定需求定制应用程序,而不影响整个系统的稳定性或性能。
租户生命周期管理:在多租户环境中处理租户的入驻、升级、降级和退出会非常复杂。开发人员需要建立管理租户生命周期事件的流程和自动化,确保平稳过渡,最大程度地减少服务中断。
安全与合规:多租户应用程序必须遵守严格的安全和合规要求,尤其是在处理敏感数据或在受监管行业运营时。实施严格的安全措施(如数据加密、安全访问控制和定期安全审计)非常有挑战性且耗时。
计费和计量:在共享环境中,准确跟踪和计费每个租户的资源使用非常复杂。设计一个透明且公平的计费系统需要实施计量和监控机制,以准确捕捉特定租户的使用数据。
在设计多租户应用时,需要对这些挑战进行仔细的规划和架构决策。我们需要在设计初期中就全盘考量上述的挑战。
多租户系统的设计思考
如果你问 ChatGPT,哪些是设计多租户系统的最佳实践,它会把上面的这些条目再给你列一下,再给你一些建议(针对每个条目,应该注意哪些)。阅读后,我觉得信息增益不大。倒是 AWS 的 DynamoDB 的论文,给了我很多启发。以下是我的思考。
用户的权利和义务 SLA
用一句话概括就是,设置并管理好用户的预期。在执行层面就是,和用户对齐服务的 SLA,并通过技术手段严格遵守 SLA。
通常,我们认为服务的 SLA 就是稳定性几个 9,服务中断不超过多少时间,修复时间不超过多长等等,这些 SLA 当然重要。在我看来,这些 SLA 定义了用户的权利。DynamoDB 服务,还定义了哪些其他的 SLA 呢? DynamoDB 的表通常被划分为多个 partition, 对于每一个 partition(作为计算和存储的基本调度单元),DynamoDB 引入了 Read Capacity Unit(RCU)和 Write Capacity Unit(WCU)来控制其相应的吞吐 SLA。RCU 的定义如下,对于一个至多 4KB 的 item,一个 RCU 保证每秒做一次强一致读。同理,WCU 的定义是对于一个至多 4KB 的 item,一个 WCU 保证每秒做一次写操作。将一个表的所有的 partition 的 RCU 和 WCU 求和,即为这个表的总吞吐 SLA。举个例子,一个表有 4 个 partition,每个 partition 如果申请了 800 RCU 和 WCU,那这张表的整体 RCU 和 WCU 就是 3200,即,这张表应该要保证每秒能进行 3200 次的读操作和写操作。乍一看,这个 SLA 依然是用户的权利。但精髓的是,DynamoDB 通过严格的 throttling 机制,同时也限制了这个表的读写上限为 3200 QPS。如此,这张表的 3200 QPS 也变成了用户的义务 SLA。即,用户必须遵守义务 SLA(当然,DynamoDB 是通过技术手段确保了用户必须去遵守),才能享受权利 SLA,比如稳定性几个 9,返回延时保证在单毫秒级别等等。
这真的是一个非常精妙的设计!通过严格遵守义务 SLA,把复杂系统里的很多不确定性因素都避免了。当每个用户都定义好了义务 SLA,就不会再出现意外的流量激增(traffic spike)(当然, DynamoDB 也引入了一些新颖的设计,来解决这种临时的流量激增,但这不在本文的讨论范围内),因为所有用户的义务 SLA 的总和就是最大吞吐需求。有了明确的需求,系统可以提前做好充裕的资源分配,也不用担心这些资源会不够。只有当需求(义务 SLA)改变的时候,才需要新增资源。
Force 用户严格遵守义务 SLA
多租户系统的一个非常大的挑战就是,一个 bad player 可能会导致整体服务不稳定从而影响其他租户。这就违反了多租户系统的第一设计原则:不同租户理论上不应该知道其他租户的存在。杜绝 bad player 出现的最好的方式就是,扼杀在萌芽中。有效的技术手段就是 active 地 throttling(限流),确保用户就不能变成一个 bad player。DynamoDB 也是这么去做的。
其他的多租户系统设计实践
如果能严格遵守上述的设计原则,其他的设计会变得更水到渠成。这里,也总结一下多租户系统的一些其他设计原则:
做好租户隔离:实施数据分区策略,如单独的数据库、单独的模式或带有租户标识符列的共享模式,以确保数据隐私和安全。此外,应用严格的访问控制和租户上下文感知数据访问,以保护租户数据。
设计好可水平扩展性(horizontal scaling):可水平扩展是多租户系统的关键,必须要能够做到。你可以依赖分布式系统设计来完成,也可以通过申请独立资源,为每个租户独立部署(当然这个方法比较原始)。确保系统可以增加更多资源以适应不断增加的工作负载。通过分布式和云原生来统一部署的好处在于,可以做到动态地扩缩容,也能根据不同租户的业务属性不同,做好削峰填谷,以确保最佳性能和资源利用。
定制化和可扩展性:开发支持租户定制、扩展的模块化和灵活的架构。使用可配置设置、插件和基于 API 的界面,允许租户根据其独特要求定制应用程序,而不影响整个系统的稳定性或性能。
集中式管理:实施集中式管理系统,用于应用程序更新、安全补丁和维护任务。这简化了管理,减少了开销,并确保所有租户同时受益于改进和修复。
可观察性和监控:完善的可观测性属于基本操作。实施全面的监控和可观察性工具,以跟踪应用程序性能、资源使用和潜在问题。使用日志、指标和跟踪解决方案,以获取应用程序健康状况并识别改进方案。
计费和计量:设计透明公平的计费系统,准确追踪每个租户的资源使用和成本。实施计量和监控机制,捕获租户特定的使用数据并生成计费报告。
总结
这期番外篇,依然是从 DynamoDB 服务引申出来,讨论了一下多租户系统设计的思考。如果你一定要从这篇文章中获得些什么,就是下面这两句话:提供一个稳定的,严格遵守 SLA 的服务,远比提供一个不稳定,但偶尔能超越预期的服务要对用户更友善。设计多租户系统时,除了和用户定义好权利 SLA,也定义好义务 SLA,并严格执行这些 SLA。
内核杂谈微信群和知识星球
内核杂谈有个微信群,大家会在上面讨论数据库相关话题。目前群人数快 400 人啦,所以已经不能分享群名片加入了,可以添加我的微信(zhongxiangu)或者是内核杂谈编辑的微信(wyp_34358),备注:内核杂谈。
除了数据库内核的专题 blog,我还会 push 自己分享每天看到的有趣的 IT 新闻,放在我的知识星球里(免费的,为爱发电),欢迎加入。
评论