함수를 사용하여 효율성을 위한 구축 - Amazon Aurora

함수를 사용하여 효율성을 위한 구축

사용자 정의 함수는 기본적으로 단일 샤드에 최적화되어 있지 않지만 단일 샤드 작업으로 실행되도록 구성할 수 있습니다. 함수는 로직을 캡슐화하고 단일 샤드 최적화 방식으로 실행되도록 할 수 있습니다.

단일 샤드 작업이 중요한 이유

리소스 사용률은 성능 및 비용 효율성에 중요합니다. 단일 샤드 작업은 교차 샤드 작업에 비해 훨씬 적은 리소스를 사용합니다. 예를 들어 함수를 실행하여 백만 개의 행을 삽입할 때 단일 샤드 실행은 교차 샤드 실행의 경우 126.5ACU에 비해 약 90.5ACU를 사용하므로 리소스 효율성이 35% 향상됩니다.

또한 단일 샤드 실행은 다음을 제공합니다.

  • 교차 샤드 작업보다 35% 높은 처리량

  • 보다 예측 가능한 응답 시간

  • 데이터 증가에 따른 확장성 향상

단일 샤드 작업 및 함수

함수는 다음 사전 조건 중 하나가 충족되면 샤드에서 실행됩니다.

  • 함수는 변경할 수 없는 것으로 생성되어 단일 샤드 최적화 쿼리에 포함됩니다.

  • 함수는 사용자가 배포합니다.

샤드에서 실행되는 함수는 데이터가 있는 위치에서 실행되므로 더 잘 수행되고 확장됩니다.

함수 및 변동성

함수의 변동성을 확인하려면 PostgreSQL의 시스템 테이블에서 다음 쿼리를 사용합니다.

SELECT DISTINCT nspname, proname, provolatile FROM pg_proc PRO JOIN pg_namespace NSP ON PRO.pronamespace = NSP.oid WHERE proname IN ('random', 'md5');

출력 예시:

  nspname   | proname | provolatile 
------------+---------+-------------
 pg_catalog | md5     | i
 pg_catalog | random  | v
(2 rows)

이 예제에서 md5()는 변경할 수 없으며 random()은 휘발성입니다. 즉, md5()를 포함하는 단일 샤드 최적화 문은 단일 샤드 최적화 상태로 유지되지만 random()를 포함하는 문은 그렇지 않습니다.

변경 불가능한 함수가 있는 예제:

EXPLAIN ANALYZE SELECT pg_catalog.md5('123') FROM s1.t1 WHERE col_a = 776586194 AND col_b = 654849524 AND col_c = '3ac2f2affb02987159ccd6ebd23e1ae5';
                          QUERY PLAN 
----------------------------------------------------
 Foreign Scan  (cost=100.00..101.00 rows=100 width=0) 
               (actual time=3.409..3.409 rows=1 loops=1)
 Single Shard Optimized
 Planning Time: 0.313 ms
 Execution Time: 4.253 ms
(4 rows)

휘발성 함수가 있는 예제:

EXPLAIN ANALYZE SELECT pg_catalog.random() FROM s1.t1 WHERE col_a = 776586194 AND col_b = 654849524 AND col_c = '3ac2f2affb02987159ccd6ebd23e1ae5';
                          QUERY PLAN 
------------------------------------------------------
 Foreign Scan on t1_fs00001 t1  
   (cost=100.00..15905.15 rows=1 width=8) 
   (actual time=0.658..0.658 rows=1 loops=1)
 Planning Time: 0.263 ms
 Execution Time: 2.892 ms
(3 rows)

출력은 md5()가 푸시다운되고 단일 샤드 최적화로 실행되지만 random()는 그렇지 않음을 보여줍니다.

함수 배포

하나의 샤드에서만 데이터에 액세스하는 함수는 성능 이점을 얻기 위해 해당 샤드에서 실행해야 합니다. 함수를 배포하고 함수 서명에 전체 샤드 키가 포함되어야 합니다. 샤드 키의 모든 열을 함수에 파라미터로 전달해야 합니다.

함수 예제:

CREATE OR REPLACE FUNCTION s1.func1( param_a bigint, param_b bigint, param_c char(100) ) RETURNS int AS $$ DECLARE res int; BEGIN SELECT COUNT(*) INTO res FROM s1.t1 WHERE s1.t1.col_a = param_a AND s1.t1.col_b = param_b AND s1.t1.col_c = param_c; RETURN res; END $$ LANGUAGE plpgsql;

배포 전에 함수는 단일 샤드 최적화되지 않습니다.

EXPLAIN ANALYZE SELECT * FROM s1.func1(776586194, 654849524, '3ac2f2affb02987159ccd6ebd23e1ae5');
                                              QUERY PLAN 
------------------------------------------------------------------------------------------------------
 Function Scan on func1  (cost=0.25..0.26 rows=1 width=4) 
                         (actual time=37.503..37.503 rows=1 loops=1)
 Planning Time: 0.901 ms
 Execution Time: 51.647 ms
(3 rows)

함수를 배포하려면 다음을 수행하세요.

SELECT rds_aurora.limitless_distribute_function( 's1.func1(bigint,bigint,character)', ARRAY['param_a','param_b','param_c'], 's1.t1' );

배포 후에 함수는 단일 샤드에 최적화됩니다.

EXPLAIN ANALYZE SELECT * FROM s1.func1(776586194, 654849524, '3ac2f2affb02987159ccd6ebd23e1ae5');
                                           QUERY PLAN 
------------------------------------------------------------------------------------------------
 Foreign Scan  (cost=100.00..101.00 rows=100 width=0) 
               (actual time=4.332..4.333 rows=1 loops=1)
 Single Shard Optimized
 Planning Time: 0.857 ms
 Execution Time: 5.116 ms
