Как оптимизировать скрипт redis cli для обработки 50 миллионов ключей
Я написал ниже bash скрипт для обработки ключа и значения redis. У меня около 45-50 миллионов ключей в моем Redis. Я хочу получить все значения и выполнить некоторую обработку. Чтобы сделать это, мой скрипт ниже обрабатывает 1 миллион ключей. Чтобы обработать 50 миллионов ключей, потребуется 50 часов, а я этого не хочу. Я новичок в Redis Cli - может кто-нибудь, пожалуйста, помогите мне оптимизировать приведенный ниже сценарий, или было бы очень здорово, если бы кто-то мог дать какое-то предложение.
Мой шаблон значения ключа Redis:
Keys - 123.item.media
Values - 93839,abc,98,829 | 38282,yiw,282,282 | 8922,dux,382,993 |
Keys - 234.item.media
Values - 2122,eww,92,211 | 8332,uei,902,872 | 9039,uns,892,782 |
Keys - 839.item.media
Values - 7822,nkp,77,002 | 7821,mko,999,822 |
В приведенном ниже сценарии я передаю все свои ключи и вычисляю, сколько записей у меня есть для каждого ключа. Например - этот ключ (123.item.media) имеет 3 записи, а этот (839.item.media) - две записи.
Таким образом, для ключей и значений bove результат должен быть следующим: Total Count: 8
Таким же образом я делаю для всех 50 миллионов ключей - что занимает слишком много времени.
Мой код:
#!/bin/sh
cursor=-1
keys=""
recordCount=0
while [ $cursor -ne 0 ];
do
if [ $cursor -eq -1 ]
then
cursor=0
fi
reply=`redis-cli SCAN $cursor MATCH "*" COUNT 100`
#echo $reply
cursor=`expr "$reply" : '\([0-9]*[0-9 ]\)'`
keys=${reply#[0-9]*[[:space:]]}
for i in $keys
do
#echo $i
#echo $keys
value=$(redis-cli GET $i)
temCount=`echo $value | awk -F\| '{print NF}'`
#echo $temCount
recordCount=`expr ${temCount} + ${recordCount}`
done
done
echo "Total Count: " $recordCount
Цените вашу помощь заранее!
2 ответа
Вы слишком часто повторяете в цикле, даже для простых вещей, таких как арифметика, которые могут быть выполнены встроенными в Bash. Когда у вас есть такие вещи в цикле, который выполняется несколько миллионов раз, это замедлит процесс. Например:
cursor=$(expr "$reply" : '\([0-9]*[0-9 ]\)')
temCount=$(echo $value | awk -F\| '{print NF}')
recordCount=$(expr ${temCount} + ${recordCount})
Я не эксперт по Redis. Основываясь на моем беглом понимании redis-cli, вы можете сделать это:
redis-cli --scan | sort -u > all.keys
while read -r key;
value=$(redis-cli get "$key")
# do your processing
done < all.keys
Если это не ускорит процесс, следующая идея будет разделить all.keys
составьте куски по несколько тысяч строк и выполните параллельный цикл для каждого подмножества ключей. Если это не работает достаточно быстро, я рекомендую изучить mget
введите команду и измените цикл так, чтобы мы получали значения в пакетах, а не по одному.
Кроме того, Bash может быть не лучшим выбором для этого. Я уверен, что есть лучшие способы сделать это на Python или Ruby.
Большая часть вашего времени тратится на 50 миллионов сетевых вызовов на 50 миллионов ключей согласно этой строке:
value=$(redis-cli GET $i)
Чтобы выполнить массовый запрос, вы можете просто добавить команды GET в список, скажем,1000, и выполнить массовый запрос, используя --pipe
вариант.
--pipe Transfer raw Redis protocol from stdin to server.
--pipe-timeout <n> In --pipe mode, abort with error if after sending all data.
no reply is received within <n> seconds.
Пример массовой вставки приведен здесь на официальной документации Redis, вы можете получить массовые чтения по аналогичным строкам.
Это, безусловно, даст вам необходимый импульс и конвертирует ваш скрипт в пару часов вместо 50 часов. Вы можете настроить значение вашего группового списка на 1000,10000 или 100000, чтобы увидеть, что лучше всего работает, исходя из размера ваших значений.