连接到 redis 时,Unix 套接字比 tcp 慢

Posted

技术标签:

【中文标题】连接到 redis 时,Unix 套接字比 tcp 慢【英文标题】:Unix sockets slower than tcp when connected to redis 【发布时间】:2015-01-12 03:16:47 【问题描述】:

我正在开发高性能 Web 服务器,它应该能够处理约 2k 的同时连接和 40k QPS,实现响应时间

它的作用是查询 Redis 服务器(在同一主机上运行)并将响应返回给客户端。 在测试期间,我观察到使用 TCP STREAM_SOCKET 的实现比使用 unix 套接字连接更好。大约 1500 个连接时,TCP 保持大约 8 毫秒,而 unix 套接字达到大约 50 个。 服务器是用 C 编写的,它基于常量 Posix 线程池,我使用阻塞连接到 Redis。我的操作系统是 CentOS 6,使用 Jmeter、wrk 和 ab 进行了测试。 为了与 redis 连接,我使用 hiredis 库,它提供了这两种连接到 Redis 的方式。 据我所知,unix socket 至少应该和 TCP 一样快。 有人知道什么会导致这种行为吗? .我被困住了。谢谢。

【问题讨论】:

您能否使用 redis-benchmark 在您的应用程序之外复制这种较慢的速度? 根据this linkthis link 在大量使用流水线(即长流水线)时,与 TCP/IP 环回相比,unix 域套接字的性能优势往往会降低。 你设置了 SO_NODELAY 吗?在某些情况下,我已经看到这有很大帮助。 谢谢大家的回答! @TimCooper redis-bench 在 redis unix 套接字上运行良好,因此需要与我的应用程序相关。 @geert3 不幸的是,禁用 Nagle 没有帮助,我之前尝试过。 【参考方案1】:

Unix 域套接字通常比环回接口上的 TCP 套接字更快。一般来说,Unix 域套接字平均有 2 微秒的延迟,而 TCP 套接字有 6 微秒。

如果我使用默认值(无管道)运行 redis-benchmark,我会看到每秒 160k 请求,主要是因为单线程 redis 服务器受到 TCP 套接字的限制,160k 请求以 6 微秒的平均响应时间运行。

当使用 Unix 域套接字时,Redos 每秒可实现 320k SET/GET 请求。

但是有一个限制,事实上,在 Torusware,我们的产品 Speedus 已经达到了这个限制,这是一种平均延迟为 200 纳秒的高性能 TCP 套接字实现(请通过 info@torusware.com 联系我们以获得请求极限性能版本)。在几乎零延迟的情况下,我们看到 redis-benchmark 实现了每秒大约 50 万个请求。所以我们可以说,redis-server 的延迟平均每个请求大约为 2 微秒。

如果您想尽快回答并且您的负载低于 redis 服务器的峰值性能,那么避免流水线可能是最好的选择。但是,如果您希望能够处理更高的吞吐量,那么您可以处理请求管道。响应可能需要更长的时间,但您将能够在某些硬件上处理更多请求。

因此,在前面的场景中,使用 32 个请求的管道(在通过套接字发送实际请求之前缓冲 32 个请求),您可以通过环回接口每秒处理多达 100 万个请求。在这种情况下,UDS 的好处并不那么高,尤其是因为处理这种流水线是性能瓶颈。事实上,管道为 32 的 1M 请求大约是每秒 31k 个“实际”请求,我们已经看到 redis-server 每秒能够处理 160k 个请求。

Unix 域套接字每秒分别处理大约 1.1M 和 1.7M 的 SET/GET 请求。 TCP 环回每秒处理 1M 和 1.5 个 SET/GET 请求。

使用流水线,瓶颈从传输协议转移到流水线处理。

这与redis-benchmark 网站中提到的信息一致。

但是,流水线显着增加了响应时间。因此,在没有流水线的情况下,100% 的操作通常在不到 1 毫秒的时间内运行。当流水线处理 32 个请求时,高性能服务器中的最大响应时间为 4 毫秒,如果 redis-server 在不同的机器或虚拟机中运行,则为数十毫秒。

因此,您必须权衡响应时间和最大吞吐量。

【讨论】:

奇怪的是,没有人对这个冗长而漂亮的答案发表评论。感谢您分享您的经验!【参考方案2】:

虽然这是一个老问题,但我想补充一点。其他答案谈论 500k 甚至 1.7M 响应/秒。使用 Redis 可能可以实现这一点,但问题是:

客户端--#Network#--> Web服务器--#Something#--> Redis

我认为网络服务器的功能类似于 Redis 的 html 代理。

这意味着您的请求数量也受限于网络服务器可以实现的请求数量。 有一个经常被遗忘的限制:如果你有一个 100Mbit 的连接,你可以使用每秒 100.000.000 位,但默认为 1518 位的包(包括包后所需的空间)。这意味着:65k 网络包。假设您的所有响应都较小,那么由于 CRC 错误或丢失的包,此类包的数据部分和非必须重新发送。

另外,如果不使用持久连接,则需要为每个连接进行 TCP/IP 握手。这会为每个请求添加 3 个包(2 个接收,一个发送)。因此,在这种未优化的情况下,您的网络服务器仍然有 21k 可获得的请求。 (或 210k 用于“完美”千兆连接) - 如果您的响应适合一个 175 字节的数据包。

所以:

持久连接只需要一点内存,所以启用它。它可以让你的表现翻两番。 (最好的情况) 如果需要,可以使用 gzip/deflate 来减少响应大小,以便它们可以容纳尽可能少的数据包。 (每个数据包丢失都可能是响应丢失) 通过去除不需要的“垃圾”(如调试数据或长 xml 标记)来减少响应大小。 HTTPS 连接会增加巨大的(在此进行比较)开销 添加网卡并中继它们 如果响应总是小于 175 字节,请为此服务使用专用网卡并减小网络帧大小以增加每秒发送的包数。 不要让服务器做其他事情(比如提供普通网页) ...

【讨论】:

以上是关于连接到 redis 时,Unix 套接字比 tcp 慢的主要内容,如果未能解决你的问题,请参考以下文章

在 Linux 下将 TCP 流量重定向到 UNIX 域套接字

连接到服务器时如何获取 TCP 套接字 ID

Docker:尝试在 unix:///var/run/docker.sock 连接到 Docker 守护程序套接字时获得权限被拒绝

Redis-Redis 绑定套接字用unix socket还是tcp socket比较好

使用 SQLAlchemy 通过 unix 套接字连接到数据库

Flask SQLalchemy 无法使用 Unix 套接字连接到 Google Cloud Postgresql 数据库