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

时间:2014-01-27 19:37:38

标签: mysql design-patterns pool

我有一个包含2000行的资源表。 引擎是innodb。有一个'free_at'字段(索引)。 在每个请求上,我需要锁定表,获取一个免费资源(按'free_at'列排序),将该行更新为非空闲,并释放锁。

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

现在大约有800个进程不断地从表中请求资源(每10-15秒一次,所以平均值高达80秒)。

我的瓶颈是锁定等待时间,每次请求的时间范围为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

2 个答案:

答案 0 :(得分:0)

您需要锁定表中的行,而不是整个表。正如您所发现的那样,锁定整个表格并不会扩大规模。

坚持使用InnoDB。 MEMORY和MyISAM访问方法不会这样做。

假设您的resource表包含以下列(可以包含其他列):

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)

答案 1 :(得分:0)

我设法通过内部缓冲资源解决了这个问题。我不是一次获取1个资源,而是取出其中的5个,从而将该表上的调用和锁定量减少了5倍。