Внутренний обмен технологиями Bullet SMS: Redis

Redis

принцип

Redis — это «база данных» в памяти.В дополнение к хранилищу у него есть много мощных команд, которые выходят далеко за рамки определения базы данных, поэтому он официально называется «хранилищем структур данных», системой хранения структур данных. С помощью инструкций, предоставляемых Redis, мы можем реализовать такие функции, как кэширование, очереди сообщений, уведомления о событиях, списки лидеров, управление запасами и распределенные блокировки.

Базовая структура

Ядром Redis является однопроцессный однопоточный сервис, который реализует мультиплексирование ввода-вывода через epoll, select и т. д. и может параллельно обрабатывать сетевые события.

структура данных

Redis предоставляет следующие типичные структуры данных

strings

Redis реализует тип строки, называемый SDS (Simple Dynamic String), который отличается от строк C:

  1. Реализуйте конкатенацию строк, чтобы уменьшить перераспределение памяти.
  2. Длина строки поддерживается для быстрого доступа и во избежание переполнения буфера.
  3. Двоичный сейф, т.е. поддерживает хранение пробелов (\0)

linkedlist

Redis реализует дважды ациклический связанный список и реализует список, используя эту структуру данных.

Hashtable

Redis реализует HashMap, который соответствует собственному сценарию использования, то есть реализации массива плюс связанный список. Эта структура данных реализует типы данных Hash, Set в Redis. Особенности следующие:

  1. Использование алгоритма хеширования MurmurHash3 обеспечивает лучшее распределение строк с сильной регулярностью.
  2. Новый узел вставляется в заголовок, а не в нижний колонтитул, потому что кеш будет существовать в определенной степени, и функция «к кешу, добавленному позже, будет легче получить доступ, чем к кешу, добавленному ранее».
  3. Прогрессивная перефразировка. База данных Redis сама по себе представляет собой огромную хеш-таблицу.Каждый раз, когда для повторного хеширования требуется оперировать сотнями миллионов ключей, прогрессивное повторное хеширование является незаменимой гарантией. Способ повторного хеширования состоит в том, чтобы поддерживать две таблицы и индексы.Когда требуется повторное хеширование, установите для rehashIndex значение 0, а затем каждый раз, кроме операции вставки, данные в rehashIndex из oldTable будут передаваться в newTable до тех пор, пока rehashIndex == oldTable.length() - 1. Затем установите для rehashIndex значение -1, и перефразирование завершено.

skiplist

Список с пропусками достигает средней O(logN) и наихудшей O(N) временной сложности за счет наслоения связанного списка. Redis реализует тип данных Sorted Set, используя эту структуру данных. Кроме того, HashTable необходимо использовать в Sorted Set для реализации запроса O(1).

intset

Коллекция целых чисел, то есть коллекция, содержащая только целые числа. Redis реализует Set, используя эту структуру данных.

ziplist

Сжатый список. Сжатый список — это структура данных, которая жертвует производительностью и экономит место. По сравнению со связанным списком, он экономит место указателя. Redis использует его как реализацию List, Hash и Sorted Set и использует hash-max-ziplist-entries(512), hash-max-ziplist-value(64), list-max-ziplist-size(8 Kb), zset-max-ziplist-entries(128), zset-max-ziplist -value(64) настроить, чтобы решить, использовать ли ziplist.

Упорство

Независимо от того, является ли это базой данных в памяти или реляционной базой данных, недопустимо, чтобы данные не могли быть восстановлены после простоя или сбоя питания. У Redis есть два способа резервного копирования данных:

AOF

То есть Append-Only-File.Когда резервное копирование включено, Redis создаст файл с именем по умолчанию appendonly.aof. И записать все данные в памяти в файл в виде команд.Когда новая команда для работы с данными будет выполняться позже, она будет помещаться в буфер и регулярно записываться в файл (когда appendfsync не всегда). Настройте политику AOF в redis.conf со следующими параметрами:

