重构:从 Redis 到 SQLite

文摘   2024-10-11 19:57   美国  

背景


我们是 Wafris,一家开源 Web 应用防火墙公司,除了其他框架外,我们还提供 Rails 中间件客户端。


在发布时,v1 客户端需要与您的应用一起部署本地 Redis 数据存储。我们现在发布 Rails 客户端的 v2 版本,它使用 SQLite 作为后端数据存储。


本文介绍了我们从 Redis 迁移到 SQLite 的决策过程、性能考虑因素和架构变更。如果您对我们为客户端(部署的中间件)从 Redis 迁移到 SQLite 的决策过程感兴趣,请继续阅读本文。


TL;DR


  • SQLite 有其优点和缺点。

  • Redis 有其优点和缺点。

  • 传统的 RDBMS(Postgres/MySQL)有其优点和缺点。


这些数据存储不是可以直接替换的,如果您尝试这样做,会遇到麻烦。本文介绍了我们将 v1 基于 Redis 的客户端重新架构为 v2 基于 SQLite 的客户端的测试和决策过程。


是什么促使这一变化?


从 Wafris 成立之初,我们的目标就是让开发人员尽可能轻松地保护他们的网站。


但我们的 v1 在实现这一承诺方面效果不一。我们当时认为选择用户自有(自带)Redis 数据存储来支持 Wafris 客户端是一个不错的选择。


部分原因是我们来自 Heroku 生态系统,在那里 Redis 只需点击一下就可以启动并部署,易于远程访问。我们还研究了像 Sidekiq 这样采用类似模型的成功项目。


如果我们试图让事情变得简单,就不应该随意让您成为 Redis 数据库管理员。


但生态系统远不止于此,我们的许多用户遇到了难以调试和修复的 Redis 部署问题。


换句话说,如果我们试图让事情变得简单,就不应该随意让您成为 Redis 数据库管理员。


此外,当我们在  RailsWorld 2023[1]  展出时,对于 Redis 以及假设您会自动需要一个 Redis 服务器与 Rails 应用一起运行,确实有一种"血腥"的氛围。


什么是速度?


虽然与传统的 RDBMS 相比,Redis 是"快速"的,但它仍然是一个数据库,您必须管理连接、内存、进程等,这会给堆栈引入更多的脆弱性(与我们试图实现的相反)。


更重要的是,如果您在云环境中,您会面临网络延迟。网络延迟对我们来说是一个大问题,因为每个传入的 HTTP 请求都必须根据存储在 Wafris 中的规则进行评估。因此,尽管我们为 v1 客户端付出了巨大努力使其尽可能快,但我们经常会遇到这样的情况:尽管我们尽了最大努力,但由于应用所在的网络很慢,我们仍然会拖慢应用的速度。



单体式假设


虽然肯定存在完全分布式的应用,而且大多数 Rails 应用都是"宏伟的单体"(tm? 不知道,别告我),但我们发现很多分布式应用打破了我们的假设。


部署到多个区域的应用、将功能拆分到具有重叠职责的服务器中的应用,或者只有部分是 Rails 并与其他语言或框架一起部署的应用。


大多数情况下,生产环境中的情况并不那么整洁,这给使用 Redis 带来了更多的摩擦。


迫使我们重新思考架构


Wafris 是一个 Web 应用防火墙。在 Rails 中,它作为中间件安装。它允许您设置规则,如"阻止 IP 1.2.3.4",然后当有人请求您的网站时,您根据这些规则评估该请求。


想象一下这是一个简化的两步过程:


  1. 将 HTTP 请求与规则进行比较(如果匹配 == 403,否则 200)

  2. 报告处理结果(阻止、允许、通过)


抽象地说,这是一个成对的过程,首先是从数据库中"读取"规则(步骤 2),然后"写入"报告,详细说明对该请求的处理以及其数据。


从逻辑角度来看,这个过程的第一个"读取"部分比后面的"写入"部分重要得多:


  • 读取(即"请求")需要按顺序处理

  • 请求过滤 必须 有效,否则可能会放行恶意请求

  • 读取(即"请求")对时间敏感,因为它们会影响用户感知的网站性能

  • 写入(即"报告")可以更慢地完成,可以批处理、异步等


进入 SQLite


其他人已经比我更雄辩地写过 SQLite 适合做什么,所以在这个话题上,我会为您指出以下资源:


  • Aaron Francis 的"高性能 SQLite"课程,网址为  https://highperformancesqlite.com/[2]

  • Stephen Margheim 的"Rails 上的 SQLite" -  最佳性能的原因和方法[3]

  • Oldmoe -  https://oldmoe.blog/[4]


SQLite 对 Wafris 有什么好处?


如上所述,我们的主要瓶颈是网络 IO,Stephen 提到了 SQLite 文档中的这句话:"SQLite 不与客户端/服务器数据库竞争。SQLite 与 fopen() 竞争。"


