我有一个应用程序使用托管在一台计算机上的MySQL数据库和在其他计算机上运行的6个客户端,这些计算机通过本地网络读取和写入。
我有一个主要的工作表,其中包含大约120,000个要处理的行。每个客户端从表中抓取40个未分配的工作项(将它们标记为已分配),完成工作,然后将结果写回到同一工作表中。这个序列一直持续到没有更多的工作要做。
以上是一张图片,显示了使用UPDATE查询从其中一个客户端向表中写回40个结果的每个块所花费的时间。您可以看到持续时间在大多数时间内相当小,但突然持续时间达到300秒并保持在那里直到所有工作完成。这种快速增加的时间来执行查询是我需要帮助的。
客户端负载不重。服务器有点装,但它有16GB的RAM,8个核心,除了托管这个数据库之外什么也没做。
这是相关的SQL代码。
创建表:
CREATE TABLE work (
item_id MEDIUMINT,
item VARCHAR(255) CHARACTER SET utf8,
allocated_node VARCHAR(50),
allocated_time DATETIME,
result TEXT);
/* Then insert 120,000 items, which is quite fast. No problem at this point. */
INSERT INTO work VALUES (%s,%s,%s,NULL,NULL,NULL);
客户分配40个项目:
UPDATE work SET allocated_node = %s, allocated_time=NOW()
WHERE allocated_node IS NULL LIMIT 40;
SELECT item FROM work WHERE allocated_node = %s AND result IS NULL;
使用完成的结果更新行(这是在运行几个小时后变得非常慢的部分):
/* The chart above shows the time to execute 40 of these for each write back of results */
UPDATE work SET result = %s WHERE item = %s;
我在Ubuntu 14.04上使用MySQL,具有所有标准设置。 决赛桌大约160MB,没有索引。
我没有看到我的查询有任何问题,除了整个事情需要两倍的时间之外,它们的工作正常。
具有这些问题经验的人是否可以建议我在MySQL中更改任何配置设置以修复此性能问题,或者请指出我正在做的任何可能解释图表中时间的问题。
感谢。
答案 0 :(得分:0)
如果没有索引,则会扫描整个表格。如果项ID越大,则必须扫描更大量的表以使行更新。 我会尝试一个索引,甚至可能是item_id的主键吗?
对于这样的机器和相对较小的数据库来说,持续时间的增加似乎仍然太高。
答案 1 :(得分:0)
鉴于正确诊断需要更多细节(见下文),我认为这有两种潜在的性能降低可能性。
一个是你遇到了Schlemiel the Painter的问题,你可以用它来改善
CREATE INDEX table_ndx ON table(allocated_node, item);
但基数如此之低似乎不太可能。 MySQL不需要这么长时间才能找到未分配的节点。
更可能的解释是,您在客户端之间遇到某种类型的锁定冲突。可以肯定的是,在系统停止的300秒内,运行
SHOW FULL PROCESSLIST
从管理员连接到MySQL。看看它有什么用,并可能用它来更新你的问题。另外,发布
的结果SHOW CREATE TABLE
针对您正在使用的表格。
你应该这样做:
START TRANSACTION;
allocate up to 40 nodes using SELECT...FOR UPDATE;
COMMIT WORK;
-- The two transactions serve to ensure that the node selection can
-- never lock more than those 40 nodes. I'm not too sure of that LIMIT
-- being used in the UPDATE.
START TRANSACTION;
select those 40 nodes with SELECT...FOR UPDATE;
<long work involving those 40 nodes and nothing else>
COMMIT WORK;
如果您使用单个事务和表级锁定(甚至是隐式),则可能会发生一个客户端将所有其他客户端锁定。从理论上讲,这应该只发生在MyISAM表中(只有表级锁定),但我也看到线程在InnoDB表中已经停滞了很长时间。
答案 2 :(得分:0)
你的“外部锁定”技术听起来不错。
consumer.instance.timeout.ms
会对第一个INDEX(allocated_node)
有显着帮助。
UPDATE
将有助于最终INDEX(item)
。
(具有两列的复合索引将仅帮助其中一个更新,而不是两者。)
突然增加的原因:你不断填补大UPDATE
字段,使表格大小增加。在某些时候,表是如此之大,以至于它无法缓存在RAM中。因此,它从缓存变为全表扫描。
TEXT
- 由于...; SELECT ... FOR UPDATE; COMMIT;
立即发生,FOR UPDATE
无效。
你可以玩“40”,但我想不出为什么一个更大或更小的数字会有所帮助。