Mysql高并发更新

时间:2019-03-11 20:12:32

标签: mysql concurrency

我有一个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。 现在,我有了一个Web应用程序,它为同时访问该应用程序的数千个用户分配了唯一的代码。考虑到流量很高,我不确定什么是正确的处理方式。 我写的查询是:

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

应用程序以1000 req / sec的并发性运行此查询。

我观察到的是整个表被锁定了,而且扩展性不好。 我该怎么办? 谢谢。

2 个答案:

答案 0 :(得分:0)

据了解,coupons已预先填充,而null user_id已更新为非null。

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)

因此,您应该与DBA合作以确保优化数据库实体。需要权衡。

此外,由于您具有客户端应用程序,因此您有机会预提取null coupons.user_id并直接在coupons.id上进行更新。很好奇您的解决方案。

答案 1 :(得分:0)

这个问题可能更适合DBA(但我不是DBA),但我会尽力为您提供一些有关正在发生的事情的想法。

在执行更新查询时,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>锁定记录。这样,您将具有并发性。

希望这会有所帮助!