SQLite 不与客户端/服务器数据库竞争。SQLite 与 fopen() 竞争。


理论上,仅仅基于消除网络往返,这应该比 Redis 解决方案快得多。


因此,我们决定对 SQLite 和 Redis 进行基准测试。


对 SQLite 和 Redis 进行基准测试


基准测试是一门用精确数字欺骗自己的黑暗艺术。


而对数据存储进行基准测试更加困难。我见过的每一个该死的数据库基准测试都覆盖着一层星号和限定条件,HN 上的评论都充满了"如果你在编译时设置这个标志,你就能在读取时获得 3% 的速度提升,而运行这个测试的人没有这样做的事实证明他们被收买了,他们积极销售疯狂的游艇摇滚 Harambe 模因的可疑 NFT 骗局。



然而,我们在这里有一个优势。我们不是试图在完美条件和精心调整的设置下获得某个绝对的速度数字。我们正在针对我们自己的数据创建一个极其具体和有偏见的基准测试,这个用例会导致一些极端情况,我既不期望也不希望其他人必须处理这些情况。


刻意忽略优化调整


我们正试图优化让人们将 Wafris 扔进他们的应用中并让它"正常工作™"(好吧,这个可能真的是 商标[5] )。


我们也不是针对某个理论基准套件进行测试(那可能会被操纵,就像  NVIDIA[6] )。


我们正在测试 我们自己 应用的热路径和 我们 最糟糕的查询。


我们最糟糕的查询是一个有点复杂的请求,用于"词法十进制"数据结构,它将 IP 范围(v4 和 v6)映射到类别。最简单的考虑是 IP -> 国家映射,如果 IP 地址在两个地址的范围内,我们返回国家。


这很糟糕,因为这些结构必然很大(数百万行),而在 IPv6 的情况下,每个条目都很大。


为了实现这一点,我们预先计算范围查找,然后将它们写入:


  • SQLite 中的一个表

  • Redis 中的一个 有序集合[7]


对于每个入站 HTTP 请求,在病态情况下,我们必须检查请求 IP 是否在:


  1. 自定义允许范围

  2. 自定义阻止范围

  3. GeoIP 范围

  4. IP 信誉范围


这就是我们所说的"热路径" - 这种单一的查询类型如此重要,以至于我们可以忽略所有其他的查询类型和功能。


因此,对于我们的基准测试脚本,我们只测试了这一种查询类型,这为我们节省了将更多重要(但与热路径无关)的应用部分移植到基准测试中的数天工作。


测试协议



测试在我的本地 Macbook Air M2 上进行,使用 homebrew 安装的 Redis 和本地 SQLite 数据库。


  1. 我们针对现有的范围数据集(120 万条记录)进行了测试

  2. 然后以相同的顺序对 SQLite 和 Redis 运行多组 IP

  3. 在每个倍数上,我们运行测试 5 次并取平均值


测试结果



SQLite 击败 Redis 就像一个被过度炒作的 UFC 战士从围栏上跳下来落地踢拳一样(对于我们在这个利基市场中的特定用例)


SQLite 比本地部署的 Redis 实例大约快 3 倍。您的里程可能会有所不同。请再次注意,这是在考虑任何网络延迟之前。


从我们的角度来看,这是一个了不起的结果,因为即使 SQLite 在本地只与 Redis 相当,我们仍然能够通过完全消除网络时间而获胜。


我想再次强调,这是一个极其有缺陷的测试(有意为之),设置非常简单,但它以一种反映我们观察到的真实世界使用缺陷的方式进行。


图表中缺少什么?


基准测试存在于真空中。图表未能捕捉到的是:


  • 即使 SQLite 在基准测试中的性能明显更差(比如慢 2 倍),由于网络延迟,它在"真实世界"中 仍然 可能更快,即使是对于在同一数据中心/区域的 Redis

  • 即使您有一个超级强大的 Redis 服务器(集群、分片等),仍然存在一些网络带宽、连接等方面的限制,再加上跨区域延迟。

    SQLite 让我们"免费"获得近乎无限的水平扩展;下面会详细介绍。

  • 使用 SQLite 的入门过程要好得多 - 我想大多数用户甚至不会知道正在使用它;他们只需将 gem 添加到他们的 Web 应用中就可以启动并运行。

  • 有很多潜在的改进可以从 Redis 中获得更多性能。

    然而,我们一直无法说服用户对他们的 Redis 设置进行甚至基本的配置更改(如缓存驱逐策略),因为这太麻烦了,他们不想成为 Redis 管理员。


我们专注于:


  1. 让 Wafris 易于部署

  2. 使规则评估尽可能快


其他一切都源于此,所以这就是我们重新架构的方向。不是"最好的数据库设置",不是"对我们(Wafris)来说最容易管理的基础设施",等等。


结果只是开始


很好,现在我们已经确定 SQLite 比 Redis 更快(注意事项,注意事项,注意事项),但一切仍然不太理想,因为存在现实世界的权衡。


