视频

了解更多
英特尔和 Redis 做出了重大的性能改进!在这里我们分享了我们用来评估和最大化 Redis GEO 命令性能的优化技术,例如减少浪费的计算和简化算法。
所有努力都得到了回报。总的来说,我们使 Redis 的性能提高了之前的四倍!
Redis 的 GEO 命令(正式称为 地理空间索引)是处理地理数据的强大工具。Redis 可以存储对象的地理坐标,例如商店位置或移动中的送货卡车。然后 Redis 可以对这些数据执行数学运算和基于坐标的运算,例如确定两点之间的距离(我的午餐订单的送货车离我有多远?)或查找在另一个点给定半径内的所有注册点(离我当前位置最近的宠物店在哪里?)。
最终,这些经度和纬度只是您的应用程序可以访问和更新的数据。与作为关键业务逻辑一部分的任何数据库一样,您希望获得最佳性能 - 特别是在访问数据的系统在物理上移动(送货卡车)并且因此需要实时响应时。为了实现这一点,重要的是优化您用于与 GEO 数据交互的查询和算法。
为了最大限度地提高 Redis GEO 命令的执行性能,我们深入研究了性能分析和工作负载特征化技术。目标是识别瓶颈,优化资源利用率,并提高整体性能。
在这篇文章中,我们分享了这些优化技术,例如减少浪费的计算和简化算法,以及我们分析的结果。结论是:我们可以将 GEOSEARCH 查询的吞吐量提高到之前的四倍,并且此增强功能已作为 Redis 7 的一部分发布。
性能分析和工作负载特征化是了解应用程序性能特征的重要技术。这些技术涉及收集和分析有关应用程序行为及其资源使用情况的数据,以识别瓶颈,优化资源利用率,并提高整体性能。
无论我们试图加快什么应用程序,我们都使用确定性、简洁和有条理的方法来监控和分析性能。要自己执行此操作,您可以采用多种 性能方法,有些方法比其他方法更适合,具体取决于您要进行的分析类型和问题类别。
总的来说,当您想提高程序的效率时,此通用清单适用于大多数情况
1. 确定性能目标:在开始之前,您需要清楚了解要实现的目标。目标可能包括提高响应时间、减少资源使用或提高吞吐量。在这种情况下,我们希望同时提高每次操作的响应时间,并相应地提高可实现的吞吐量。
2. 识别工作负载:接下来,确定要分析的工作负载。这可能涉及识别应用程序将执行的特定任务或操作,以及预期负载和使用模式。对于 GEO 命令用例,我们使用了一个包含 6000 万个数据点的数据集,该数据集基于 OpenStreetMap (PlanetOSM) 数据和一组用于基准测试的地理距离查询。
3. 收集数据:确定工作负载后,收集有关应用程序性能及其资源使用情况的每种用例的数据。您可以使用探查器、调试器或性能分析工具。收集有关程序内存使用情况、CPU 利用率和其他指标的数据。通过在定时间隔内对堆栈跟踪进行采样来分析 CPU 使用情况是识别性能关键代码部分(也称为热点)的快速简便方法。我们的发现正是基于使用 Linux perf 工具 来实现这一目标。我们的 有关识别性能回归 和潜在的 CPU 性能改进的文档包含了工具的完整列表和更深入的方法。
4. 分析数据:收集数据后,可以使用各种技术来分析数据并识别可能影响性能的应用程序区域。其中一些技术包括按时间分析性能、比较不同的工作负载以及查找数据中的模式。我们发现,使用 perf report 分析记录的配置文件信息,在对正在分析的应用程序具有编码上下文的情况下,可以轻松改进与 CPU 相关的用例。您对应用程序代码越了解,优化它就越容易。
5. 优化应用程序:根据您的分析,您可以识别可能导致性能问题的应用程序区域。然后采取措施优化代码,例如更改为更有效的算法,减少内存使用量,或进行其他代码调整。在我们接下来展示的场景中,我们专注于算法的效率和减少重复计算。
6. 重复此过程:性能分析和工作负载特征化是持续进行的过程,因此定期审查和分析应用程序的性能非常重要。这使您能够识别和解决更新或使用情况中可能出现的新的瓶颈或问题。
如果您不知道软件在做什么,就无法使其运行得更快。在这种情况下,这意味着简要介绍处理地理空间数据的方法。
大多数用于查询 Redis 地理空间索引 的命令都会计算两个坐标之间的距离,因此有必要检查算法是如何完成的。 海弗森距离 是一个有用的度量,因为它考虑了地球的曲率,比 欧几里得距离 计算结果更准确。
要在球体(例如地球)上计算海弗森距离,您需要两点的纬度和经度,以及球体的半径。海弗森公式计算球体表面上最短路径的直线距离。
这些计算严重依赖于三角函数来计算 Harvesive 距离,调用三角函数在 CPU 周期方面成本很高。在简单 GEOSEARCH 命令的瓶颈分析中,大约 55% 的 CPU 周期花在这些函数内,如 图 1 所示。这意味着值得花时间优化这些代码块,或者正如 Amhdal 所说,“通过优化系统的一部分获得的整体性能改进受到改进的部分实际使用时间的限制。”
因此,让我们专注于最大的优化可能。
如上图所示,geohashGetDistanceIfInRectangle 生成了 54.78% 的 CPU 周期。在其中,我们调用了 geohashGetDistance 三次。前两次,我们调用该例程以生成中间结果(例如,检查一个点是否超出了经度或纬度的范围)。这避免了在条件为假时进行 CPU 密集型距离计算。检查数据是否在范围内是有意义的,但我们不需要重复这样做。
我们第一次优化尝试减少了两个步骤中的中间结果的浪费计算(并在 PR #11535 中进行了详细说明)。
这种优化带来了显著的差异。对于该 GEOSEARCH 查询,使用单个基准测试客户端且没有管道,我们将平均延迟(包括往返时间)从 93.598ms 降低到 73.046ms,这代表了大约 22% 的延迟下降。
对 GEOSEARCH 查询的相同性能分析还表明,在某些情况下,Redis 会执行对 sdsdup 和 sdsfree 的虚假调用。这些命令分别分配和释放字符串的内存。这种情况发生在大型数据集中,其中许多元素在搜索范围之外。
我们在 PR 11522 中提出的性能改进很简单:我们不再预先分配字符串的内存,而是改变了 geoAppendIfWithinShape 的作用,让调用者仅在需要时才创建字符串。这导致延迟下降了大约 14%,可实现的每秒操作数提高了 25%。
为了优化地理索引查询,我们专注于数据模式信息。目的是简化计算海弗森距离所需的计算次数。这纯粹是一个数学练习。
当经度差为 0 时
PR #11579 描述了此简化的优化和性能改进。底线是:这导致 Redis GEOSEARCH 命令的可实现每秒操作数提高了 55%。
所有严重依赖于将双精度浮点数转换为字符串表示的用例(例如,将双精度浮点数 (1.5) 转换为字符串 (“1.5”))都可以通过用等效的定点双精度浮点数到字符串优化版本替换对 snprintf(buf,len,”%.4f“,value) 的调用来获益。
图 3 中显示的 GEODIST 命令就是一个很好的例子。大约四分之一的 CPU 时间用于类型转换。
PR 11552 详细描述了我们建议的优化,这导致 GEODIST 命令的可实现每秒操作数提高了 35%。
Redis 作为数据库流行的主要原因之一是它的性能。我们通常用亚毫秒的响应时间来衡量查询,并且我们希望不断改进它!
我们在本文中描述的优化的累积效果:我们将 Redis 地理空间命令的性能提高到其先前速度的四倍。您已经可以从这些增强中获益,因为它们是 Redis 7.0.7 的一部分。这将帮助您比以前更快地达到目标。
命令 | 测试用例 | Redis 7.0.5 可实现的每秒操作数 | Redis 7.0.7 可实现的每秒操作数 | 改进系数 |
GEODIST key … | geo-60M-elements-geodist-pipeline-10 | 775524 | 993632 | 1.3 倍 |
GEOSEARCH … FROMLONLAT … BYRADIUS | geo-60M-elements-geosearch-fromlonlat | 11.8 | 13.8 | 1.2 倍 |
GEOSEARCH … FROMLONLAT … BYBOX | geo-60M-elements-geosearch-fromlonlat-bybox | 13.2 | 49.6 | 3.8 倍 |
命令 | 测试用例 | Redis 7.0.5 的整体 p50 延迟(包括 RTT)(毫秒) | Redis 7.0.7 的整体 p50 延迟(包括 RTT)(毫秒) | 改进系数 |
GEODIST key … | geo-60M-elements-geodist-pipeline-10 | 2.575 | 2.007 | 1.3 倍 |
GEOSEARCH … FROMLONLAT … BYRADIUS | geo-60M-elements-geosearch-fromlonlat | 679.935 | 598.097 | 1.1 倍 |
GEOSEARCH … FROMLONLAT … BYBOX | geo-60M-elements-geosearch-fromlonlat-bybox | 605.739 | 161.791 | 3.7 倍 |
请注意,这不是一项孤立的性能改进,而是恰恰相反。这组改进是我们称为“Redis 基准规范”的性能改进努力的一个现实例子。我们一直在努力提高整个 Redis API 的可见性和性能。
想要了解更多关于地理空间计算的信息?这段五分钟的视频将为您提供概述。
在本文系列的性能博客中了解更多信息,其中大部分与英特尔合作撰写
*英特尔、英特尔标识和其他英特尔商标是英特尔公司或其子公司的商标。