appendonly yes/no 是否开启 AOF 模式
appendfilename appendonly.aof
appendfsync always/everysec/no #写入磁盘时机,always 表示每次都会同步到磁盘,由于是同步操作,性能下降严重。everysec 表示每秒刷盘。no 表示只放入缓存区中,由操作系统指定刷盘时机(Linux 一般是 30 秒)

Когда я выполнил следующую команду:

set liuzhiguo 123
set liuzhiguo abc
set liuzhiguo 456
set liuzhiguo 1231 ex 30

Файл AOF выглядит так:

*2 		消息行数
$6 		第一条消息长度
SELECT  消息内容
$1 		第二条消息长度
0		消息内容

*3		
$3
set
$9
liuzhiguo
$3
123

*3
$3
set
$9
liuzhiguo
$3
abc

*3
$3
set
$9
liuzhiguo
$3
456

*3
$3
set
$9
liuzhiguo
$4
1231

*3
$9
PEXPIREAT
$9
liuzhiguo
$13
1544420872751

Видно, что режим AOF напрямую записывает команду в файл, поэтому при восстановлении данных Redis будет выполнять команды одну за другой для восстановления данных. Поэтому эффективность восстановления данных в режиме AOF невелика, а при повторном нажатии клавиши необходимо выполнять все команды операции. В ответ на проблему повторных операций над одними и теми же данными Redis предоставляет функцию перезаписывания AOF, то есть отбрасывание исходного файла appendonly.aof и перезапись данных в памяти в файл по команде.

RDB

А именно Redis DataBase, этот режим сохранения включен по умолчанию. При запуске резервного копирования Redis создаст дочерний процесс (bgsave), создаст двоичный файл с именем dump.rdb по умолчанию и создаст резервную копию данных в памяти один за другим. Каждый раз, когда вы выполняете резервное копирование, исходный файл RDB будет удален, а полные данные будут сохранены снова. Что касается времени резервного копирования, в redis.conf есть следующие параметры для запуска резервного копирования:

save 900 1		900 秒内有 1 次变动
save 300 10		300 秒内有 10 次变动
save 60 10000	60 秒内有 10000 次变动

Благодаря своему размеру и естественной способности сжатия инструкций RDB может восстанавливать данные намного быстрее, чем AOF. Однако, поскольку каждый раз можно создавать только полные резервные копии, потребление ресурсов больше, чем у AOF, и он не такой гибкий, как AOF. И из-за неопределенности времени резервного копирования целостность данных не так хороша, как AOF.

RDB-AOF

Redis предложил сохранение смешанного режима RDB-AOF после версии 4.0, которую можно включить с помощью параметра aof-use-rdb-preamble в redis.conf. В этом режиме формат RDB будет использоваться при перезаписи полной резервной копии и AOF, а затем команда будет добавлена ​​к файлу в формате AOF.

Таким образом, производительность восстановления данных выше, чем у чистого AOF, полное резервное копирование выполняется быстрее, чем у AOF, объем резервной копии меньше, чем у AOF, а производительность частичного резервного копирования выше, чем у RDB.

Высокая доступность

Redis обеспечивает высокую доступность с помощью Sentinel и репликации

копировать

Настроив «slaveof ip port» в файле redis.conf или выполнив команду «slaveof ip port» для работающего узла redis, этот узел можно сделать подчиненным узлом экземпляра redis.

При запуске ведомого узла (slave) он отправляет команду синхронизации на главный узел (master).Главный узел использует метод bgsave для создания файла RDB и создания буфера для записи команды записи. Сгенерированный файл RDB будет отправлен на подчиненный узел, и подчиненный узел начнет загружать файл RDB, который выполняется синхронно. После того, как подчиненный узел завершит загрузку, главный сервер отправит буферную запись на подчиненный сервер, после чего всякий раз, когда главный узел выполняет команду, он будет распространять копию на подчиненный узел.

