Используя 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
Комментариев нет :
Отправить комментарий