Mysql High Concurrency Updates - PullRequest
       8

Mysql High Concurrency Updates

0 голосов
/ 11 марта 2019

У меня есть таблица mysql:

CREATE TABLE `coupons` (
    `id` INT NOT NULL AUTO_INCREMENT,
    `code` VARCHAR(255),
    `user_id` INT,
    UNIQUE KEY `code_idx` (`code`)
) ENGINE=InnoDB;

Таблица состоит из тысяч / миллионов кодов, и изначально user_id для всех равно NULL.Теперь у меня есть веб-приложение, которое присваивает уникальный код тысячам пользователей, одновременно посещающих приложение.Я не уверен, как правильно справиться с этим, учитывая очень высокий трафик.Я написал следующий запрос:

UPDATE coupons SET user_id = <some_id> where user_id is NULL limit 1;

И приложение запускает этот запрос, скажем, с параллелизмом 1000 рэк / сек.

Я заметил, что вся таблица заблокирована, и этоплохо масштабируется.Что я должен делать?Благодаря.

Ответы [ 2 ]

0 голосов
/ 12 марта 2019

Этот вопрос может быть более подходящим для администраторов баз данных (а я не администратор баз данных), но я постараюсь дать вам некоторые идеи о том, что происходит.

InnoDB фактически не блокирует всю таблицу, когда вы выполняете запрос на обновление. Что он делает, так это следующее: он устанавливает блокировку записи, которая не позволяет любой другой транзакции вставлять, обновлять или удалять строки, где значение coupons.user_id равно NULL.

С вашим запросом на данный момент (который зависит от user_id, равного NULL), вы не можете иметь параллелизм, потому что ваша транзакция будет выполняться одна за другой, а не параллельно. Даже индекс на вашем coupons.user_id не поможет, потому что при установке блокировки InnoDB создаст для вас теневой индекс, если у вас его нет. Результат будет таким же.

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

  1. Назначить пользователя на купон в асинхронном режиме. Поместите все запросы о назначении в очередь, затем обработайте очередь в фоновом режиме. Может не подходить для ваших бизнес-правил.
  2. Уменьшить количество заблокированных записей. Идея заключается в том, чтобы при выполнении обновления заблокировать как можно меньше записей. Для этого вы можете добавить в таблицу один или несколько индексированных столбцов, а затем использовать индекс в предложении WHERE запроса Update.

Примером столбца является product_id или категория, может быть местоположение пользователя (страна, почтовый индекс). тогда ваш запрос будет выглядеть примерно так:

ОБНОВЛЕНИЕ купонов SET user_id = WHERE product_id = user_id равен NULL LIMIT 1;

И теперь InnoDB будет блокировать только записи с product_id = <product_id>. Таким образом, вы будете иметь параллелизм.

Надеюсь, это поможет!

0 голосов
/ 12 марта 2019

Как понятно, coupons заполняется заранее, а null user_id обновляется до значения, которое не равно нулю.

explain update coupons set user_id = 1 where user_id is null limit 1;

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

Добавление индекса к coupons.user_id, например, меняет стратегию MySQL.

create unique index user_id_idx on coupons(user_id);
 explain update coupons set user_id = 1 where user_id is null limit 1;
+----+-------------+---------+------------+-------+---------------+-------------+---------+-------+------+----------+------------------------------+
| id | select_type | table   | partitions | type  | possible_keys | key         | key_len | ref   | rows | filtered | Extra                        |
+----+-------------+---------+------------+-------+---------------+-------------+---------+-------+------+----------+------------------------------+
|  1 | UPDATE      | coupons | NULL       | range | user_id_idx   | user_id_idx | 5       | const |    6 |   100.00 | Using where; Using temporary |
+----+-------------+---------+------------+-------+---------------+-------------+---------+-------+------+----------+------------------------------+
1 row in set (0.01 sec)

Таким образом, вы должны работать с администратором баз данных, чтобы обеспечить оптимизацию объекта базы данных. Компромиссы должны быть рассмотрены.

Кроме того, поскольку у вас есть клиентское приложение, у вас есть возможность предварительно извлечь null coupons.user_id и выполнить обновление непосредственно на coupons.id. Любопытно услышать о вашем решении.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...