После отключения и повторного подключения подчиненный узел отправит команду psync главному узлу для выполнения частичной ресинхронизации, когда он снова выйдет в онлайн, а главный узел в течение этого периода отправит команду подчиненному узлу для выполнения. Для выполнения этой функции главный и подчиненный узлы поддерживают «смещения репликации».

Используйте информацию, чтобы увидеть статус репликации:

# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6380,state=online,offset=280,lag=0		// 从节点信息
master_replid:6088224db78515c7c2cbef387fb90cefd459f0d5
master_repl_offset:280											// 主节点偏移量
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:280


# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:1									// 与主节点 1 秒前同步
master_sync_in_progress:0										// 是否在进行 sync 同步
slave_repl_offset:280											// 从节点偏移量
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:6088224db78515c7c2cbef387fb90cefd459f0d5
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:280
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:280

Sentinel

Для достижения высокой доступности недостаточно только репликации, и подчиненный узел может автоматически заполниться после недоступности службы главного узла. Redis реализует мониторинг нод и координацию через Sentinel.Sentinel это специальная нода Redis.В конфигурационных файлах --sentinel и sentinel.conf нужно указать параметры при запуске, а в конфигурационном файле указать ip и хост мастер ноды. После запуска Sentinel отправит информационную команду главному узлу, получит соответствующую информацию о подчиненном узле и установит соединение с подчиненным узлом. Когда главный узел не отвечает, Sentinel ждет тайм-аута, указанного в конфигурации, а затем повышает подчиненный узел до главного. Когда главный узел снова запустится, Sentinel отправит команду slaveof главному узлу, попросив его стать подчиненным узлом.

Сам Sentinel также поддерживает высокую доступность.Несколько Sentinels будут публиковать свою собственную информацию на каждом узле master-slave, чтобы знать о существовании других Sentinels и устанавливать соединения. Когда несколько Sentinel сосуществуют, консенсус в отношении статуса и идентичности главного и подчиненного узлов будет иметь более сложный процесс координации, что является еще одной долгой историей.

Подробное введение в Sentinel см. по адресу: https://redis.io/topics/sentinel и в разделе «Проектирование и реализация Redis (второе издание)».

кластер

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

сегментация клиента

Например, ShardedJedis, путем хеширования ключа на клиенте и последующего назначения его указанному узлу. Преимущества: можно масштабировать без изменения Redis Недостатки: можно расширить только один раз и нельзя плавно обновить

Шардинг прокси-слоя

Например, Twemproxy. Прокси-уровень получает запрос клиента и передает его соответствующему узлу Redis, обычно используя согласованный хэш для сегментирования. А поскольку прокси-уровень можно унифицировать или считывать из одного и того же источника данных, прокси-уровень можно расширять. Преимущества: клиенту не нужно заботиться о состоянии сервиса Redis и не нужно шардировать. Недостаток: Сложно расширять.

Redis Cluster

Кластер, реализованный самой Redis, обеспечивает безболезненное расширение и плавную миграцию. Запуск режима кластера нужно настроить в конфигурационном файле:

cluster-enabled yes
cluster-config-file nodes.conf 
cluster-node-timeout 1500

В режиме кластера будет создано 16384 слота, и каждому узлу в кластере будет назначено свое количество слотов.Все слоты должны быть указаны для работы, и узел должен указать хотя бы один слот. Таким образом, теоретический максимум кластера Redis составляет 16384 узла.

Когда необходимо добавить/получить ключ, слот, в котором должен находиться ключ, получается через crc16(key) & 16384, а затем находится узел, в котором находится слот.Если узел выполняет его напрямую, иначе он будет вернуть на соответствующий узел клиента ip+порт.

