이전 포스트에서 Row Generation 방법을 소개해 드렸습니다.
이번 포스트에서는 각 DB 마다 Row Generation 성능을 간단하게 비교하도록 하겠습니다.
비교 테스트에 사용한 장비는 8vCPU, 16GB 의 사양으로 Hands-On 테스트에 사용했던 장비를 그대로 사용하였고 여기에 Oracle XE 21c 를 추가로 설치했습니다.
Oracle XE 21c는 SGA+PGA 합쳐서 2GB 를 넘지 못하도록 제약이 걸려 있습니다. 따라서 불가피하게 PGA가 3000MB 로 설정되어 있는 Autonomous Database 에서도 같이 테스트를 진행했습니다.
■ 8cpu, pga_aggregate_target=512m
1억건은 메모리 부족으로 에러가 발생합니다.
에러가 안나는 최대 row 갯수는 220만건 정도로 0.5 초 정도 소요됩니다.
Recursive With 구문으로 천만건 생성하는데 2분 7초 정도 소요되었습니다.
SQL> select count(*) from (select rownum from dual connect by level <= 100000000);
select count(*) from (select rownum from dual connect by level <= 100000000)
*
ERROR at line 1:
ORA-30009: Not enough memory for CONNECT BY operation
SQL> select count(*) from (select rownum from dual connect by level <= 2200000);
COUNT(*)
----------
2200000
Elapsed: 00:00:00.54
SQL> with t(n) as (
2 select 1 as n from dual
3 union all
4 select n+1 as n from t where n < 10000000
5 )
6 select count(*) from t;
COUNT(*)
----------
10000000
Elapsed: 00:02:07.19
■ pga_aggregate_target=3000m
Oracle Autonomous Database 에서는 재귀 쿼리로 천만건 생성하는데 3.21 초 정도 소요되었습니다.
Recursive With 구문으로는 천만건 생성에 1분 40초 정도 소요되었습니다.
SQL> show parameter pga_aggregate_target;
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
pga_aggregate_target big integer 3000M
SQL> select count(*) from (select rownum from dual connect by level <= 10000000);
COUNT(*)
----------
10000000
Elapsed: 00:00:03.21
SQL> with t(n) as (
2 select 1 as n from dual
3 union all
4 select n+1 as n from t where n < 10000000
5 )
6 select count(*) from t;
COUNT(*)
----------
10000000
Elapsed: 00:01:40.69
MySQL 에서는 cte max recursive depth 를 기본 1000 에서 1억으로 우선 늘립니다.
Recursive CTE 를 이용해서 천만건 row 를 생성하는데 1.73초 수행되었습니다.
Recursive CTE 를 이용해서 1억건 row 를 생성하는데 1분 3초가 소요되었습니다.
mysql> set @@cte_max_recursion_depth=100000000;
Query OK, 0 rows affected (0.00 sec)
mysql> with recursive t(n) as (
-> select 1 as n
-> union all
-> select n+1 as n from t where n < 10000000
-> )
-> select count(*) from t;
+----------+
| count(*) |
+----------+
| 10000000 |
+----------+
1 row in set (1.73 sec)
mysql> with recursive t(n) as (
-> select 1 as n
-> union all
-> select n+1 as n from t where n < 100000000
-> )
-> select count(*) from t;
+-----------+
| count(*) |
+-----------+
| 100000000 |
+-----------+
1 row in set (1 min 3.85 sec)
PostgreSQL 은 generate_series 를 이용해 천만건 생성하는데 1.5초가 소요되었습니다. 동일한 방법으로 1억건 생성할 때는 15.4초가 걸렸습니다.
Recursive CTE 를 이용해서 천만건 row 를 생성하는데는 4.6초, 1억건 생성할 경우는 47초가 소요되었습니다.
postgres=# select count(*) from generate_series(1, 10000000);
count
----------
10000000
(1 row)
Time: 1583.327 ms (00:01.583)
postgres=# select count(*) from generate_series(1, 100000000);
count
-----------
100000000
(1 row)
Time: 15474.199 ms (00:15.474)
postgres=# with recursive t(n) as (
postgres(# select 1 as n
postgres(# union all
postgres(# select n+1 as n from t where n < 10000000
postgres(# )
postgres-# select count(*) from t;
count
----------
10000000
(1 row)
Time: 4682.084 ms (00:04.682)
postgres=# with recursive t(n) as (
postgres(# select 1 as n
postgres(# union all
postgres(# select n+1 as n from t where n < 100000000
postgres(# )
postgres-# select count(*) from t;
count
-----------
100000000
(1 row)
Time: 47140.642 ms (00:47.141)
이제 SingleStore 차례입니다. SingleStore 는 바로 1억건부터 시작해서 1억건 단위로 올려보겠습니다.
1억건에 0.86초 ~ 6억건에 6.4초정도 소요되었습니다.
7억건 생성시 메모리 부족 에러가 발생했습니다.
singlestore> select count(*) from gen_rows(100000000);
+-----------+
| count(*) |
+-----------+
| 100000000 |
+-----------+
1 row in set (0.86 sec)
singlestore> select count(*) from gen_rows(200000000);
+-----------+
| count(*) |
+-----------+
| 200000000 |
+-----------+
1 row in set (1.85 sec)
singlestore> select count(*) from gen_rows(300000000);
+-----------+
| count(*) |
+-----------+
| 300000000 |
+-----------+
1 row in set (2.60 sec)
singlestore> select count(*) from gen_rows(400000000);
+-----------+
| count(*) |
+-----------+
| 400000000 |
+-----------+
1 row in set (4.03 sec)
singlestore> select count(*) from gen_rows(500000000);
+-----------+
| count(*) |
+-----------+
| 500000000 |
+-----------+
1 row in set (4.44 sec)
singlestore> select count(*) from gen_rows(600000000);
+-----------+
| count(*) |
+-----------+
| 600000000 |
+-----------+
1 row in set (6.40 sec)
singlestore> select count(*) from gen_rows(700000000);
ERROR 1712 (HY000): Memory used by MemSQL (8328.62 Mb) has reached the 'maximum_memory' setting (14141 Mb) on this node.
Possible causes include (1) available query execution memory has been used up for table memory (in use table memory: 8058.62 Mb) and (2) the query is large and complex and requires more query execution memory than is available (in use query execution memory 0.00 Mb).
See https://docs.memsql.com/troubleshooting/latest/memory-errors for additional information.
실제 운영환경에서 임의의 Row 를 몇억건씩 만들어 사용할 일은 그리 많지 않을 것입니다. 하지만 대량의 데이터를 만들고 수정하고 삭제하는 테스트는 매우 빈번하게 일어납니다.
SingleStore 는 다른 데이터베이스보다 수십배 많은 데이터를 처리하면서도 빠른 응답이 가능해 이런 테스트마저도 쾌적하게 진행할 수 있습니다.
Image by starline on Freepik