动态ProxySQL查询规则
弹性在Shopify很重要,虽然我们有很多工具 可以 在事故发生时 帮助我们,但有些问题需要在数据库层面上解决。在Shopify,我们使用ProxySQL来管理应用程序和我们的MySQL数据库之间的连接。ProxySQL接受来自应用程序的连接,并在MySQL数据库中分配流量,提高性能。
典型的ProxySQL拓扑结构的一般概述
ProxySQL有一个强大的功能,叫做查询规则。这些规则在Shopify的主要用途是改道、重写或拒绝与指定的重码匹配的查询。然而,强大的力量伴随着巨大的责任。这些规则很强大,如果使用不当,会产生意想不到的后果。以Shopify的规模,我们正在运行成千上万的ProxySQL实例,所以对每个实例应用查询规则是一个痛苦和耗时的过程,特别是在事件发生时。我们建立了一个工具来帮助我们解决这些挑战,并使部署新规则变得安全和可扩展。
查询规则的类型
数据库可能因高流量、错误的代码或恶意的垃圾邮件而过载。有了ProxySQL的查询规则,你可以通过改道、重写或拒绝查询来快速而有效地应对这些问题。
重路由规则
重路由规则的例子
重路由规则允许我们将匹配的查询发送到某个主机组:一个可互换的服务器池,从应用程序的角度看,它就像一个单一的数据源。例如,我们的读副本都在一个主机组中,所以如果你想让某些查询去读副本,你就创建一个查询规则,把它们路由到那里。
最近,重路由规则对我们Shopify来说非常重要,当时一个团队创建了查询规则,将某些查询发送到读复制区,以调查扩展瓶颈。在衡量了这一做法对性能的影响后,该团队对代码进行了修改,因此被证明在读副本上性能更好的查询被永久地发送到那里。如果没有查询规则,这个过程会更加耗时,因为迭代代码变化的时间比使用查询规则要长得多。
重写规则
重写规则的例子
重写查询规则在事件中很有帮助,如果查询需要修改,而部署代码修改需要太长时间。例如,如果一个数据库列的名称被改变,但你的代码仍然引用旧的列名,你会遇到错误。在等待代码修改以引用正确的列名进行部署时,可以在此期间使用一个临时的重写规则。这是一个插入重写规则的例子。
`` INSERT INTO mysql_query_rules ```` (rule_id, active, match_pattern, replace_pattern) V ```ALUES (123, 1, "users.name", "users.first_name");`
被重写的查询实例
拒绝规则
拒绝规则的例子
如果我们需要保护我们的数据库不受恶意查询或行为不端的代码修改的影响,那么用于拒绝查询的查询规则就很有用。
查询规则的危险性
ProxySQL的查询规则是非常灵活和强大的,但如果使用不当,也会导致严重的问题。例如,你可以创建一个规则,拒绝比你预期更多的查询,或者以不同于预期的方式重写查询,导致数据损坏和停机。
试运行查询规则
如果你能安全地干运行查询规则,这些问题是可以避免的。例如,为了重写与某个词条相匹配的查询,你可以在干运行模式下创建一个规则并预览结果。该规则实际上不会执行它的行动,它只是记录匹配的查询和将采取的行动。一旦作者确信他们的规则是正确的,他们就会把规则从 "干运行 "模式改为 "活动 "模式,在这种模式下,规则实际上会采取行动。
ProxySQL缺乏干运行的功能,所以我们决定将其添加到我们的ProxySQL分叉中。我们希望把它贡献给上游,这样ProxySQL社区就可以干运行他们的查询规则了。你可以在ProxySQL的repo中找到一个拉动请求,其中包括我们对我们的分叉所做的修改。
| 活动 | 日志 | 模式 |
| 1 | 任何事情 | 活跃 |
| 0 | 1 | 干燥的运行 |
| 0 | 0 | 禁用 |
显示查询规则模式定义的表
ProxySQL在内部SQLite数据库中存储配置和统计数据,查询规则在mysql_query_rules表中。我们使用ProxySQL的mysql_query_rules表的active 和log 字段来定义一个查询规则的模式。
我们的查询规则模式的流程图
有了这个ProxySQL分叉的补丁,我们现在可以在干运行模式下运行查询规则,并查看哪些查询受到影响以及它们的结果。一旦我们对规则感到满意,我们只需把它改成激活模式,它就会进行操作。如果一个规则没有像我们预期的那样运行,我们可以修复这个规则,而不必担心它做任何危险的事情。
防止日志退化
对我们的方法有一个疑问,如果所有这些日志导致ProxySQL的性能下降怎么办?在大多数情况下,这种情况不会发生,但我们考虑到有人写了一个规则,其匹配的重码模式太宽泛,导致每秒有成千上万的查询被记录下来。作为对ProxySQL分叉的修改的一部分,我们对这些日志进行了指数级的回退。因此,只有第1、2、4、8等匹配的查询会被记录下来,帮助我们减轻因真正广泛的查询规则记录过多而产生的任何问题。
动态查询规则
现在你知道了我们如何使用查询规则和减轻查询规则的危险,我们将看看我们如何建立一个工具,将这些规则应用于成千上万的ProxySQL实例。
如上所述,向ProxySQL添加查询规则是使用INSERT 语句完成的。同样地,修改和删除查询规则是通过使用UPDATE 和DELETE SQL语句完成的。由于Shopify使用数以千计的ProxySQL副本,如果有人创建了一个规则,他们必须在每个副本中进行。然后,如果他们想编辑或删除该规则,他们必须做同样的事情。如果你只需要在几个副本中这样做,这还可以,但对成千上万的副本这样做是不可能扩展的。
创建查询规则的流程
我们建立了一个工具来动态地创建、修改和删除查询规则。开发人员通过一个Web应用程序为他们的规则输入参数。该应用程序然后更新我们的全球键值存储。Shopify的键值存储被许多应用程序用于控制平面的使用情况。我们在ProxySQLs旁边运行一个sideecar容器,它轮询我们的键值存储,以了解我们的规则集的变化,并将必要的变化应用到ProxySQL。如果一个规则被添加、编辑或删除,sidecar就会进行必要的修改。
分布式系统中的查询规则
在一个完美的世界里,查询规则系统运行良好,没有任何问题。然而,在分布式系统中,许多事情都可能出错。例如,键值存储发生故障,或者sidecar和ProxySQL容器之间的通信失败。当我们设计我们的系统时,我们考虑到了所有这些故障模式。你可以在《 高流量事件的弹性规划》一文中了解更多关于故障模式以及Shopify是如何进行弹性规划的。
我们设计了这个系统,让sidecar容器缓存了一个规则列表。当轮询键值存储时,sidecar会将其列表与键值存储的列表进行比较。键值存储是真理的来源,所以sidecar做任何需要的改变,以保持与键值存储的同步。然后,sidecar通过使用SQL语句修改ProxySQL的mysql_query_rules表来更新ProxySQL的规则列表。
如果键值存储发生故障,sidecar会继续正常运行,只是不会接收到规则列表的任何新变化。一旦键值存储恢复了,sidecar就会接收到任何变化。
如果sidecar和ProxySQL容器之间的通信失败,或者它们由于某种原因而失去同步,我们就会擦除ProxySQL的规则列表以及sidecar的规则列表。在下一个轮询循环中,sidecar从键值存储中获取规则,并将这些规则添加到ProxySQL中。尽管规则被删除的时间很短,但我们更倾向于ProxySQL中规则列表的一致性,而不是每个分片上规则的可用性。我们宁可让一个规则在短暂的时间内被删除,也不愿意出现大脑分裂的问题。
我们用来防止键值存储出现问题的另一个机制是为规则添加一个生存时间(TTL)。开发人员可以选择指定一个规则在自动删除前的持续时间。即使键值存储发生故障,一旦规则的TTL过期,sidecar仍然会将其删除。
有一些规则需要100%可用。在这些情况下,我们在启动时在proxysql.cnf文件中创建规则。启动时创建的规则是静态的,而由我们的工具创建的规则是动态的。
虽然这个工具最常被用作最后的手段,但它对实验非常有价值,在需要它的情况下非常有用。如果没有这个项目,以Shopify的规模快速、安全地创建规则是不可能的。希望这篇文章能让你了解到ProxySQL查询规则的实用性,并让你知道如何实现一个类似的系统。
Rahul Rangith是数据库连接管理团队的一名生产工程师实习生。他目前是滑铁卢大学计算机工程专业最后一年的学生。
如果你对从头开始建立系统以解决现实世界的问题感兴趣,我们的工程博客有关于我们遇到的其他挑战的故事。访问我们的工程职业页面,了解我们的空缺职位。加入我们的远程团队,(几乎)在任何地方工作。了解我们如何通过招聘来共同设计未来--一个默认为数字化的未来。
在您的收件箱中获得这样的故事!
来自构建和扩展Shopify的团队的故事。这个商务平台为全球超过170万家企业提供支持。
电子邮件地址是的,给我登记
与我们分享您的电子邮件并接收每月的更新。
谢谢你的订阅。
您将很快开始收到免费的提示和资源。