Кластер Redis децентрализован, и синхронизация состояний между собой зависит от протокола сплетен для связи.Существуют следующие типы сообщений кластера:

  1. Встретиться. С помощью команды «кластер встречает ip-порт» существующий узел кластера отправит приглашение новому узлу присоединиться к существующему кластеру.
  2. Пинг. Каждую секунду узел отправляет сообщение ping другим узлам в кластере.Сообщение содержит адреса, слоты, информацию о состоянии и время последней связи двух известных ему узлов.
  3. Понг. После того, как узел получит сообщение ping, он ответит сообщением pong, которое также содержит информацию о двух узлах, которую он уже знает.
  4. Потерпеть поражение. После того, как узлу не удается пропинговать узел, он рассылает сообщение о том, что узел умер, всем узлам в кластере. Другие узлы отключаются после получения сообщения.

Redis Cluster выбирает конечную согласованность и базовую доступность благодаря механизмам децентрализации и связи. Например, когда новый узел добавляется (встречается), об этом знают только приглашающий узел и приглашенный узел, а остальным узлам приходится ждать, пока пинг-сообщение распространится слой за слоем. За исключением Fail, о котором немедленно уведомляется вся сеть, другие узлы, такие как новые узлы, повторное подключение узла, выбор подчиненного узла в качестве главного узла, изменения слотов и т. д., должны ждать уведомления.

Поэтому из-за протокола сплетен Redis Cluster предъявляет высокие требования к серверному времени, иначе неточная временная метка повлияет на достоверность оценки сообщения узлом. Кроме того, сетевые накладные расходы после увеличения количества узлов также будут оказывать давление на сервер. Поэтому официально рекомендуемое максимальное количество узлов составляет 1000. Для работы и обслуживания кластера Redis вы можете обратиться кКраткий обзор опыта эксплуатации и обслуживания кластера Redis Youku Blue Whale с почти 1000 узлов.

преимущество:

  1. Истинное упругое расширение и сжатие.
  2. Это не влияет на использование во время расширения.

недостаток:

  1. Отсутствие платформы управления.
  2. Клиент должен быть совместимым.
  3. Некоторые команды не поддерживаются

После того, как Redis решит расширение через Cluster, как клиент должен его использовать? Например, JedisCluster извлекает информацию о кластере узла перед каждым запросом, чтобы вычислить, какой узел следует запрашивать, и должен обрабатывать сообщение ASK, возвращенное неправильным узлом. В результате проблема

  1. Не менее двух запросов на операцию.
  2. Если каждый раз запрашивается только один узел, также будет формироваться единая точка давления.

Для проблемы 1 решение состоит в том, чтобы клиент кэшировал состояние кластера. Что касается вопроса 2, JedisCluster поддерживает конфигурацию нескольких узлов.При извлечении информации об узле узел будет выбран случайным образом для распределения нагрузки. Чтобы справиться с проблемой 2, необходимо синхронизировать информацию узла Redis с конфигурацией клиента, что приводит к связыванию.

Другая проблема заключается в том, что такие команды, как mget и mset, которые необходимо выполнять на узлах, не поддерживаются в состоянии кластера. Решение этой проблемы заключается в добавлении слоя Proxy, что рекомендуетсяПуть к платформизации сервиса Redis от Youku Tudou, его идея использования Nginx + Redis Cluster потрясающая, и он реализует проблему выполнения команд между узлами путем агрегации запросов.

Сервис Redis, предоставляемый Alibaba Cloud, также реализует межузловые команды в кластерном режиме, используя прокси + сервер шардинга + сервер конфигурации шардинга (вероятно, zookeeper), но не использует механизм Redis Cluster, а реализует собственный «шардинг», сохраняя слот. Преимущество Redis от Alibaba Cloud заключается в том, что кластерная версия не требует совместимости с клиентом и может использоваться как автономный Redis.

Сторонняя магическая модификация Redis

Прежде чем дождаться официального кластерного решения Redis, люди не могут дождаться кластерной версии Redis, и некоторые люди, недовольные статус-кво и реализацией Redis Cluster, начали трансформировать Redis. Вышеупомянутый Alibaba Cloud Redis также относится к Redis после волшебной реформы.

