mysql - 调整 MySQL 配置以支持锁定同一表的数百个连接

标签 mysql design-patterns pool

我有一个包含 2000 行的资源表。 引擎是innodb。有一个“free_at”字段(已索引)。 对于每个请求,我需要锁定表,获取空闲资源(按“free_at”列排序),将该行更新为非空闲,然后释放锁定。

这是一个基本的池实现,我一直在使用,并且在 100-200 个连接和少于 1000 行(池中的资源)的情况下运行良好。

现在大约有 800 个进程不断地从表中请求资源(每个进程每 10-15 秒一次,因此平均可达 80\s)。

我的瓶颈是锁等待时间,每个请求的等待时间在 30 到 60 秒 (!) 之间。我确信我应该更改一些配置以使其锁定和释放更快。

我尝试将引擎类型更改为 MEMORY,但这并没有改善锁定等待时间。

我是否应该寻找另一个不基于 MySQL 且可以按优先级分配资源的池解决方案(在我的例子中是“free_at”字段)?

编辑:

我使用的锁定 LOCK TABLES table_name WRITE

然后选择 SELECT * FROM table_name WHERE (free_at < NOW() OR free_at is null) ORDER BY free_at ASC

更新“free_at”字段 UPDATE table_name SET free_at = NOW() + INTERVAL 5 MINUTE WHERE id= 1234

终于解锁了 UNLOCK TABLES

表架构

`resources` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `free_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `free_at` (`free_at`),
) ENGINE=InnoDB

最佳答案

您需要锁定表中的行,而不是整个表。正如您所发现的,锁定整个表不会扩展。

坚持使用 InnoDB。 MEMORY 和 MyISAM 访问方法不会执行此操作。

假设您的资源表具有以下列(它可以有其他列):

resource_id int not null primary key
free        int not null 1 means free, 0 means in use
free_at     timestamp not null 

假设您想要一个事务来获取最旧的可用资源行 (free = 1),那么您需要对每个需要资源的客户端执行以下操作。

 START TRANSACTION;

 SELECT resource_id
   FROM resource
  WHERE free = 1
  ORDER BY free_at ASC
  LIMIT 1
    FOR UPDATE;

此时,您的应用程序将获得一个您可以分配的resource_id,或者什么也得不到。如果没有得到,则意味着它必须等待并重试。

如果您有resource_id,请更新它以表明它正在使用中。

 UPDATE resource
    SET free = 0,
        free_at = NOW()
  WHERE resource_id = 'the resource ID you just got';

然后,尽快做

 COMMIT;

完成您启动的事务并释放该行上的锁。这种分配资源的方式比锁定表效果更好,因为每个连接只需要锁定自己的行。

如果您没有SELECT ... FOR UPDATE查询中获取资源ID,您需要立即执行

 ROLLBACK;

取消您在该行上启动的事务。然后你需要等待并重试。等待一段有意义的时间:至少是应用程序使用您的资源之一并释放它所花费的时间。如果您等待的时间较短,您的许多连接将占用资源表并减慢速度。如果您需要更多资源,请添加一些。

当您的软件使用完分配的资源后,执行此操作将其释放回池中。您可以在自动提交模式下执行此操作;这里不需要显式事务。但请确保您的客户端为此打开了自动提交模式。

 UPDATE resource
    SET free = 1
  WHERE resource_id = 'the resource ID you have been using';

请注意;我不太明白你是如何使用 free_at 所以我可能有这部分逻辑错误。

通过在

上添加复合索引(覆盖索引),查找空闲资源的 SELECT 操作很可能会加速。
 (free, free_at, resource_id)

关于mysql - 调整 MySQL 配置以支持锁定同一表的数百个连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21390030/

相关文章:

python - 为什么 pool.map() 和 map() 返回不同的结果?

mysql - mysql中如何按月、日、小时对数据进行分组?

c# - 从数据库中检索多行

php - 从具有相同名称的多行中获取结果

c# - 关于接口(interface)实现和可扩展性的架构问题

java - 如果启动了较新的实例,则终止程序的较旧实例

java - 这段代码是适配器模式的示例吗?

c# - 资源池的正确实现方式

php - MySql插入高CPU负载

java - 最佳 Java 线程安全对象池