1

生の SQL を使用して sqlalchemy で単純な SELECT クエリに時間がかかる理由を理解するのに苦労しています (14600 行/秒を取得していますが、sqlalchemy を使用せずに psycopg2 を介して同じクエリを実行すると、 38421 行/秒)。

いろいろ調べてみたところ、create_engine 呼び出しで sqlalchemy の use_native_unicode パラメータを切り替えると、実際には大きな違いが生じることに気付きました。

このクエリは、7300 行を取得するのに 0.5 秒かかります。

from sqlalchemy import create_engine

engine = create_engine("postgresql+psycopg2://localhost...",
                       use_native_unicode=True)
r = engine.execute("SELECT * FROM logtable")
fetched_results = r.fetchall()

このクエリは、同じ 7300 行を取得するのに 0.19 秒かかります。

engine = create_engine("postgresql+psycopg2://localhost...",
                       use_native_unicode=False)
r = engine.execute("SELECT * FROM logtable")
fetched_results = r.fetchall()

2 つのクエリの唯一の違いは、use_native_unicode です。しかし、sqlalchemy 自身のドキュメントでは、use_native_unicode=True (http://docs.sqlalchemy.org/en/latest/dialects/postgresql.html) のままにしておく方がよいと述べています。

use_native_unicode がパフォーマンスに大きな違いをもたらしている理由を誰か知っていますか? また、use_native_unicode をオフにするとどのような影響がありますか?

4

2 に答える 2

2

この問題は、処理している非ASCIIデータの量に基づいて決定する必要がある問題です。psycopg2のUnicodeをデコードする方法は、SQLAのC拡張機能が使用されていないことを前提とすると、SQLAlchemyの方法よりも高速ですが、Unicode変換を行わない場合と比較して、結果セットのレイテンシーが追加されます。上記のコードでは、SQLAlchemyのユニコード機能は使用されていません。これらは、列がUnicodeまたはStringタイプにマップされている場合にのみ使用されます。これは、text()、select()、または同等のORMレベルを使用している場合にのみ発生し、Unicodeタイプはこれらの結果セット列にマップされます。テーブルメタデータを使用して、text()の「typemap」パラメータ。

Psycopg2のネイティブUnicode機能OTOHはカーソルレベルで有効になるため、常に有効であり、全体としてある程度の遅延が追加されるようです。

以下は、さまざまな方法がどのように機能するかを示す一連の図です。最後のものはSQLAlchemyのものに最も似ていますが、SQLAlchemyのC拡張機能を使用する場合、おそらくpsycopg2と同じくらい高速です。

import psycopg2
from psycopg2 import extensions

conn = psycopg2.connect(user='scott', password='tiger', host='localhost', database='test')

cursor = conn.cursor()
cursor.execute("""
create table data (
    id SERIAL primary key,
    data varchar(500)
)
""")

cursor.executemany("insert into data (data) values (%(data)s)", [
        {"data":"abcdefghij" * 50} for i in xrange(10000)
    ])
cursor.close()


def one(conn):
    cursor = conn.cursor()
    cursor.execute("SELECT data FROM data")
    for row in cursor:
        row[0]

def two(conn):
    cursor = conn.cursor()
    extensions.register_type(extensions.UNICODE, cursor)
    cursor.execute("SELECT data FROM data")
    for row in cursor:
        row[0]

def three(conn):
    cursor = conn.cursor()
    cursor.execute("SELECT data FROM data")
    for row in cursor:
        row[0].decode('utf-8')

def four(conn):
    cursor = conn.cursor()
    def conv_unicode(value):
        return value.decode('utf-8')
    cursor.execute("SELECT data FROM data")
    for row in cursor:
        conv_unicode(row[0])

import timeit

print "no unicode:", timeit.timeit("one(conn)", "from __main__ import conn, one", number=100)

print "native unicode:", timeit.timeit("two(conn)", "from __main__ import conn, two", number=100)

print "in Python unicode:", timeit.timeit("three(conn)", "from __main__ import conn, three", number=100)

print "more like SQLA's unicode:", timeit.timeit("four(conn)", "from __main__ import conn, four", number=100)

私が得るタイミング:

no unicode: 2.10434007645
native unicode: 4.52875208855
in Python unicode: 4.77912807465
more like SQLA's unicode: 4.88325881958

したがって、ここで興味深いのは、SQLAのアプローチは、おそらくC拡張機能を使用した場合、実際にはUnicodeタイプとほとんどの文字列をあま​​り使用しない場合、psycopg2のネイティブアプローチよりも実際に良い選択である可能性があることです。値は純粋なASCIIのみです。

于 2012-11-20T21:47:20.380 に答える