上面测试中的一个 巨大 权衡是我们甚至没有考虑进行任何写入。


虽然从未明确说明,但"正确"数据库拥有连接、连接池、事务以及任何允许 Oracle 对数据库收取十亿美元的专有魔法的全部原因是为了管理写入与读取争用数据库中的数据。


想象一下由一位迷人的英国人驾驶的电动中国超级跑车 - 它速度快,性能出色,但在运输一堆混凝土块穿越城镇方面绝对是垃圾。


SQLite 就像那辆 NIO EP9 超级跑车。我们需要将它用于它擅长的领域,而不是强迫它承担不适合的角色。


构建同步架构


在 v1(Redis)中,更新循环如下:


  1. 用户在 Wafris Hub 中更新规则("阻止 IP 1.2.3.4")

  2. Wafris Hub 更新您的 Redis 数据存储中的规则


这显然不适用于 SQLite,因为我们无法将 SQLite 数据库"推送"到 Web 服务器。有一些较新的 SQLite 即服务提供商允许您执行这种操作的版本,但出于各种成本、性能和安全考虑,这对我们不起作用,因为我们仍然需要单个用户部署它们、打开端口、允许入站连接等。


在 v2(SQLite)中,更新循环如下:


  1. 用户在 Wafris Hub 中更新规则("阻止 IP 1.2.3.4")

  2. 在某个间隔(时间或请求数)后,客户端检查更新的规则

  3. 如果规则已更新,客户端下载一个全新的 SQLite 数据库


这种方法效果很好,因为它减少了用户的安装和配置责任。


我们看到 v2 客户端的成功安装量增加了约 3 倍。


SQLite 分布式架构


考虑一个部署在云提供商(AWS、Heroku、Fly 等)上并启用了自动扩展的 Rails 应用。


你的请求从 100 次/秒增加到 10,000 次/秒,你的计算资源(dynos、机器、ec2 实例)开始增加以处理负载,但你的数据库呢?


答案几乎肯定是否定的,因为没有数据库的话瓶颈就是瓶颈,虽然你可以"以防万一"将其过度配置 100 倍,但实际上没有人愿意这样做。



实际上,这是我们看到在高负载下杀死 Rails 应用的头号原因。很少是实际的 DDOS 攻击;更常见的是凭证填充攻击或恶意机器人在猛烈攻击网站,推高自动扩展,然后耗尽数据库连接,导致应用崩溃。

将其转换为一个将 SQLite 数据库同步到每个计算实例的系统可以很好地解决这个问题,因为所有调用都保持在新计算实例的本地。


但写入操作呢?


我们开始这篇文章时讨论了将应用分为读取(规则评估)和写入(报告)路径,然后刻意忽略了写入路径。


我们确实重新设计了写入路径,做了以下工作:


  • 异步连接到 Wafris Hub 进行报告

  • 批量发送报告

  • 完全移除客户端的数据库写入


这对大约 0% 的其他人有效,但我们不关心他们。

我们关心的是 100% 希望 Wafris 客户端易于部署且极快的用户。

很难知道你是否能从阅读这些中学到什么,但我们真诚地感谢你花时间阅读。


结论


首先:感谢  Aaron Francis[8] 、 Travis Northcutt[9] 、 Brian Hogg[10] 、 Dave Ceddia[11] 、 Peter Bhat Harkins[12]  和  Nate Bosscher[13]  对这篇文章的反馈和建议。


其次:我们对使用 SQLite 的 v2 架构非常满意。它已经帮助许多网站抵御攻击并保持在线。


它更容易上手,我们需要做的支持工作更少,用户也更少麻烦,我们认为这对于一个更安全的互联网来说是一个胜利。


如果你想开始使用,请在  Wafris Hub[14]  上注册。


参考链接


  1. RailsWorld 2023: https://wafris.org/blog/railsworld-2023-recap

  2. https://highperformancesqlite.com/

  3. 最佳性能的原因和方法: https://fractaledmind.github.io/2024/04/15/sqlite-on-rails-the-how-and-why-of-optimal-performance/

  4. https://oldmoe.blog/

  5. 商标: https://trademarks.justia.com/776/82/it-just-works-77682144.html

  6. NVIDIA: https://www.theregister.com/2021/01/28/nvidia_tpc_benchmark/#::text=Nvidia has been accused of,very umpires of the test

  7. 有序集合: https://redis.io/docs/latest/develop/data-types/sorted-sets/

  8. Aaron Francis: https://x.com/aarondfrancis

  9. Travis Northcutt: https://x.com/tnorthcutt

  10. Brian Hogg: https://brianhogg.com/

  11. Dave Ceddia: https://twitter.com/dceddia.com

  12. Peter Bhat Harkins: https://push.cx/

  13. Nate Bosscher: https://x.com/natebosscher?lang=en

  14. Wafris Hub: https://wafris.org/signup


幻想发生器
图解技术本质
 最新文章