Данные в реальном времени одновременно записываются для оптимизации Redis — исходная ссылка
Как быстро обновить одновременные запросы без принудительных блокировок? Вот горячая онлайн-практика...
задний план
Логика текущей архитектуры состоит в том, чтобы записывать данные параллельного запроса в очередь, а затем запускать отдельный асинхронный поток для последовательной обработки данных. Преимущество этого метода в том, что нет необходимости рассматривать проблему параллелизма, конечно же, очевидны и его недостатки~
Оптимистическая блокировка реализует одновременные обновления данных.
Согласно текущим бизнес-данным, обновляемым за считанные секунды,keyс низкой частотой столкновений. Автор намерен использоватьCASОптимистичная схема запирания: использованиеLuaРеализация скриптаRedisАтомарные обновления данных, даже в случае параллелизма, его производительность будет на уровне. Ниже представлена блок-схема оптимистичной блокировки CAS для обеспечения одновременного обновления данных:
Согласно приведенной выше блок-схеме,Luaсценарий:
local keys,values=KEYS,ARGV
local version = redis.call('get',keys[1])
if values[1] == '' and version == false
then
redis.call('SET',keys[1],'1')
redis.call('SET',keys[2],values[2])
return 1
end
if version == values[1]
then
redis.call('SET',keys[2],values[2])
redis.call('INCR',keys[1])
return 1
else
return 0
end
Возможные проблемы и их решения
1. В среде с высокой конкуренцией и высокой вероятностью параллельного конфликта, если CAS постоянно дает сбой, он будет продолжать повторять попытки, что приведет к высокой нагрузке на ЦП. Одна из идей для решения этой проблемы состоит в том, чтобы ввести механизм выхода, например невозможность выхода после того, как число повторных попыток превысит определенный порог. как:
func main() {
for i := 0; i < 10; i++ {
isRetry := execLuaScript()
if !isRetry {
break
}
}
}
func execLuaScript() bool {
ctx := context.Background()
r := client.GetRedisKVClient(ctx)
defer r.Close()
luaScript := `
local keys,values=KEYS,ARGV
local version = redis.call('get',keys[1])
if values[1] == '' and version == false
then
redis.call('SET',keys[1],'1')
redis.call('SET',keys[2],values[2])
return 1
end
if version == values[1]
then
redis.call('SET',keys[2],values[2])
redis.call('INCR',keys[1])
return 1
else
return 0
end`
casVersion, err := r.Get("test_version")
kvs := make([]redis.KeyAndValue, 0)
kvs = append(kvs, redis.KeyAndValue{"test_version", casVersion.String()})
kvs = append(kvs, redis.KeyAndValue{"test", "123123123"})
mv, err := r.Eval(luaScript, kvs...)
if err != nil {
log.Errorf("%v", err)
}
val, _ := mv.Int64()
log.Debugf(">>>>>> lua 脚本运行结果 :%d", val)
if val == 1 {
// lua 脚本执行成功,无需重试
return false
} else if val == 0 {
return true
}
}
2,LuaКогда сценарий выполняется, он может действовать только на той же машине, поэтому вRedisКластер должен быть связан сkeyназначен на одну и ту же машину. Многие студенты здесь могут спросить, почему, но на самом деле это очень просто.Redisявляется однопоточным, еслиLuaсценарийkeyВыполняется на разных машинах, атомарность его выполнения не может быть гарантирована.
Решение состоит в том, чтобы найти принцип технологии фрагментации:
Шардинг данных — этоhashпроцесс: даkeyДелатьmd5,sha1Ждатьhashалгоритм, согласноhashЗначения присваиваются разным машинам.
Для того, чтобы добиться назначения ключей на одну и ту же машину, на один и тот жеhashзначение, то есть одно и то жеkey(ИзменятьhashАлгоритмы хороши, но сложнее). ноkeyЭто же нереально, потому чтоkeyВсе они имеют разное применение. но мы позволяемkeyЧасть того же достижима для реализации нашего бизнеса. Так ты можешь взятьkeyчасть расчетаhashШерстяная ткань? Ответ - да,
ЭтоHash Tag. Позволяет вычислять хэши с использованием неполных строк ключей. Когда ключ содержит {}, хешируется не весь ключ, а только строка, заключенная в {}. Предположим, алгоритм хеширования — sha1. Для пользователя:{user1}:ids и пользователя:{user1}:tweets хеш-значение равно sha1(user1).
резюме
Для вышеуказанного процесса оптимизации текущая работа по рефакторингу и разработке кода завершена, но официально она не запущена.После запуска я компенсирую улучшение производительности после оптимизации~