将许多类似的记录有效地插入MySQL

时间:2015-11-06 22:10:42

标签: mysql

我试图在一个“交易”中将许多(从几十到几百个)类似的记录插入一个表中。记录仅因主键和另外一个字段而异。该表是一个MEMORY表,插入的记录经常更新并经常删除,但有些可以在表中保留一天。表结构:

id     BIGINT
sid    CHAR
pid    INT
mask   INT
param1 INT
param2 INT
...    INT
paramN INT

PRIMARY (id, sid),
KEY [BTREE] (sid), (param1), (param2), (...), (paramN)

现在插入是通过准备好的声明发生的:

INSERT INTO object_subscriptions (id, sid, pid, mask, ...)
    VALUES (:id, :sid, :pid, :mask, ...)
    ON DUPLICATE KEY UPDATE mask = mask | VALUES(mask)

样本记录:

13194140000467 | 'fBF8OfQlAjSS8uXsAAzx' | 7 | 22 |    3 | 0 | 188 | 5123 | 1
18392199238192 | 'fBF8OfQlAjSS8uXsAAzx' | 7 | 22 |    3 | 0 | 188 | 5123 | 1
26342478965721 | 'fBF8OfQlAjSS8uXsAAzx' | 7 | 22 |    3 | 0 | 188 | 5123 | 1
64322445645318 | 'fBF8OfQlAjSS8uXsAAzx' | 7 | 22 |    3 | 0 | 188 | 5123 | 1
13194140000467 | 'n2pFykYGNnsp-JfCAeJO' | 5 | 97 | 1293 | 0 | 188 | 5123 | 0
18392199238192 | 'n2pFykYGNnsp-JfCAeJO' | 5 | 97 | 1293 | 0 | 188 | 5123 | 0
26342478965721 | 'n2pFykYGNnsp-JfCAeJO' | 5 | 97 | 1293 | 0 | 188 | 5123 | 0
64322445645318 | 'n2pFykYGNnsp-JfCAeJO' | 5 | 97 | 1293 | 0 | 188 | 5123 | 0

这在每批数十个记录中效果很好。每批数百个,它急剧减速。一个明显的优化,可以使性能提高一倍或三倍,就是在一个语句中有多个VALUE集,但这仍然会带来发送N params以及sidpid的开销。每条记录,即使只有一个批次中只有idmask不同。

我在考虑在准备好的语句中包含这些“固定”值,但这些“参数”是从用户输入生成的,不受信任。由于数据更改的频率和需要执行的索引查找次数,我对MySQL和MEMORY感到困惑。否则我会规范化数据或将所有内容移动到键值存储。

2 个答案:

答案 0 :(得分:1)

这是我能想到避免重复共同元素的唯一方法。不幸的是,它可能是如此冗长,以至于它并不比你要替换的更好。

INSERT INTO object_subscriptions (id, sid, pid, mask, ...)
SELECT t1.id, t2.sid, t2.pid, t1.mask, ...
FROM (SELECT :id1 id, :mask1 mask
      UNION
      SELECT :id2, :mask2
      UNION
      SELECT :id3, :mask3
      ...
    ) t1
JOIN (SELECT :sid sid, :pid pid, ...) t2

这只是一批。要在单个查询中执行多个批处理,您可以将UNION添加到顶级SELECT

INSERT INTO object_subscriptions (id, sid, pid, mask, ...)
SELECT t1.id, t2.sid, t2.pid, t1.mask, ...
FROM (SELECT :batch1_id1 id, :batch1_mask1 mask
      UNION
      SELECT :batch1_id2, :batch1_mask2
      UNION
      SELECT :batch1_id3, :batch1_mask3
      ...
    ) t1
JOIN (SELECT :batch1_sid sid, :batch1_pid pid, ...) t2
UNION
SELECT t1.id, t2.sid, t2.pid, t1.mask, ...
FROM (SELECT :batch2_id1 id, :batch2_mask1 mask
      UNION
      SELECT :batch2_id2, :batch2_mask2
      UNION
      SELECT :batch2_id3, :batch2_mask3
      ...
    ) t1
JOIN (SELECT :batch2_sid sid, :batch2_pid pid, ...) t2
UNION
...

答案 1 :(得分:0)

使用多行插入:

INSERT INTO object_subscriptions (id, sid, pid, mask, ...)
    VALUES (:id[0], :sid[0], :pid[0], :mask[0], ...),
  (:id[1], :sid[1], :pid[`], :mask[1], ...), ...
    ON DUPLICATE KEY UPDATE mask = mask | VALUES(mask)

这将减少客户端 - 服务器的来回流量,并允许MySQL使用一些内部批量加载优化。