我有一个包含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
答案 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倍。