Как оптимизировать скрипт 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, чтобы увидеть, что лучше всего работает, исходя из размера ваших значений.

Другие вопросы по тегам