이번 포스트에서는 SingleStore, MySQL, PostgreSQL 을 대상으로 8개의 Python Process 들이 동시에 10만 Row 를 Insert 하여 총 80만 Row 를 Insert 하는 작업의 성능을 비교 테스트하겠습니다. Shell Script 에서 각각의 Python 프로그램을 Background 로 실행하며 autocommit 모드를 True 로 명시적으로 설정하여 1건 Insert 후 바로 commit 하도록 했습니다. 각 프로세스들 간에 데이터 중첩은 없으므로 인위적인 Lock 발생은 배제하였습니다.
* 테스트 장비 사양 : 8vCPU, 16GB Memory 1대
■ ingest 데이터베이스 생성 및 t 테이블 생성
mysql> create database ingest;
Query OK, 1 row affected (0.00 sec)
mysql> use ingest;
Database changed
mysql> create table t (id varchar(10) primary key, val varchar(60));
Query OK, 0 rows affected (0.02 sec)
■ my.py 프로그램 및 my.sh
$ cat my.py
import MySQLdb
import time
import sys
def fn_insert(pname):
for i in range(100000):
sql = "INSERT INTO t VALUES (%s, %s)"
cursor.execute(sql, (pname+str(i).zfill(6), str(i).zfill(50)))
if __name__=="__main__":
start_time = time.time()
pname = sys.argv[1]
conn = MySQLdb.connect(host='localhost', user='root', password='Mysql09876*', db='ingest')
conn.autocommit(True)
cursor = conn.cursor()
fn_insert(pname)
conn.close()
print(time.time() - start_time)
$ cat my.sh
python3 my.py p0 &
python3 my.py p1 &
python3 my.py p2 &
python3 my.py p3 &
python3 my.py p4 &
python3 my.py p5 &
python3 my.py p6 &
python3 my.py p7 &
■ 실행 결과
8개의 프로세스가 동시에 백그라운드로 실행할 때 각각 330초 정도 소요되었습니다.
$ sh my.sh
$ 329.4230399131775
329.54405879974365
329.5909311771393
329.7053129673004
329.9453125
329.991329908371
330.01107716560364
330.09012746810913
$ mysql -uroot -pMysql09876* -P3307 -Dingest -e "select count(*) from t"
mysql: [Warning] Using a password on the command line interface can be insecure.
+----------+
| count(*) |
+----------+
■ ingest 데이터베이스 생성 및 t 테이블 생성
postgres=# create database ingest;
CREATE DATABASE
ingest=# GRANT ALL PRIVILEGES ON DATABASE ingest TO opc;
GRANT
ingest=# exit
$ psql -d ingest
psql (14.5)
Type "help" for help.
ingest=> create table t (id varchar(10) primary key, val varchar(60));
CREATE TABLE
■ pg.py 프로그램 및 pg.sh
$ cat pg.py
import psycopg2
import time
import sys
def fn_insert(pname):
for i in range(100000):
sql = "INSERT INTO t VALUES (%s, %s)"
cursor.execute(sql, (pname+str(i).zfill(6), str(i).zfill(50)))
if __name__=="__main__":
start_time = time.time()
pname = sys.argv[1]
conn = psycopg2.connect('dbname=ingest user=opc')
conn.set_session(autocommit=True)
cursor = conn.cursor()
fn_insert(pname)
conn.close()
print(time.time() - start_time)
$ cat pg.sh
python3 pg.py p0 &
python3 pg.py p1 &
python3 pg.py p2 &
python3 pg.py p3 &
python3 pg.py p4 &
python3 pg.py p5 &
python3 pg.py p6 &
python3 pg.py p7 &
■ 실행 결과
8개의 프로세스가 동시에 백그라운드로 실행할 때 각각 105초 정도 소요되었습니다.
$ sh pg.sh
$ 105.92883110046387
105.9339280128479
105.94141340255737
105.94537353515625
105.95170617103577
105.94119691848755
105.94967579841614
105.9411129951477
$ psql -d ingest -c 'select count(*) from t'
count
--------
800000
(1 row)
■ ingest 데이터베이스 생성 및 t 테이블 생성
singlestore> create database ingest;
Query OK, 1 row affected (1.88 sec)
singlestore> use ingest;
Database changed
singlestore> create rowstore table t (id varchar(10) primary key, val varchar(60));
Query OK, 0 rows affected (0.04 sec)
■ s2.py 프로그램 및 s2.sh
$ cat s2.py
import singlestoredb as s2
import time
import sys
def fn_insert(pname):
sql = "INSERT INTO t VALUES (:1, :2)"
for i in range(100000):
cursor.execute(sql, (pname+str(i).zfill(6), str(i).zfill(50)) )
if __name__=="__main__":
start_time = time.time()
pname = sys.argv[1]
conn = s2.connect('root:1234@localhost/ingest')
conn.autocommit(True)
cursor = conn.cursor()
fn_insert(pname)
conn.close()
print(time.time() - start_time)
$ cat s2.sh
python3 s2.py p0 &
python3 s2.py p1 &
python3 s2.py p2 &
python3 s2.py p3 &
python3 s2.py p4 &
python3 s2.py p5 &
python3 s2.py p6 &
python3 s2.py p7 &
■ 실행 결과
8개의 프로세스가 동시에 백그라운드로 실행할 때 각각 34초 정도 소요되었습니다.
이 경우는 RowStore In-Memory Table 이었고 ColumnStore Table 의 경우에는 약 40초로 6초 정도 더 소요되었습니다.
$ sh s2.sh
$ 34.125914335250854
34.28299522399902
34.3340106010437
34.354299783706665
34.390718936920166
34.436617374420166
34.44035577774048
34.48827075958252
$ singlestore -p1234 -e "select count(*) from ingest.t"
singlestore-client: [Warning] Using a password on the command line interface can be insecure.
+----------+
| count(*) |
+----------+
| 800000 |
+----------+
SingleStore, PostgreSQL, MySQL 순으로 Multi Process, Concurrent Insert 성능이 좋게 기록되었습니다. SingleStore 는 대량의 Batch Loading 이나 실시간 스트리밍 데이터 저장에 탁월한 pipeline 기능이 있음을 여러번 실제 테스트 결과와 함께 말씀 드렸습니다.
Pipeline 이 아니더라도 일반 OLTP SQL 을 가지고도 빠른 속도로 Ingest Transaction 을 수용할 수 있다는 것을 확인할 수 있었습니다. 실제 운영환경에서처럼 여러 Leaf Node 를 보다 사양이 좋은 장비에 별도 설치하여 사용한다면 훨씬 더 많은 동시 트랜잭션을 Low Latency 로 운영할 수 있을 것입니다.