-1

我正在尝试使用 KeyDB 来查看是否可以获得以及可以获得多少性能改进,因为 Redis 单线程查询模型肯定存在瓶颈。所以我找到了 KeyDB,他们说他们使用“真正的”多线程对数据库进行并行查询,这与 Redis 不同,它只有 IO 多线程而不是实际查询。

从上面的文档链接:

与 Redis6 和 Elasticache 不同,KeyDB 多线程有几个方面,包括将事件循环放置在多个线程上,使用网络 IO,以及同时完成查询解析。

我的简单测试设置:

  1. 首先,我在 Ubuntu (WSL2) 上安装 KeyDB 并让它运行
    1. 我注意到启动 KeyDb 时,有 2 个线程处于活动状态: Thread 0 alive. Thread 1 alive.
  2. 我修改了 keydb.conf 以禁用一些保存/持久化,但最重要的是,我将server-threads选项更改为 2: server-threads 2。注意:我也尝试过不使用配置文件,只添加 cmd 标志--server-threads 2并将线程设置为 4,没有区别。
  3. 然后我运行一个简单的脚本:
    1. 使用一些简单的 JSON 对象在散列中创建 1M 条目
    2. 创建一个使用两个线程的简单控制台应用程序;一个线程开始在循环中执行非常简单的 SET ( SET key1 1) 或 GET ( ),而另一个线程从哈希 ( )GET key1 1中“获取所有” 。HGETALL testhash第二个线程在开始其“长查询”之前等待 1 秒。

可以在此处找到 GitHub 存储库(使用 StackExchange.Redis 库)。

我的期望:

我希望简单的快速 SET/GET 每次都花费大约相同的时间,而不会因为长查询运行时 KeyDB 中的阻塞而导致任何延迟或节流。

怎么了:

在长查询运行时,简单的快速 SET/GET 会被阻塞/延迟大约 500-700 毫秒,这表明只有一个线程正在使用,因此会阻塞其他操作。这符合 Redis 的工作方式,以及我希望使用 KeyDB 避免的情况。

日志:

“开始长查询”是当我们执行时HGETALL,几乎立即执行,简单的 SET 被限制并占用超过 500 毫秒,而它应该占用 0-1 毫秒,如之前和之后所见。

使用 ServiceStack Redis 客户端:

10:50:55.336    GetValueFromHashAsync took 1
10:50:55.367    GetValueFromHashAsync took 1
10:50:55.397    GetValueFromHashAsync took 0
10:50:55.416    Starting long query
10:50:56.191    GetValueFromHashAsync took 766 <-- THROTTLED! Delayed with what I think is the actual query time, not the IO part, so at this point, the line fetching data has not completed yet
10:50:56.228    GetValueFromHashAsync took 0
10:50:56.261    GetValueFromHashAsync took 1
....
....
10:51:00.592    GetValueFromHashAsync took 1
10:51:00.620    GetValueFromHashAsync took 1
10:51:00.651    GetValueFromHashAsync took 1
10:51:00.663    Long query done in 5244        <-- The long query returns here, line is completed, total time was about 5 seconds, while the block was about 0.7 seconds

我也测试过从哈希而不是 SET 中获取,同样的事情。

使用 StackExchange.Redis:在 GitHub reproducable 项目中,在这里找到,我使用的是 StackExchange.Redis 而不是 ServiceStack,我得到了不同的(更糟糕的!)行为:

11:27:12.084    HashGetAsync took 0
11:27:12.115    HashGetAsync took 0
11:27:12.146    HashGetAsync took 0
11:27:12.177    HashGetAsync took 1
11:27:12.183    Starting long query
11:27:14.877    Long query done in 2692
11:27:14.893    HashGetAsync took 2686      <-- THROTTLED! This time the other thread is delayed the entire time, query + IO.
11:27:14.929    HashGetAsync took 0
11:27:14.960    HashGetAsync took 0
11:27:14.992    HashGetAsync took 0
11:27:15.023    HashGetAsync took 0
11:27:15.053    HashGetAsync took 0

结论

无论我使用什么客户端库,KeyDB 都会在运行“长查询”时限制请求/查询,即使我有 2 个线程。如果我用 4 个线程启动 KeyDB 并没有关系,同样的行为。

我不知道为什么 StackExchange 的行为与 ServiceStack 不同,但这不是现在的主要问题。

4

1 回答 1

1

KeyDB,其实只是并行运行 IO 操作和 Redis 协议解析操作。它以串行方式处理命令,即一个接一个地处理命令,并且工作线程与自旋锁同步。

这就是为什么那些简单的 set/get 命令会被慢速命令阻止的原因。因此,即使使用 KeyDB,您也不应该运行慢速命令,而且多线程也无济于事。

更新

KeyDB 可以有多个线程在同一个 IP:port 上侦听,以便它可以并行接受多个连接,即SO_REUSEPORT. 它还读取(包括使用redis协议将接收到的数据解析为命令,即RESP)并并行写入套接字。

而Redis只有一个线程,即主线程,监听IP:port。默认情况下,Redis 在单个线程中读取和写入套接字。从 Redis 6.0 开始,您可以启用io-threads使其并行写入套接字。此外,如果启用io-threads-do-reads,Redis 也会并行读取和协议解析。

于 2021-09-28T11:02:17.353 回答