Codis

Codis — это чуть ли не самый известный сторонний Redis с большим количеством обновлений Redis. Его архитектура — zookeeper + proxy + server-group (master + slave), а для визуальной работы и обслуживания предусмотрена консоль.

Запишите доступные прокси-узлы через zookeeper, а затем используйте клиент Jodis, модифицированный командой разработчиков Codis на основе Jedis, чтобы найти доступные прокси-узлы в zookeeper для вызова. Если вы используете jedis или другие клиенты, вы можете подключиться только к прокси-серверу или найти способ подключиться к zookeeper, чтобы получить узел, а затем выполнить опрос.

Codis поддерживает эластичное расширение, а метод сегментирования аналогичен методу Redis Cluster.crc32(key) % 1024Разделенный на 1024 слота, каждый экземпляр сохраняет данные соответствующего слота.

LedisDB и SSDB

LedisDB и SSDB очень похожи, оба используют нижний уровень LevelDB для повторной реализации Redis или реализуют только протокол Redis. Благодаря методу многопоточности + жесткий диск достигается производительность QPS, аналогичная производительности Redis на одной машине, и емкость может быть увеличена в значительной степени. Отношения между LedisDB/SSDB и Redis эквивалентны отношениям между TiDB и MySQL.

Недостаток в том, что есть преимущество в стоимости в емкости, и нет никакого другого преимущества.

дела

Предоставлено Редисомwatch,multi,execи другие методы реализации оптимистичных блокирующих транзакций. Поток использования транзакций выглядит следующим образом:

  1. watch key1 key2
  2. многократная открытая транзакция
  3. установить ключ1 значение1, установить ключ2 значение2, чтобы поставить команду в очередь.
  4. exec, выполнить команду.

Если key1/key2 были изменены другими клиентами между multi и exec, во время exec будет возвращено nil, а set key1 value1 и set key2 value2 не будут выполняться. Redis сохранит словарь watch_keys со структурой: client -> keys, is_dirty. Когда Redis обрабатывает каждую команду, которая изменяет данные, он проверяет, существует ли ключ в watch_keys, и если да, то изменяет is_dirty на true.

Когда клиент, выполняющий транзакцию, выполнит exec, он проверит поле is_dirty, и если оно окажется ложным, все накопленные инструкции будут сразу отброшены и не будут выполняться.

Транзакции не используются во многих сценариях в Redis. В случае большого параллелизма требуется повторная попытка. В большинстве случаев есть лучшие способы их использования:

Lua

Redis обеспечивает поддержку скриптов Lua, атомарно выполняет ряд инструкций и может писать код для вынесения логических суждений. Например, сценарий, требующий вставки большого объема данных:

for i=1,10000000,1 do
    local num = math.random(1000000,999999999);
    redis.call("set",num,i)
end

На этой машине, QPS 83w, потребовалось около 12 секунд, чтобы выполнить 10 миллионов команд. Redis является однопоточным при выполнении сценариев Lua и не может обрабатывать другие запросы, поэтому Redis является атомарным. Ниже приведен сценарий Lua, реализованный с использованием этой функции при захвате красного конверта:

// 该脚本传入 4 个参数
// KEYS[1] = 未领取的红包列表 key
// KEYS[2] = 已领取的红包列表 key
// KEYS[3] = 红包已领取人ID列表 key
// KEYS[4] = 领取人ID

// 检查领取人是否在已领取列表内
if redis.call('hexists', KEYS[3], KEYS[4]) ~= 0 then
	return nil
