2017-07-31 137 views
-1

我们需要每秒高于100个并发表的请求数。mySQL表更新并发性能

它是一个包含一堆唯一键码的表格,它们在被请求时被分配一个request_guid。

该表包含整数主键,唯一键码和空request_guid。

我们正在运行下面的查询,它的工作原理与OK 10,000条记录,但随着100万个+把它记录嘎然而止,并在每次更新需要16+秒。

表:

(id INT 
keycode VARCHAR(50) 
request_guid VARCHAR(45) NULL) 

* Concurrant查询:

UPDATE coupon 
SET request_guid = ? 
WHERE request_guid IS NULL 
ORDER BY RAND() LIMIT 1;* 

的RAND()的订单正在使用,否则我们得到的锁定问题,当在同一行正试图同时更新。

任何人都可以想到一个更好的方法来做到这一点,以提高性能?钥匙可能应该放在单独的桌子上?

我希望以上是有道理的,谢谢你的帮助!

干杯,

˚F

+0

您不需要在更新表格时执行排序,基本上,您正试图将所有NULL request_guid更新为特定值。你可以删除'ORDER BY RAND()LIMIT 1'。 –

+0

这表明你没有索引。在查询上运行EXPLAIN PLAN并查找TABLE SCAN。如果你看到一个,你就知道你错过了一个索引。这将不会扫描您的表添加行。为什么在插入行时不添加请求guid?我不明白更新的要求。 – duffymo

+0

如果您的代码正常工作,并且您正在寻找更高效的代码,那么我会考虑更多的代码审查问题。 –

回答

1

你就错了!

首先,ORDER BY RAND()必须将整个表进行排序,所以这将是缓慢的窘况。

答案是欺骗。我将假设在设置这个特定的表时,事先知道唯一键码的列表,并且一旦它被填充,就想随机选择键码。所以让我们从一个表“all_keycodes”开始。

Table all_keycodes 
keycode VARCHAR(50) 

现在,让我们创建另一个表:

CREATE TABLE keycodes (
id INT PRIMARY KEY AUTO_INCREMENT, 
keycode VARCHAR(50) NOT NULL, 
UNIQUE(keycode) 
) 

...我们以随机顺序代码填满它,我们能做的只有这一次,设置系统时。

INSERT INTO keycodes (keycode) 
SELECT keycode FROM all_keycodes 
ORDER BY rand() 

现在这个表包含一个int主键,我们将用它来拉出键码在它们的ID,已随机的顺序。

现在我们所要做的就是按照适合并发环境的快速方式按顺序提供这些键码。不幸的是,MySQL没有序列,但它确实有表格!

CREATE TABLE used_keycodes( 
id INT PRIMARY KEY AUTO_INCREMENT, 
request_guid VARCHAR(45) NOT NULL, 
UNIQUE(request_guid) 
) 

现在,你可能会问,wtf?

简单。当你想要求一个键码,你这样做:

INSERT INTO used_keycodes (request_guid) VALUES (your guid) 

并返回INSERT_ID给你另一个表的键码。

这是并发的,安全的,它会很好地缩放。最重要的是,无论使用多少个键码,找到一个尚未使用的键码总是需要相同的时间,它只是一个INSERT。

您也可以将used_keycodes.id设置为REFERENCE keycodes.id。

+0

感谢您花时间添加这样一个深入的解决方案。会给出这个:)一个简单的问题,如果我们在keycodes表中有多个密钥批次的将来引入keybatch_id,我觉得这个解决方案将不再起作用?我相信我们现在可以忍受这一点,再次感谢:) –

+0

是的,这有点破解,因为我使用auto_increment作为原子共享计数器......但它是MySQL唯一的原子共享计数器。我相信MySQL可以像auto_increment一样拆分PK(key_batch_id,counter),你必须检查它。或者只是创建更多的表...使其更容易完成时删除它们,而不是慢DELETE。 – peufeu

+0

这两个表中的重大改进:摆脱'id'并将'UNIQUE'键推广为'PRIMARY KEY'。 –

0

一个简单的方法是删除ORDER BY并通过将隔离级别设置为READ UNCOMMITTED(从而允许脏读)来避免锁定。

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED ; 
UPDATE coupon SET request_guid = ? WHERE request_guid IS NULL LIMIT 1; 
COMMIT ; 

这应该确保他们被允许阅读WHERE子句中的非空,但未提交值更新不会在同一行竞争。

请注意,这不是一个非常时尚的解决方案,它只适用于您的数据库支持READ UNCOMMITTED隔离级别(MySQL/InnoDB所做的)。

+0

谢谢@kayaman。这听起来像一个简单的答案。但是,这是否意味着如果更新同时发生,您最终可能会覆盖对方的值? :) –

+0

请参阅我的答案的第3段。 – Kayaman

+0

谢谢,会给这个去吧! –