(4 rows)

rds_aurora.limitless_stat_statements에서 sso_calls 열을 확인하여 단일 샤드 최적화를 확인할 수 있습니다.

subcluster_id | subcluster_type | calls | sso_calls |                query 
--------------+-----------------+-------+-----------+--------------------------------------
 2            | router          |     2 |         1 | SELECT * FROM s1.func1( $1, $2, $3 )
 3            | router          |     1 |         1 | SELECT * FROM s1.func1( $1, $2, $3 )
(2 rows)

함수 및 효율성 패턴

데이터에 가까운 로직을 실행하는 것이 더 효율적이며 함수는 이를 달성하는 데 중요한 역할을 합니다. 함수의 효율성을 개선하기 위한 두 가지 주요 사용 사례가 있습니다.

  1. 복잡한 데이터에서 샤드 키를 추출하여 별도의 단일 샤드 최적화 함수 호출

  2. 교차 샤드 로직을 단일 샤드 최적화 문과 분리하여 교차 샤드 워크로드를 단일 샤드 최적화로 전환

복잡한 데이터에서 샤드 키 추출

여러 데이터베이스 작업을 수행하는 서명 s3.func3(p_json_doc json)이 있는 함수를 고려합니다. 이러한 작업은 모든 샤드에 걸쳐 있는 트랜잭션 내의 모든 샤드에서 실행됩니다. JSON 문서에 샤드 키가 포함된 경우 단일 샤드 최적화 함수를 빌드하여 데이터베이스 작업을 수행할 수 있습니다.

원래 패턴:

s3.func3(p_json_doc json) database operation 1; database operation 2; database operation 3;

최적화된 패턴:

s3.func3(p_json_doc json) DECLARE v_a bigint; BEGIN v_a := (p_json_doc->>'field_a')::bigint; SELECT s3.func3_INNER(v_a, p_json_doc); END;

내부 함수가 다음을 수행하는 경우:

s3.func3_INNER(p_a, p_json_doc) database operation 1 WHERE shard_key = p_a; database operation 2 WHERE shard_key = p_a; database operation 3 WHERE shard_key = p_a;

이 패턴에서 샤드 키는 복잡한 데이터 형식으로 캡슐화되거나 다른 파라미터에서 추론할 수 있습니다. 로직, 데이터 액세스 및 함수는 샤드 키를 결정, 추출 또는 구성한 다음 단일 샤드에 대한 작업만 수행하는 단일 샤드 최적화 함수를 호출할 수 있습니다. 애플리케이션 인터페이스는 변경되지 않으므로 최적화는 비교적 쉽게 테스트할 수 있습니다.

다른 함수 또는 데이터에서 샤드 키 연기

로직 또는 데이터 액세스가 샤드 키를 계산하거나 결정할 때 또 다른 설계 패턴이 적용됩니다. 이는 대부분의 호출에 대해 단일 샤드에서 함수를 실행할 수 있지만 경우에 따라 교차 샤드 실행이 필요한 경우에 유용합니다.

원래 패턴:

NEWORD(INTEGER, …) RETURNS NUMERIC DECLARE all_whid_local := true; LOOP through the order lines Generate warehouse ID; IF generated warehouse ID == input warehouse ID THEN ol_supply_whid := input warehouse ID; ELSE all_whid_local := false; ol_supply_whid := generated warehouse ID; END IF; … END LOOP; … RETURN no_s_quantity;

별도의 함수가 있는 최적화된 패턴:

CREATE OR REPLACE FUNCTION NEWORD_sso(no_w_id INTEGER, …) RETURNS NUMERIC … RETURN no_s_quantity; … END; LANGUAGE 'plpgsql'; SELECT rds_aurora.limitless_distribute_function( 'NEWORD_sso(int,…)', ARRAY['no_w_id'], 'warehouse' ); CREATE OR REPLACE FUNCTION NEWORD_crosshard(no_w_id INTEGER, …) RETURNS NUMERIC … RETURN no_s_quantity; … END; LANGUAGE 'plpgsql';

그런 다음 기본 함수가 단일 샤드 최적화 버전 또는 교차 샤드 버전을 호출하도록 합니다.

IF all_whid_local THEN SELECT NEWORD_sso(…) INTO no_s_quantity; ELSE SELECT NEWORD_crosshard(…) INTO no_s_quantity; END IF;

이 접근 방식을 사용하면 대부분의 간접 호출이 단일 샤드 최적화의 이점을 활용하면서 교차 샤드 실행이 필요한 경우 올바른 동작을 유지할 수 있습니다.

단일 샤드 작업 확인

EXPLAIN을 사용하여 문이 단일 샤드에 최적화되어 있는지 확인합니다. 출력은 최적화된 작업을 위해 '단일 샤드 최적화'를 명시적으로 보고합니다.

배포 전 샤드 간 호출:

                       QUERY PLAN 
---------------------------------------------------------------------
 Function Scan on func1  (cost=0.25..0.26 rows=1 width=4) 
                         (actual time=59.622..59.623 rows=1 loops=1)
 Planning Time: 0.925 ms
 Execution Time: 60.211 ms

배포 후 단일 샤드 호출:

                       QUERY PLAN 
----------------------------------------------------------------------
 Foreign Scan  (cost=100.00..101.00 rows=100 width=0) 
               (actual time=4.576..4.577 rows=1 loops=1)
 Single Shard Optimized
 Planning Time: 1.483 ms
 Execution Time: 5.404 ms

실행 시간의 차이는 단일 샤드 최적화의 성능 이점을 보여줍니다.