我可以在多个查询中拆分查询或创建并行性来加速查询吗?

我有一个表avl_pool ,我有一个函数可以在地图上找到最接近该(x, y)位置的链接。

该选择的性能非常线性,该function需要~8 ms才能执行。 因此,计算此行选择1000行需要8秒。 或者,正如我在此示例中所示,20.000行需要162秒。

 SELECT avl_id, x, y, azimuth, map.get_near_link(X, Y, AZIMUTH) FROM avl_db.avl_pool WHERE avl_id between 1 AND 20000 "Index Scan using avl_pool_pkey on avl_pool (cost=0.43..11524.76 rows=19143 width=28) (actual time=8.793..162805.384 rows=20000 loops=1)" " Index Cond: ((avl_id >= 1) AND (avl_id <= 20000))" " Buffers: shared hit=19879838" "Planning time: 0.328 ms" "Execution time: 162812.113 ms" 

使用pgAdmin我发现如果同时在分离的窗口上执行一半的范围,执行时间实际上被分成两半。 所以看起来服务器可以毫无问题地处理对同一个表/函数的多个请求。

 -- windows 1 SELECT avl_id, x, y, azimuth, map.get_near_link(X, Y, AZIMUTH) FROM avl_db.avl_pool WHERE avl_id between 1 AND 10000 Total query runtime: 83792 ms. -- windows 2 SELECT avl_id, x, y, azimuth, map.get_near_link(X, Y, AZIMUTH) FROM avl_db.avl_pool WHERE avl_id between 10001 AND 20000 Total query runtime: 84047 ms. 

那么我该如何应对这种情况以提高性能呢?

C# aproach我想我可以创建多个线程,每个线程发送一部分范围,然后我加入客户端中的所有数据。 所以相反,一个查询有20k和162秒,我可以发送10个查询,2000行,并在~16秒内完成。 当然,联接中可能存在间接成本,但与160秒相比不应该很大。

或者我应该考虑不同的方法,如果只是一个简单的SQL解决方案,那就更好了吗?


@PeterRing我不认为function代码是相关的,但无论如何这里是。

 CREATE OR REPLACE FUNCTION map.get_near_link( x NUMERIC, y NUMERIC, azim NUMERIC) RETURNS map.get_near_link AS $BODY$ DECLARE strPoint TEXT; sRow map.get_near_link; BEGIN strPoint = 'POINT('|| X || ' ' || Y || ')'; RAISE DEBUG 'GetLink strPoint % -- Azim %', strPoint, Azim; WITH index_query AS ( SELECT --Seg_ID, Link_ID, azimuth, TRUNC(ST_Distance(ST_GeomFromText(strPoint,4326), geom )*100000)::INTEGER AS distance, sentido, --ST_AsText(geom), geom FROM map.vzla_seg S WHERE ABS(Azim - S.azimuth)  330 ORDER BY geom  ST_GeomFromText(strPoint, 4326) LIMIT 101 ) SELECT i.Link_ID, i.Distance, i.Sentido, v.geom INTO sRow FROM index_query i INNER JOIN map.vzla_rto v ON i.link_id = v.link_id ORDER BY distance LIMIT 1; RAISE DEBUG 'GetLink distance % ', sRow.distance; IF sRow.distance > 50 THEN sRow.link_id = -1; END IF; RETURN sRow; END; $BODY$ LANGUAGE plpgsql IMMUTABLE COST 100; ALTER FUNCTION map.get_near_link(NUMERIC, NUMERIC, NUMERIC) OWNER TO postgres; 

考虑将map.get_near_link函数标记为PARALLEL SAFE 。 这将告诉数据库引擎在执行函数时允许尝试生成并行计划:

PARALLEL UNSAFE表示该函数无法在并行模式下执行,并且SQL语句中存在此类函数会强制执行串行执行计划。 这是默认值。 PARALLEL RESTRICTED表示该function可以并行模式执行,但执行仅限于并行组负责人。 PARALLEL SAFE表示该function可以安全地以并行模式运行而不受限制。

有几种设置可能导致查询计划程序在任何情况下都不生成并行查询计划。 请考虑以下文档:

  • 15.4。 并行安全

  • 15.2。 什么时候可以使用并行查询?

在我的阅读中,如果您像这样重构您的函数,您可能能够实现并行计划:

 CREATE OR REPLACE FUNCTION map.get_near_link( x NUMERIC, y NUMERIC, azim NUMERIC) RETURNS TABLE (Link_ID INTEGER, Distance INTEGER, Sendito TEXT, Geom GEOGRAPHY) AS $$ SELECT S.Link_ID, TRUNC(ST_Distance(ST_GeomFromText('POINT('|| X || ' ' || Y || ')',4326), S.geom) * 100000)::INTEGER AS distance, S.sentido, v.geom FROM ( SELECT * FROM map.vzla_seg WHERE ABS(Azim - S.azimuth) NOT BETWEEN 30 AND 330 ) S INNER JOIN map.vzla_rto v ON S.link_id = v.link_id WHERE ST_Distance(ST_GeomFromText('POINT('|| X || ' ' || Y || ')',4326), S.geom) * 100000 < 50 ORDER BY S.geom <-> ST_GeomFromText('POINT('|| X || ' ' || Y || ')', 4326) LIMIT 1 $$ LANGUAGE SQL PARALLEL SAFE -- Include this parameter ; 

如果查询优化器在执行此函数时将生成并行计划,则无需实现自己的并行化逻辑。

我做过这样的事情。 它的效果相对较好。 请注意,每个连接一次只能处理一个查询,因此对于查询的每个分区,您必须具有单独的连接。 现在,在C#中,您可以使用线程与每个连接进行交互。

但另一种选择是使用异步查询并让单个线程管理和轮询整个连接池(这有时简化了应用程序端的数据操作)。 请注意,在这种情况下,您最好在每个轮询周期后确保睡眠或其他屈服点。

请进一步注意,查询加速的程度取决于磁盘I / O子系统和CPU并行度。 因此,您不能只是抛出更多的查询,并期望加快速度。

我用SSIS创建了一个脚本,将每个服务器分成7个不同的“@Mode”(在我的情况下,许多服务器根据其IP的最后三位数分配@Mode – 这会创建相当均匀分布的存储桶。

  (CONVERT(int, RIGHT(dbserver, 3)) % @stages) + 1 AS Mode 

在SSIS中,我有7组相同的14个大型查询正在运行。 每个都分配了一个传递给存储过程的不同@Mode编号。

从本质上讲,这允许7个同时进行的查询从不在同一台服务器上运行,并有效地将运行时间减少了大约85%。

因此,创建一个SSIS包,第一步是刷新@Mode表。

然后创建一个包含7个容器的容器。 在这7个容器中的每个容器中,使用参数映射执行SQL查询到@Mode。 我将所有内容都指向存储过程,所以在我的例子中,SQLStatement字段读取的内容如下: EXEC StoredProc ? 。 的? 然后将检查您为@Mode创建的参数映射。

最后,在SQL查询中,确保将@Mode指示为运行查询的服务器的变量。