LoginSignup
1
2

More than 3 years have passed since last update.

SqlAlchemyのコネクションプーリングの効果比較

Posted at

SqlAlchemyのコネクションプーリングの効果比較

コネクションプーリングのあり・なしを比較してみた。
それぞれ、 psycopg2pg8000 で確認してした。

結果は、コネクションプーリングなしが 280~310倍遅いということになったが、それ以上に今回の測定では pg8000 が若干早いという結果になった。

環境

  • Ubuntu 18.04 LTS ... WSL1 (Windows 10)
    • libpq ... 13.2
  • Python 3.8
    • SQLAlchemy 1.4.6
    • pg8000 1.19.1(Pure Python)
    • psycopg2 2.8.6 (Nativeライブラリを使用)

測定方法

測定方法は、参考1のやり方と合わせ1多重で1000回の接続・切断を行う。
ただし、コードの書き方は厳密に合わせはしない。なるべく今風の書き方をする。

なお、SqlAlchemyではデフォルトで QueuePool というものを使う。
コネクションプーリングを明示的に使わないようにするには NullPool を使う。

趣味の範囲なので、複数測った平均だとかはとらず、一発取り。1000回の試行の合計なので、それだけでバラつきは減る。プログラムの調整で複数回は実行するがバラつきはおよそ±3秒の範囲。

結果(概要)

参考にしたサイトだと 270 倍だったが、同じような水準になった。
なお、PCが貧弱で、かつ WSL1 を使っているので遅い面はあるので、相対値のみ参考にしてください。
(注: 1000で割った数が1コネクションあたりの時間になります。)

pg8000 psycopg2
QueuePool 0.078 [s/1000conn] 0.094 [s/1000conn]
NullPool 21.747s [s/1000conn] 28.930 [s/1000conn]
Ratio 279倍 307倍

検証結果から言えること

  • コネクションにかかるコストは相対的に大きい
    1コネクションでみると一番遅いパターンでも 0.028 秒なので体感して遅い訳ではない
  • pg8000(Pure Python) より、psycopg2(ネイティブクライアント使用)のが若干遅い

検証結果に対する補足

  • psycopg2 の方が遅いのは、この計測の場合 Python → C の呼び出しに関わるオーバヘッド(メモリ確保やデータ変換などの橋渡し)の影響が大きく出るのではないかと思われる
  • 常駐プログラムでない場合は NullPool相当となる
    • その都度処理が終わるなら当然コネクションの使いまわしができないから

検証コード

共通化することはできるのだが、まぁいいや。

pg8000 + NullPool

# -*- coding: utf-8 -*-
"""Example of postgresql+pg8000."""
import os
import time

import sqlalchemy


def main(engine):
    """Run main."""
    t_0 = time.time()
    for _ in range(1000):
        with engine.connect():
            pass
    t_1 = time.time()
    print(f'dt = {(t_1 - t_0):.3f}s')


if __name__ == '__main__':
    engine_pg8000 = sqlalchemy.create_engine(
        sqlalchemy.engine.URL.create(
            'postgresql+pg8000',
            host=os.environ.get('PGHOST'),
            port=os.environ.get('PGPORT'),
            database=os.environ.get('PGDATABASE'),
            username=os.environ.get('PGUSER'),
            password=os.environ.get('PGPASSWORD')
        ),
        poolclass=sqlalchemy.pool.NullPool
    )
    main(engine_pg8000)
    engine_pg8000.dispose()

# EOF

pg8000 + QueuePool(default)

# -*- coding: utf-8 -*-
"""Example of postgresql+pg8000."""
import os
import time

import sqlalchemy


def main(engine):
    """Run main."""
    t_0 = time.time()
    for _ in range(1000):
        with engine.connect():
            pass
    t_1 = time.time()
    print(f'dt = {(t_1 - t_0):.3f}s')


if __name__ == '__main__':
    engine_pg8000 = sqlalchemy.create_engine(
        sqlalchemy.engine.URL.create(
            'postgresql+pg8000',
            host=os.environ.get('PGHOST'),
            port=os.environ.get('PGPORT'),
            database=os.environ.get('PGDATABASE'),
            username=os.environ.get('PGUSER'),
            password=os.environ.get('PGPASSWORD')
        ),
        pool_size=1,
        max_overflow=1
    )
    main(engine_pg8000)
    engine_pg8000.dispose()

# EOF

psycopg2 + NullPool

# -*- coding: utf-8 -*-
"""Example of postgresql+psycopg2."""
import os
import time

import sqlalchemy


def main(engine):
    """Run main."""
    t_0 = time.time()
    for _ in range(1000):
        with engine.connect():
            pass
    t_1 = time.time()
    print(f'dt = {(t_1 - t_0):.3f}s')


if __name__ == '__main__':
    engine_psycopg2 = sqlalchemy.create_engine(
        sqlalchemy.engine.URL.create(
            'postgresql+psycopg2',
            host=os.environ.get('PGHOST'),
            port=os.environ.get('PGPORT'),
            database=os.environ.get('PGDATABASE'),
            username=os.environ.get('PGUSER'),
            password=os.environ.get('PGPASSWORD')
        ),
        poolclass=sqlalchemy.pool.NullPool
    )
    main(engine_psycopg2)
    engine_psycopg2.dispose()

# EOF

psycopg2 + QueuePool(default)

# -*- coding: utf-8 -*-
"""Example of postgresql+psycopg2."""
import os
import time

import sqlalchemy


def main(engine):
    """Run main."""
    t_0 = time.time()
    for _ in range(1000):
        with engine.connect():
            pass
    t_1 = time.time()
    print(f'dt = {(t_1 - t_0):.3f}s')


if __name__ == '__main__':
    engine_psycopg2 = sqlalchemy.create_engine(
        sqlalchemy.engine.URL.create(
            'postgresql+psycopg2',
            host=os.environ.get('PGHOST'),
            port=os.environ.get('PGPORT'),
            database=os.environ.get('PGDATABASE'),
            username=os.environ.get('PGUSER'),
            password=os.environ.get('PGPASSWORD')
        ),
        pool_size=1,
        max_overflow=1
    )
    main(engine_psycopg2)
    engine_psycopg2.dispose()

# EOF

参考

測定の参考にしたサイト

SQLAlchemy関連

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2