else
	// 取出一个未领取的红包
	local redEnvelop = redis.call('rpop', KEYS[1]);
	if redEnvelop then
		// 红包中的 receiver 填入领取人 ID
		local x = cjson.decode(redEnvelop);
		x['receiver'] = KEYS[4];
		local re = cjson.encode(x);

		// 领取人放入已领取人ID列表,将红包放入已领取红包列表
		redis.call('hset', KEYS[3], KEYS[4], KEYS[4]);
		redis.call('lpush', KEYS[2], re);

		// 给相应的 key 续期
		if redis.call('llen', KEYS[2]) == 1 then
			redis.call('expire', KEYS[2], 172800);
		end
		if redis.call('hlen', KEYS[3]) == 1 then
			redis.call('expire', KEYS[3], 172800);
		end
		return re;
	end
end
return nil

Следует отметить, что, поскольку сценарии Lua могут выполняться только на одном экземпляре Redis, когда Lua выполняется в состоянии кластера, Redis проверит исполняемый ключ. Чтобы гарантировать, что все ключи должны находиться на определенной машине, Redis ограничивает, что все ключи должны находиться в одном и том же слоте.

Таким образом, для сценария с красным конвертом ключ, переданный в Lua, создаетсяxxx{redpacketId}обработки, чтобы убедиться, что все ключи попадают в слот.

трубопровод

Redis поддерживает использование конвейеров для пакетного выполнения команд, а затем единообразно возвращает их, чтобы уменьшить количество циклов обработки.Обычно он используется для пакетной вставки данных и пакетного получения данных.

настоящий бой

тайник

режим кэширования

Кэширование — наиболее распространенный вариант использования Redis. Обычно процесс кэширования таков:

  1. Пропустить: получить данные из источника данных и поместить их в кеш.
  2. хит: вернуть данные.
  3. Обновление: сначала сохраните данные в базе данных, а затем сделайте кэш недействительным.

Не рекомендуется одновременно обновлять данные в кэше, поскольку одновременные обновления могут привести к искажению данных. ВидетьПочему Facebook удаляет кеш, а не обновляет его?а такжеScaling Memcache at Facebook, в котором упоминается: «Мы выбираем удаление кэшированных данных вместо их обновления, потому что удаление является идемпотентным». Но удаление кеша не полностью освобождает от грязных данных, но вероятность относительно мала.

Пакетный запрос

При запросе может потребоваться ситуация, например, где id в (xx,yy,zz), и в этом случае можно использовать кеш запросов.mgetОдновременный запрос нескольких ключей может значительно повысить эффективность. Вот эталонные данные:

get 81833.06 requests per second	
mget 10 73475.39 requests per second		  734,753
mget 20 64226.07 requests per second		  642,260			
mget 30 59559.26 requests per second		1,786,770			99%   < 1 milliseconds
mget 50 48995.59 requests per second		2,449,750			99%   < 1.5 milliseconds
mget 100 29214.14 requests per second		2,921,414			99%   < 2.5 milliseconds
mget 200 16730.80 requests per second		3,346,000			99%   < 3 milliseconds
mget 500 7222.30 requests per second		3,611,150			99%   < 9 milliseconds

Согласно общему количеству полученных данных и среднему времени отклика обычно считается, что количество управляемых мгетов ниже 100 является относительно сбалансированным.

100 раз на mget по сравнению с get, что соответствует 35-кратному повышению производительности. В сочетании с затратами времени на двусторонние вызовы между машинами фактическое повышение производительности, вероятно, будет более чем в 100 раз.

Распределенная блокировка

Редис может бытьSET key randomValue NX EX 30Присвойте значение ключу и в то же время определите, существует ли ключ, и заданное время истечения срока действия. Срок истечения варьируется в зависимости от бизнеса.

Высвобождение замка может напрямую управлять ключом. Но дело рискованно:

Например, A получает блокировку со сроком действия 30 секунд. По какой-то причине запрос не мог быть обработан за 30 секунд, и пришел B и получил блокировку. К этому моменту A завершил операцию снятия блокировки, и блокировка, удерживаемая B, будет снята.

Чтобы избежать этой проблемы, необходимо определить, является ли значение значением множества, и если да, то выполнить операцию del. Чтобы выполнить эти две команды атомарно, вам нужно использовать скрипт lua:

