Данные в реальном времени одновременно записываются для оптимизации 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).
резюме
Для вышеуказанного процесса оптимизации текущая работа по рефакторингу и разработке кода завершена, но официально она не запущена.После запуска я компенсирую улучшение производительности после оптимизации~