gpt4 book ai didi

ruby - Redis - 为什么这段代码这么快?

转载 作者:IT王子 更新时间:2023-10-29 06:00:47 29 4
gpt4 key购买 nike

最近我使用 Redis.Eval 改进了一些代码并且效果很好。事实上,这太有效了,但我不明白这是怎么可能的。

对于 TL;DR

改进了多次使用 Redis.zcard 以使用一次 Redis.eval 的 Redis 代码。代码速度提高了 100 多倍(在测试环境中。在实际项目中,速度提高了 1000 多倍)。我不知道为什么。有人可以解释一下吗?

代码做什么

它完成了一个非常简单的任务。它接受一个字符串数组,这些字符串是存储在 Redis 中的 ZSET 的键,并对相应的 ZSET 的大小求和,并返回一个整数值,即总和。

测试设置

为了尽可能多地消除外部变量,我搭建了一个简单的测试环境,如下所示。

redis = Redis.new(host: '127.0.0.1', db: 1)
KEYS = 500.times.collect do |i| "KEY#{i}" end
KEYS.each do |key|
redis.zadd(key, 0, "DATA")
end

改进前

在我更改代码之前,它的工作方式如下。

sum = 0
KEYS.each do |key|
sum += redis.zcard(key)
end

然后我用下面的一行代码测试了这段代码的速度。
t = Time.now;总和=0; KEYS.each 做 |key| sum += redis.zcard(key) 结束; puts(Time.now - t)
结果打印出 0.202seconds(202ms)
(请注意,我是根据测试环境和上面写的代码计算的时间,而不是真实环境)

改进后

在我使用 Lua 脚本和 EVAL 更改代码后,它的工作如下。

script = " 
local sum = 0
for index, key in pairs(KEYS) do
sum = sum + redis.call('zcard', key);
end
return sum"
sum = redis.eval(script, KEYS)

然后,我还使用以下单行代码测量了执行上述代码所花费的时间。
t = Time.now; redis.eval(脚本,KEYS); puts(Time.now - t)
这给了我 0.001519seconds(1.5ms)。这比“改进前”的代码快 134 倍

困惑

让我感到困惑的是,一个 redis.zcard(KEYS[0]) 大约需要 0.000542seconds(0.542ms)。因此,在 redis 中计算 500 ZCARD 的 redis.eval 代码花费了大约相同的时间在 ruby​​ 中计算 3 Redis.ZCARD。

当我第一次在我的项目中发现它时,我认为网络延迟的减少和排队等待时间的减少起到了作用。但是,当我在本地redis上测试这个时,我怀疑我的理论。完全没有网络延迟,也没有其他任务在使用 Redis。

我的理论是

  1. Ruby summation(sum += redis.zcard(key)) 占用了大部分时间。
  2. 即使我使用的是本地主机,redis 和 ruby​​ 通信之间也存在某种延迟。
  3. 在处理许多查询时,redis 存在内部延迟。 (虽然不太可能)

任何人都可以向我解释为什么这个 Redis.eval 代码非常快吗?谢谢!

最佳答案

这是由于滞后造成的,基于对套接字的写入/读取

20% - 向套接字写入命令

80% - 从套接字读取结果

require 'benchmark'
require 'redis'

redis = Redis.new(host: '127.0.0.1', db: 1)
KEYS = 10_000.times.collect { |i| "KEY#{i}" }
KEYS.each { |key| redis.zadd(key, 0, 'DATA') }

script = "
local sum = 0
for index, key in pairs(KEYS) do
sum = sum + redis.call('zcard', key);
end
return sum"

Benchmark.bm do |x|
x.report { puts KEYS.inject(0) { |sum, key| sum + redis.zcard(key) } }
x.report do
client = redis.client
client.send(:ensure_connected) do
KEYS.inject(0) { |sum, key| sum + client.process([[:zcard, key]]) { client.read } }
end.tap { |res| puts res}
end
x.report do
client = redis.client
client.send(:ensure_connected) do
connection = client.connection
socket = connection.instance_variable_get(:@sock)
KEYS.map do |key|
command = connection.build_command([:zcard, key])
# Redis::Connection::Ruby
socket.write(command) # write to socket, 20% of execution time
line = socket.gets # read from socket, 80% of execution time
reply_type = line.slice!(0, 1)
connection.format_reply(reply_type, line)
end.inject(:+)
end.tap { |res| puts res}
end
x.report { puts redis.eval(script, KEYS) }
end

# user system total real
# 10000
# 0.480000 0.230000 0.710000 ( 0.966610)
# 10000
# 0.510000 0.250000 0.760000 ( 1.132668)
# 10000
# 0.500000 0.270000 0.770000 ( 1.193521)
# 10000
# 0.030000 0.000000 0.030000 ( 0.054858)
# [Finished in 4.923s]

由于传输延迟,可能是在 redis 中执行的脚本。

关于ruby - Redis - 为什么这段代码这么快?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37341664/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com