- KEYS[1] 为 锁名称,ARGV[1] 为锁内容, 即 set 时的 randomValue
if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end

Кроме того, в реализации spring-data-redis нетset nx ex, поэтому вам нужно найти Jedis или Lettuce для вызова нативного метода.

прилавок

Еще одна похвальная команда Redis — автоинкремент, который предоставляет методы incr/incrby/incrbyfloat(string), hincrby/hincr(hash) и цинкrby(zset) для разных типов данных.

Эти команды можно использовать для вычета инвентаря, записи частоты доступа к интерфейсу, записи количества лайков, комментариев и репостов статьи, а также количества вычитаемых красных пакетов.

Таблица лидеров

Используйте упорядоченный список zset, например, для расчета рейтинга очков пользователей:

  • zadd/zincrby сохраняет или автоматически увеличивает баллы пользователя
  • zrevrank Получить ранг пользователя
  • zscore Получить баллы пользователя
  • zrevrange получить рейтинг

очередь сообщений

используя списокlpush(левое нажатие) иbrpopИнтерфейс (Blocked Right Pop) может реализовать функцию очереди сообщений:

  1. поместите сообщениеlpushк очереди.
  2. Все экземпляры проходятbrpopСлушайте очередь и вынимайте данные для потребления.

В процессе потребления скорость потребления можно определить в соответствии с бизнес-ситуацией, настроив пул потоков.

Асинхронная ленивая очередь слияния

Например, при захвате и захвате красных пакетов данные инвентаризации должны храниться асинхронно. Но просто асинхронного хранения недостаточно, и количество операций с базой данных не уменьшится. В это время может потребоваться сжатие 100 запросов в один запрос, а в базу данных берутся только последние данные.

Таких требований можно достичь с помощью zset + list, нам нужно несколько вещей:

  1. Задачи, которые необходимо отложить, помещаются в список zset, а оценка — это временная метка, которую необходимо выполнить.
  2. Поток в фоновом режиме извлекает zset каждую секунду, выполняет zrangeByScore, диапазон оценок составляет 0 ~ текущая временная метка, если данные получены, они помещаются в список очереди выполнения и, наконец, данные, найденные zrem.
  3. Исполнитель, который прослушивает очередь списка и начинает выполнение задач в этот момент.

Так же реализовано асинхронное обновление в красном конверте. При ограблении групповых красных конвертов, если данные в базе каждый раз обновляются, время отклика обязательно увеличится. Если вы используете этот метод обновления, он будет обновлен только через 30 секунд после последнего захвата красного конверта, а обновления данных, которые происходят в течение 30 секунд, будут объединены только в одно.

уведомление о событии

Redis предоставляетpublish,subscribeКогда команда реализует функцию широковещания, сообщение может быть уведомлено на определенный канал (канал) при публикации, и все узлы, которые подписаны на этот канал, могут получить сообщение.

С помощью этого механизма мы можем добиться уведомления о событии для всего узла.

Например, в системе начисления баллов все данные конфигурации базы данных, такие как действия, розыгрыши лотерей, проверки, дойные коровы и т. д., будут помещены в кеш JVM для достижения максимальной производительности. Для обновления данных база данных сначала обновляется каждую минуту. Но проблема в том, что время обновления каждого экземпляра отличается, в результате чего запрос к экземпляру A данных и экземпляру B отличается. Затем конфигурация запланированной задачи была изменена на выполнение в 0-ю секунду каждой минуты, что значительно улучшило проблему.

Но метод опроса все еще недостаточно элегантен, а конфигурация, полученная большинством из нас, не имеет изменений, бесполезна. Время обновления конфигурации должно быть настроено на изменение.

В настоящее время можно использовать широковещательную рассылку Redis.Всякий раз, когда данные базы данных изменяются, все узлы уведомляются об обновлении данных через широковещательную рассылку или просто помещают данные для обновления в широковещательную рассылку.

