Данные в реальном времени одновременно записываются в оптимизацию Redis

Redis Lua

Данные в реальном времени одновременно записываются для оптимизации Redis — исходная ссылка

Как быстро обновить одновременные запросы без принудительных блокировок? Вот горячая онлайн-практика...

задний план

Логика текущей архитектуры состоит в том, чтобы записывать данные параллельного запроса в очередь, а затем запускать отдельный асинхронный поток для последовательной обработки данных. Преимущество этого метода в том, что нет необходимости рассматривать проблему параллелизма, конечно же, очевидны и его недостатки~

Оптимистическая блокировка реализует одновременные обновления данных.

Согласно текущим бизнес-данным, обновляемым за считанные секунды,keyс низкой частотой столкновений. Автор намерен использоватьCASОптимистичная схема запирания: использованиеLuaРеализация скриптаRedisАтомарные обновления данных, даже в случае параллелизма, его производительность будет на уровне. Ниже представлена ​​блок-схема оптимистичной блокировки CAS для обеспечения одновременного обновления данных:

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).

резюме

Для вышеуказанного процесса оптимизации текущая работа по рефакторингу и разработке кода завершена, но официально она не запущена.После запуска я компенсирую улучшение производительности после оптимизации~

Подписывайтесь на нас

关注我们