Hen*_*son 19 python sql postgresql psycopg2
我们有一个相当特殊的应用程序,它使用PostgreSQL 8.3作为存储后端(使用Python和psycopg2).我们对重要表执行的操作在大多数情况下都是插入或更新(很少删除或选择).
出于理智的原因,我们已经创建了我们自己的数据映射器层,它运行得相当好,但它有一个很大的瓶颈,即更新性能.当然,我不希望更新/替换场景像'插入空表'那样快速,但是接近它会很好.
请注意,此系统没有并发更新
我们总是在更新中设置每行的所有字段,这可以在我在测试中使用"替换"一词的术语中看到.到目前为止,我已经尝试了两种解决更新问题的方法:
创建一个replace()需要更新行数组的过程:
CREATE OR REPLACE FUNCTION replace_item(data item[]) RETURNS VOID AS $$
BEGIN
FOR i IN COALESCE(array_lower(data,1),0) .. COALESCE(array_upper(data,1),-1) LOOP
UPDATE item SET a0=data[i].a0,a1=data[i].a1,a2=data[i].a2 WHERE key=data[i].key;
END LOOP;
END;
$$ LANGUAGE plpgsql
Run Code Online (Sandbox Code Playgroud)创建一个insert_or_replace规则,以便除偶尔删除之外的所有内容都成为多行插入
CREATE RULE "insert_or_replace" AS
ON INSERT TO "item"
WHERE EXISTS(SELECT 1 FROM item WHERE key=NEW.key)
DO INSTEAD
(UPDATE item SET a0=NEW.a0,a1=NEW.a1,a2=NEW.a2 WHERE key=NEW.key);
Run Code Online (Sandbox Code Playgroud)这两者都加快了更新速度,虽然后者减慢了插入量:
Multi-row insert : 50000 items inserted in 1.32 seconds averaging 37807.84 items/s
executemany() update : 50000 items updated in 26.67 seconds averaging 1874.57 items/s
update_andres : 50000 items updated in 3.84 seconds averaging 13028.51 items/s
update_merlin83 (i/d/i) : 50000 items updated in 1.29 seconds averaging 38780.46 items/s
update_merlin83 (i/u) : 50000 items updated in 1.24 seconds averaging 40313.28 items/s
replace_item() procedure : 50000 items replaced in 3.10 seconds averaging 16151.42 items/s
Multi-row insert_or_replace: 50000 items inserted in 2.73 seconds averaging 18296.30 items/s
Multi-row insert_or_replace: 50000 items replaced in 2.02 seconds averaging 24729.94 items/s
Run Code Online (Sandbox Code Playgroud)
关于测试运行的随机说明:
该表如下所示:
CREATE TABLE item (
key MACADDR PRIMARY KEY,
a0 VARCHAR,
a1 VARCHAR,
a2 VARCHAR
)
Run Code Online (Sandbox Code Playgroud)所以,真正的问题是:如何加快更新/替换操作的速度?(我认为这些调查结果可能"足够好",但我不想在不点击SO人群的情况下放弃:)
任何人都暗示一个更优雅的replace_item(),或者证明我的测试完全被破坏是非常受欢迎的.
如果您想尝试重现,可以在此处获得测试脚本.记得先检查一下......它是WorksForMe,但......
您需要编辑db.connect()行以适合您的设置.
编辑
感谢#postgresql @ freenode中的andres我有另一个单查询更新的测试; 很像一个多行插入(在上面列为update_andres).
UPDATE item
SET a0=i.a0, a1=i.a1, a2=i.a2
FROM (VALUES ('00:00:00:00:00:01', 'v0', 'v1', 'v2'),
('00:00:00:00:00:02', 'v3', 'v4', 'v5'),
...
) AS i(key, a0, a1, a2)
WHERE item.key=i.key::macaddr
Run Code Online (Sandbox Code Playgroud)
编辑
感谢#postgresql @ freenode和jug/jwp中的merlin83,我有另一个带有insert-to-temp/delete/insert方法的测试(上面列为"update_merlin83(i/d/i)").
INSERT INTO temp_item (key, a0, a1, a2)
VALUES (
('00:00:00:00:00:01', 'v0', 'v1', 'v2'),
('00:00:00:00:00:02', 'v3', 'v4', 'v5'),
...);
DELETE FROM item
USING temp_item
WHERE item.key=temp_item.key;
INSERT INTO item (key, a0, a1, a2)
SELECT key, a0, a1, a2
FROM temp_item;
Run Code Online (Sandbox Code Playgroud)
我的直觉是,这些测试对现实场景中的表现并不具有代表性,但我认为差异非常大,足以说明进一步调查的最有希望的方法.perftest.py脚本包含所有更新以及那些想要检查它的人.它虽然相当丑陋,所以不要忘记你的护目镜:)
编辑
#postgresql @ freenode中的andres指出我应该使用insert-to-temp/update变体进行测试(上面列为"update_merlin83(i/u)").
INSERT INTO temp_item (key, a0, a1, a2)
VALUES (
('00:00:00:00:00:01', 'v0', 'v1', 'v2'),
('00:00:00:00:00:02', 'v3', 'v4', 'v5'),
...);
UPDATE item
SET a0=temp_item.a0, a1=temp_item.a1, a2=temp_item.a2
FROM temp_item
WHERE item.key=temp_item.key
Run Code Online (Sandbox Code Playgroud)
编辑
可能是最终编辑:我更改了我的脚本以更好地匹配我们的加载方案,并且即使在稍微扩展一些并添加一些随机性时,似乎数字也保持不变.如果有人从其他场景得到非常不同的数字,我会有兴趣了解它.
在你的insert_or_replace。尝试这个:
WHERE EXISTS(SELECT 1 FROM item WHERE key=NEW.key LIMIT 1)
Run Code Online (Sandbox Code Playgroud)
代替
WHERE EXISTS(SELECT 1 FROM item WHERE key=NEW.key)
Run Code Online (Sandbox Code Playgroud)
正如评论中所指出的,这可能不会有任何作用。那么,我要补充的是,您始终可以通过删除索引来加快 INSERT/UPDATE 性能。这可能不是您想要做的事情,除非您发现您的表索引过度,但至少应该检查一下。
| 归档时间: |
|
| 查看次数: |
5087 次 |
| 最近记录: |