оптимизация

Несмотря на то, что Redis обладает высокой производительностью, из-за однопоточной функции при возникновении медленного запроса все операции будут заблокированы. Поэтому все же нужно обращать внимание на то, на какие ямы вы будете наступать. Redis предоставляет slowlog для просмотра медленных запросов.

Общие минные поля

  • keys *Временная сложность команды keys равна O(n), где n — количество всех ключей в Redis, и это наиболее распространенная команда с наихудшей производительностью. В общем, заблокируйте эту команду онлайн (добавьте команду переименования KEYS "" в конфигурацию)

  • большой ключ. Чем больше данных хранится в ключе, тем хуже производительность, например, lindex и lrange для очень большого списка. Кроме того, большое значение заблокирует миграцию данных кластера и может вызвать отработку отказа. Он будет блокироваться даже при удалении, например, удаление набора с данными 1 кВт займет 5 секунд. Или большие ключи в кластере вызовут неравномерное распределение памяти в кластере. Поэтому вам нужно избегать помещения слишком большого количества данных в ключ при его использовании.

  • bgrewriteaof,bgsave, при перезаписи файла aof и резервном копировании файла RDB будет разветвлен дочерний процесс и память, которая блокируется в этот период, в зависимости от объема памяти Redis и производительности машины. Поэтому практика многих предприятий заключается в том, чтобы закрывать aof и rdb на главном узле, а резервное копирование — только на подчиненном узле.

Разделение больших ключей

В системе баллов есть дань-задание, приглашающий может получить вознаграждение за выполнение задания приглашенным, и оно будет начисляться рано утром каждый день.

Для этой задачи первая оптимизация, которую мы делаем, заключается в том, чтобы каждый день сохранять пользователей, которые получали награды в наборе, проходить через sscan пользователей, которым нужно платить дань, и выполнять задачу. Это позволяет избежать сканирования библиотеки и гарантирует, что идентификатор пользователя, полученный каждый раз, является точным и действительным. Проблема в том, что в случае, если текстовое сообщение станет популярным, в наборе будет больше userId, и возникнет проблема больших ключей, набор необходимо разбить на несколько наборов.

Идея разделения аналогична шардингу кластера Redis.По хэшу(userId)% count получается количество осколков от 0 до count и добавляется к исходному ключу.Процесс выглядит следующим образом:

  1. Получите количество осколков по хэшу (идентификатору пользователя) %, например 16
  2. Исходный ключ "TRIBUTE:USER:SET:20181225", добавьте количество осколков, чтобы получить "TRIBUTE:USER:SET:20181225:16", а затем введите в него userId sadd
  3. Когда все ключи вынуты, цикл for прописывается от 0-count до ключа, а затем выполняется sscan для каждого ключа.

использовать хэш

Например, при сохранении количества лайков, репостов и комментариев к статье их можно сохранить как три значения, а именно: статья:лайк, статья:репост и статья:комментарий. Его также можно сохранить как хэш-объект, ключ — статья, а хэш-ключ — лайк, репост, комментарий.

выгода:

  1. Необходимые данные можно получить через hgetall
  2. Сохранить память.

Использовать значение:

# lua
for i=1,1000000,1 do
    redis.call("set","article:like:"..i,1)
    redis.call("set","article:repost:"..i,1)
    redis.call("set","article:comment:"..i,1)
end

# memory
used_memory:226568704
used_memory_human:216.07M
used_memory_rss:282144768
used_memory_rss_human:269.07M

Используйте хэш:

# lua
for i=1,1000000,1 do
    redis.call("HMSET","article:"..i, "like", 1, "repost", 1, "comment", 1)
end

# memory
used_memory:121402896
used_memory_human:115.78M
used_memory_rss:132640768
used_memory_rss_human:126.50M

value использует почти вдвое больше памяти. Причина в том, что в настоящее время тип хэша будет выбирать реализацию структуры данных ziplist.