2016-06-25 74 views
1

我有一台应用程序使用托管在一台机器上的MySQL数据库和6台运行在其他机器上的客户机,这些机器通过本地网络读取和写入。MySQL更新逐渐变慢

我有一个主要的工作表,其中包含大约120,000个项目行工作。每个客户从表格中抽取40个未分配的工作项目(将它们标记为已分配),完成工作,然后将结果写回到同一工作表格。这个顺序一直持续到没有更多的工作要做。

enter image description here

上面是图片,显示为40分的结果的每个块使用UPDATE查询的客户端之一写回表所花费的时间量。你可以看到大部分时间的持续时间相当短,但是突然间持续时间高达300秒,并且一直持续到所有工作完成。执行查询的时间迅速增加是我需要帮助的。

客户端负载不重。该服务器有点加载,但它有16GB的内存,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

'工作'变得多大? 'innodb_buffer_pool_size'和'key_buffer_size'的值是什么?什么引擎正在使用? –

回答

0

没有索引,扫描完整的表格。如果项目ID变大,则必须扫描更大量的表以获得行更新。 我会尝试一个索引,甚至可能是item_id的主键?

对于这样的机器和相对小的数据库,持续时间的增加似乎仍然过高。

+0

感谢您的回答。我知道索引会使找到要更新的行更快,但如果这是问题,我不应该指望时间会线性增加吗?图表显示了一些阈值,之后更新的持续时间明显更长。 – user1592096

+0

你和Iserni的回答都是正确的。在'item'上添加索引解决了性能问题。谢谢。 – user1592096

0

鉴于需要更多细节才能进行正确的诊断(请参阅下文),我在这里看到两种潜在的性能下降可能性。

之一是,你正在运行到一个倒楣的画家的问题,你可以用

CREATE INDEX table_ndx ON table(allocated_node, item); 

改善,但它看起来不太可能有这么低的基数。MySQL不应该花那么长时间才能找到未分配的节点。

更可能的解释可能是您遇到了某种客户端之间的锁定冲突。可以肯定的是,在系统停止的那300秒期间,从管理员连接到MySQL,运行

SHOW FULL PROCESSLIST 

。看看它有什么要说的,并可能用它来更新你的问题。此外,对您使用的表格发布

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表中也停滞了很久。

+0

我目前没有使用任何明确的锁定,但我想你是说即使如此,锁定可能会隐含发生?另外我不熟悉使用交易。在事务完成之前,事务2是否锁定表进行访问?在这种情况下,其他客户会被封锁? – user1592096

+0

理论上,SELECT不应该被阻塞,InnoDB表上的UPDATE只会锁定受影响的行。在实践中,我已经看到复杂的UPDATE与SELECT和子查询lock *所有涉及表* - 这就是为什么我倾向于可能过度使用临时表和MEMORY表来简化查询。但要真正确定发生了什么,您需要检查进程列表,甚至可以检查引擎状态。 – LSerni

+0

谢谢。你有什么建议检查“引擎状态”? – user1592096

0

你的'外部锁定'技术听起来很好。

INDEX(allocated_node)将对第一个UPDATE有显着帮助。

INDEX(item)将对最终的UPDATE有很大的帮助。

(与两列一个复合索引将有助于只有一个更新的,不能同时使用。)

的原因突然增加:您不断填补大TEXT领域,使表的大小增长。在某些时候,表格太大以至于无法缓存到RAM中。所以,它从被缓存到全表扫描。

...; SELECT ... FOR UPDATE; COMMIT; - FOR UPDATE是无用的,因为COMMIT立即发生。

你可以玩“40”,但我想不出为什么一个更大或更小的数字会有所帮助。