Используя Redis Ruby Gem мы начали с конвейерных (pipilined) запросов. Pipiline запросы в Redis возвращают массив с результатами всех операций в конвейере:
data = {}
$redis.pipelined do
keys.each do |key|
data[key] = $redis.hgetall(key)
end
end
data.each do |key,value|
data[k] = v.value
end
Такой подход оказался весьма медленным, как только мы стали работать с большими наборами ключей. Очень хотелось чего-то похожего на multi-get в Memcached. И использование Lua скриптов в Redis выглядело самой интересной альтернативой любым других решениям, чтобы сделать такую фичу. Не особо работая с Lua мы очень удивились тому, как много можно сделать с использованием Lua скриптов. Используя Lua мы можем за один запрос к Redis получить все ключи, которые нам нужны:
local collate = function (key)
local raw_data = redis.call('HGETALL', key)
local data = {}
for idx = 1, #raw_data, 2 do
data[raw_data[idx]] = raw_data[idx + 1]
end
return data;
end
local data = {}
for _, key in ipairs(KEYS) do
data[key] = collate(key)
end
Код реально простой. Мы можем пройтись по переданным в функцию ключам и собрать данные из разных хешей в одну кучу. Проблемы начались, когда мы стали отправлять данные обратно в ruby. Оказалось, что объекты в Lua не всегда просто сериализуются в объекты Ruby. Реализация Lua в Redis позволяет использовать сериализацию в cjson и cmsgpack. Нам надо просто вернуть всё назад:
-- return json return cjson.encode(response) -- return messagepack return cmsgpack.pack(data)Выбирая между pipelined запросами, lua + json и lua + messagepack, последний вариант оказался самым быстрым. Наша финальная реализация:
require 'redis'
require 'redis'
require 'msgpack'
keys = %w(FOO BAR BAZ)
lua_msgpack_loader = <<LUA
local collate = function (key)
local raw_data = redis.call('HGETALL', key)
local hash_data = {}
for idx = 1, #raw_data, 2 do
hash_data[raw_data[idx]] = raw_data[idx + 1]
end
return hash_data;
end
local data = {}
for _, key in ipairs(KEYS) do
data[key] = collate(key)
end
return cmsgpack.pack(data)
LUA
redis = ::Redis.new(:driver => :hiredis)
data = MessagePack.unpack(redis.eval(lua_msgpack_loader, :keys => keys))
И конечно же результаты тестов для 10к ключей:
user system total real lua + json 0.350000 0.010000 0.360000 ( 1.242315) lua + msgpack 0.260000 0.020000 0.280000 ( 1.146377) redis pipelined 1.070000 0.020000 1.090000 ( 1.759858)В целом, мы были приятно удивлены как Redis и Lua позволяют делать классные вещи!
Оригинал на английском языке: http://stdout.tradier.com/development/2014/07/10/using-lua-to-implement-multi-get-on-redis-hashes.html?utm_source=redisweekly&utm_medium=email#.VBLQO8J_sud
Комментариев нет :
Отправить комментарий