Интервьюер: Как вы обеспечиваете согласованность данных в сценариях с высокой степенью параллелизма?

Java
Интервьюер: Как вы обеспечиваете согласованность данных в сценариях с высокой степенью параллелизма?

В интервью всегда так.

1. Анализ сценариев

Интервьюер: Каков QPS вашего сервиса?

Я: В пиковый период работы нашего сервиса количество посещений достаточно большое, около 30 000.

Интервьюер: Может ли ваш сервер поддерживать такой большой объем трафика? Кэш есть?

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

Интервьюер: Часть данных хранится в двух местах. При обновлении данных, как вы обеспечиваете согласованность данных?

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

Обычно существует четыре метода обеспечения согласованности данных:

  1. Сначала обновите кеш, а затем обновите базу данных.
  2. Сначала обновите базу данных, а затем обновите кеш.
  3. Сначала удалите кеш, а затем обновите базу данных.
  4. Сначала обновите базу данных, а затем удалите кеш.

Подробно рассматривается каждый вариант:

2. Решения

2.1 Сначала обновите кеш, затем обновите базу данных

Если одновременно приходят два одновременных запроса на запись, процесс выполнения выглядит следующим образом:

数据一致性1.jpg

  1. Напишите запрос 1 для обновления кеша, установите возраст равным 1
  2. Напишите запрос 2 для обновления кеша, установите возраст равным 2
  3. Напишите запрос 2 для обновления базы данных, установите возраст 2
  4. Напишите запрос 1 для обновления базы данных, установите возраст равным 1

Результатом выполнения является то, что возраст в кеше установлен на 2, а возраст в базе данных установлен на 1, что приводит к несогласованности данных.Это решение невозможно.

2.2 Сначала обновите базу данных, затем обновите кеш

Если одновременно приходят два одновременных запроса на запись, процесс выполнения выглядит следующим образом:

  1. Напишите запрос 1 для обновления базы данных, установите возраст равным 1
  2. Напишите запрос 2 для обновления базы данных, установите возраст 2
  3. Напишите запрос 2 для обновления кеша, установите возраст равным 2
  4. Напишите запрос 1 для обновления кеша, установите возраст равным 1

Результатом выполнения является то, что возраст в базе данных установлен на 2, а возраст в кэше установлен на 1, что приводит к несогласованности данных.Это решение невозможно.

2.3 Сначала удалите кэш, затем обновите базу данных

Если одновременно приходят два одновременных запроса на чтение и запись, процесс выполнения выглядит следующим образом:

数据一致性2.jpg

  1. Запрос на запись удаляет кеш
  2. В кеше запроса запроса на чтение нет данных, затем запросите базу данных, а затем запишите данные в кеш.
  3. написать запрос на обновление базы данных

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

2.4 Сначала обновите базу данных, затем удалите кеш

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

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

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

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

На самом деле вероятность такой ситуации очень мала.Кэш записи на несколько порядков быстрее, чем запись БД.Кэши чтения и записи - это все операции с памятью, и скорость очень высокая.

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

3. Резюме и размышления

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

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

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

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

  1. двухэтапная фиксация
  2. TCC
  3. локальная таблица сообщений
  4. Сообщение транзакции MQ
  5. ПО промежуточного слоя для распределенных транзакций

В следующей статье мы подробно разберем преимущества и недостатки этих решений.