调整MySQL配置以支持数百个锁定同一表的连接
我有一个2000行的资源表。 引擎是innodb。有一个“free_at”字段(索引)。 在每个请求中,我需要锁定表,获取一个空闲资源(按“free_at”列排序),将该行更新为non free,然后释放锁 这是我一直在使用的一个基本池实现,在100-200个连接和不到1000行(池中的资源)的情况下运行良好 目前大约有800个进程不断地从表中请求资源(每10-15秒一次,因此平均高达80秒) 我的瓶颈是锁等待时间,每个请求的等待时间在30到60秒之间(!)。我肯定有一些配置我应该改变,使其锁定和释放更快 我尝试将引擎类型更改为内存,但这并没有改善锁定等待时间 我是否应该寻找另一个不是基于MySQL的池解决方案,并且可以按优先级分配资源(在我的例子中是“free_at”字段) 编辑: 我用它来锁调整MySQL配置以支持数百个锁定同一表的连接,mysql,design-patterns,pool,Mysql,Design Patterns,Pool,我有一个2000行的资源表。 引擎是innodb。有一个“free_at”字段(索引)。 在每个请求中,我需要锁定表,获取一个空闲资源(按“free_at”列排序),将该行更新为non free,然后释放锁 这是我一直在使用的一个基本池实现,在100-200个连接和不到1000行(池中的资源)的情况下运行良好 目前大约有800个进程不断地从表中请求资源(每10-15秒一次,因此平均高达80秒) 我的瓶颈是锁等待时间,每个请求的等待时间在30到60秒之间(!)。我肯定有一些配置我应该改变,使其锁定
LOCK TABLES\u name WRITE
然后选择
SELECT*FROM table_name,其中(free_at
更新“free_at”字段
updatetable\u name SET free\u at=NOW()+间隔5分钟,其中id=1234
最终解锁
解锁表格
表模式
`resources` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`free_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `free_at` (`free_at`),
) ENGINE=InnoDB
`资源`(
`id`int(11)非空自动增量,
`在`日期时间默认值为空,
主键(`id`),
键'free\u at`('free\u at`),
)引擎=InnoDB
您需要锁定表中的行,而不是整个表。正如您所发现的那样,锁定整个表不会按比例放大 坚持使用InnoDB。内存和MyISAM访问方法不这样做 假设您的
资源
表有以下列(它可以有其他列):
假设您需要一个事务来获取最早的可用资源行(free
=1),下面是您对每个需要资源的客户机所做的操作
START TRANSACTION;
SELECT resource_id
FROM resource
WHERE free = 1
ORDER BY free_at ASC
LIMIT 1
FOR UPDATE;
此时,您的应用程序将获得一个单一的资源id(您可以分配该资源id),或者一个也不会得到。如果没有,这意味着它必须等待并重试
如果你有一个资源id,更新它以表明它正在使用中
UPDATE resource
SET free = 0,
free_at = NOW()
WHERE resource_id = 'the resource ID you just got';
然后,尽可能快地去做
COMMIT;
要完成您启动的事务,请释放该行上的锁。这种分配资源的方式比锁定表更好,因为每个连接只需要锁定自己的行
如果您没有从中获取资源id,请选择。。。对于更新
查询,您需要立即执行以下操作
ROLLBACK;
取消您在该行开始的交易。然后你需要等待,然后再试一次。等待一段有意义的时间:至少等待应用程序使用某个资源并释放它所需的时间。如果您等待的时间较短,那么您的许多连接将在资源表上敲打,并使速度减慢。如果您需要更多资源,请添加一些
当您的软件使用分配的资源完成时,执行此操作以将其释放回池中。您可以在自动提交模式下执行此操作;这里不需要显式事务。但一定要确保你的客户为此打开了自动提交模式
UPDATE resource
SET free = 1
WHERE resource_id = 'the resource ID you have been using';
请注意;我不太明白你是如何在上使用free\u的,所以我可能把这部分逻辑搞错了
SELECT
查找空闲资源的操作很可能会通过在
我设法通过内部缓冲资源来解决这个问题。我不是一次获取一个资源,而是获取其中的5个资源,从而将该表上的调用和锁的数量减少了5倍。能否显示正在使用的表架构和SQL代码?您是锁定整个表还是使用SELECT。。。对于更新
?使用free_at为空
肯定会阻止在SELECT
语句中使用索引。我以前尝试过使用SELECT FOR UPDATE,但遇到死锁异常。这就是为什么我改为使用整行锁定的原因。您提出了一个XY问题:您想要执行X,并且您认为Y是执行此操作的最佳方式。与其问X,不如问Y。我肯定会再次尝试建议的解决方案,只是设想有一种最佳实践方法,可以使用整个表锁创建一个池。实现该方法会延长锁的等待时间。当一行被锁定(在事务中),并且多个其他连接尝试对表进行排序并获取下一行时,会发生什么情况?这不是个问题吗?试着跳过订单,只做限制1。确保尽快提交事务。除非有什么原因你必须循环使用这些资源,只要抓住第一个符合条件的资源就行了。确保表中的可搜索列声明为非NULL,并且已从WHERE
子句中删除或col为NULL
。
UPDATE resource
SET free = 1
WHERE resource_id = 'the resource ID you have been using';
(free, free_at, resource_id)