Concurrent Insert 성능 비교 - SingleStore, PostgreSQL, MySQL

Jongsoo Noh·2022년 10월 25일
0

SingleStore

목록 보기
16/20
post-thumbnail

🎯 Concurrent Insert 성능 비교

이번 포스트에서는 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대

1. MySQL

■ 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(*) |
+----------+

2. PostgreSQL

■ 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)

3. SingleStore

■ 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 로 운영할 수 있을 것입니다.

profile
Database Guy

0개의 댓글