假设我有一个主键列表,每行需要更新一个值。运行是否更好:
-- run 10,000 of these queries
UPDATE mytable SET myflag = 1 WHERE id = [each_id]
或者将更新组合到批量查询中,如下所示:
-- run 100 of these queries, where the IN () list contains about 100 elements
UPDATE mytable SET myflag = 1 WHERE id IN (3, 4, 5, 9, 99, ... 7887 )
100个IN()项目的100个查询怎么样?
答案 0 :(得分:5)
都不是。在 PostgreSQL 中,我会改为:
WITH x AS (
SELECT unnest('{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20
,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40
,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60
,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80
,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100
}'::int[]) AS id
)
UPDATE mytable t
SET myflag = 1
FROM x
WHERE t.id = x.id;
我在我的示例中放了这么多ID,以提供10000个ID很多的视觉线索。问题中提出的两个想法是:
必须解析列表并将10000个语句放在一起并发送到服务器,这可能需要比UPDATE本身更长的时间。
必须为id
中的每个mytable
搜索10000个项目的列表(数组)中的匹配ID。不能使用标准索引。这将非常慢。性能会随着mytable
的大小而降低。
mytable.id
上的索引是所有呈现的替代需求,其表现优于两个变体。
CTE解析数组一次(子查询也可以工作 - MySQL没有CTE) - unnest()
相当快。在一个语句中完成所有操作可以使10000个语句超过一个数量级。如果这些语句在单个事务中运行,则添加另一个数量级。如果您应该使用单独的会话,请添加另一个。
罕见的例外适用于在 write 负载较重的情况下具有锁定问题的数据库。正如已建议的那样只是基准。 EXPLAIN ANALYZE
是你在PostgreSQL中的朋友。
如果操作增长 huge ,并且大部分表已更新和/或磁盘空间或内存不足,则可能仍然是个好主意将操作分成几个逻辑块 - 只是不是太多,找到最佳点。主要是让HOT updates回收之前UPDATE
次运行的表膨胀。考虑this related question。
答案 1 :(得分:4)
我发现第二种方法在为非常大的数据集进行插入时要快几个数量级。这非常依赖于您的系统,因为查询的IN部分或多或少会有效,具体取决于表大小,索引等。
做自己简单的基准测试是唯一可行的方法。
答案 2 :(得分:3)
在正常情况下,运行一个更新语句是最有效的。例如,
UPDATE mytable set myflag=1 where id IN (select id from someothertable where stuff).
根据您的架构,可能可能会更慢。你应该基准测试并找出答案。
注意,几乎确定要慢一点,就是从客户端向数据库服务器运行10,000个语句。在存储过程中运行10,000个更新并从客户端运行10,000个更新是两个非常不同的事情。如果您要运行10,000次更新路线,请确保在SP中执行此操作。
答案 3 :(得分:2)
通常,RDBMS往返是一个主要因素,但在这种情况下,解析in
列表的成本也可能会很高。但是,如果您对查询进行参数化,则第二种解决方案可能会更快,因为解析只会进行一次。
答案 4 :(得分:0)
这主要取决于硬盘的fsyncs数量:这是系统中最慢的部分。
对于PostgreSQL:如果可能的话只在一个事务中执行少量事务。但要注意行锁定,两个事务不能同时更新同一行。