2012-07-09 3 views
1

sqlalchemy (파이썬 2.7)로 두 개의 테이블을 만들었습니다. 데이터베이스는 mysql 5.5입니다. 다음은 내 코드입니다 :열 수가 sqlalchemy의 속도에 영향을 줍니까?

engine = create_engine('mysql://root:[email protected]/test') 

metadata = MetaData() 

conn = engin.connect() 

# For table 1: 

columns = [] 

for i in xrange(100): 

    columns.append(Column('c%d' % i, TINYINT, nullable = False, server_default = '0')) 
    columns.append(Column('d%d' % i, SmallInteger, nullable = False, server_default = '0')) 

user = Table('user', metadata, *columns) 
# So user has 100 tinyint columns and 100 smallint columns. 

# For table 2: 

user2 = Table('user2', metadata, 

     Column('c0', BINARY(100), nullable = False, server_default='\0'*100), 
     Column('d0', BINARY(200), nullable = False, server_default='\0'*200), 
) 

# user2 has two columns contains 100 bytes and 200 bytes respectively. 

I then inserted 4000 rows into each table. Since these two tables have same row length, I 
expect the select speed will be almost the same. I ran the following test code: 

s1 = select([user]).compile(engine) 

s2 = select([user2]).compile(engine) 

t1 = time() 

result = conn.execute(s1).fetchall() 

print 't1:', time() - t1 

t2 = time() 

result = conn.execute(s2).fetchall() 

print 't2', time() - t2 

The result is : 

t1: 0.5120000 

t2: 0.0149999 

이 극적으로 SQLAlchemy의 성능 에 영향을 미칠 것입니다 테이블의 열 수를 의미합니까? 미리 감사드립니다.

답변

2

테이블의 열 수가 극적으로 SQLAlchemy의 성능에 영향을 줍니까?

잘 힘든 일 이잖아, 그것은 아마도 동일한 인터페이스를 사용하는 동안 다른 DB 엔진과 상호 작용하는 방법에 지나지이없는, 실제로 sqlalchemy 다음,이 경우 기본 SQL 엔진, MySQL에 더 의존 .

SQLAlchemy는 응용 프로그램 개발자에게 SQL의 강력 함과 유연성을 제공하는 Python SQL 툴킷과 Object Relational Mapper입니다.

간단하고 Python 도메인 언어에 맞게 효율적이고 고성능 데이터베이스 액세스를 위해 설계된 잘 알려진 엔터프라이즈 수준의 지속성 패턴 모음을 제공합니다.

내가 틀릴 수도 있지만 표준 SQL을 사용하여 벤치마킹을 시도해 볼 수 있습니다. 나는 실제로 몇 가지 테스트를 실행

...

import timeit 

setup = """ 
from sqlalchemy import create_engine, MetaData, select, Table, Column 
from sqlalchemy.dialects.sqlite import BOOLEAN, SMALLINT, VARCHAR 
engine = create_engine('sqlite://', echo = False) 
metadata = MetaData() 
conn = engine.connect() 
columns = [] 

for i in xrange(100): 
    columns.append(Column('c%d' % i, VARCHAR(1), nullable = False, server_default = '0')) 
    columns.append(Column('d%d' % i, VARCHAR(2), nullable = False, server_default = '00')) 


user = Table('user', metadata, *columns) 
user.create(engine) 
conn.execute(user.insert(), [{}] * 4000) 

user2 = Table('user2', metadata, Column('c0', VARCHAR(100), nullable = False, server_default = '0' * 100), \ 
           Column('d0', VARCHAR(200), nullable = False, server_default = '0' * 200)) 
user2.create(engine) 
conn.execute(user2.insert(), [{}] * 4000) 
""" 

many_columns = """ 
s1 = select([user]).compile(engine) 
result = conn.execute(s1).fetchall() 
""" 

two_columns = """ 
s2 = select([user2]).compile(engine) 
result = conn.execute(s2).fetchall() 
""" 

raw_many_columns = "res = conn.execute('SELECT * FROM user').fetchall()" 
raw_two_columns = "res = conn.execute('SELECT * FROM user2').fetchall()" 

timeit.Timer(two_columns, setup).timeit(number = 1) 
timeit.Timer(raw_two_columns, setup).timeit(number = 1) 
timeit.Timer(many_columns, setup).timeit(number = 1) 
timeit.Timer(raw_many_columns, setup).timeit(number = 1) 

>>> timeit.Timer(two_columns, setup).timeit(number = 1) 
0.010751008987426758 
>>> timeit.Timer(raw_two_columns, setup).timeit(number = 1) 
0.0099620819091796875 
>>> timeit.Timer(many_columns, setup).timeit(number = 1) 
0.23563408851623535 
>>> timeit.Timer(raw_many_columns, setup).timeit(number = 1) 
0.21881699562072754 

내가이 찾을 않았다 가지 흥미로운 그는 테스트 max 사용하지만했다
http://www.mysqlperformanceblog.com/2009/09/28/how-number-of-columns-affects-performance/

...

난 정말 sqlalchemy 사랑해, 그래서 비단뱀 자신의 sqlite3 모듈을 사용하여 그것을 비교하기로 결정했습니다

import timeit 
setup = """ 
import sqlite3 
conn = sqlite3.connect(':memory:') 
c = conn.cursor() 

c.execute('CREATE TABLE user (%s)' %\ 
      ("".join(("c%i VARCHAR(1) DEFAULT '0' NOT NULL, d%i VARCHAR(2) DEFAULT '00' NOT NULL," % (index, index) for index in xrange(99))) +\ 
      "c99 VARCHAR(1) DEFAULT '0' NOT NULL, d99 VARCHAR(2) DEFAULT '0' NOT NULL")) 

c.execute("CREATE TABLE user2 (c0 VARCHAR(100) DEFAULT '%s' NOT NULL, d0 VARCHAR(200) DEFAULT '%s' NOT NULL)" % ('0'* 100, '0'*200)) 

conn.commit() 
c.executemany('INSERT INTO user VALUES (%s)' % ('?,' * 199 + '?'), [('0',) * 200] * 4000) 
c.executemany('INSERT INTO user2 VALUES (?,?)', [('0'*100, '0'*200)] * 4000) 
conn.commit() 
""" 

many_columns = """ 
r = c.execute('SELECT * FROM user') 
all = r.fetchall() 
""" 

two_columns = """ 
r2 = c.execute('SELECT * FROM user2') 
all = r2.fetchall() 
""" 

timeit.Timer(many_columns, setup).timeit(number = 1) 
timeit.Timer(two_columns, setup).timeit(number = 1) 

>>> timeit.Timer(many_columns, setup).timeit(number = 1) 
0.21009302139282227 
>>> timeit.Timer(two_columns, setup).timeit(number = 1) 
0.0083379745483398438 

같은 결과가 나왔으므로 데이터베이스 구현이 sqlalchemy이 아니라고 생각합니다. sqlite3를 모듈

DEFAULT의 INSERT

import timeit 

setup = """ 
from sqlalchemy import create_engine, MetaData, select, Table, Column 
from sqlalchemy.dialects.sqlite import BOOLEAN, SMALLINT, VARCHAR 
engine = create_engine('sqlite://', echo = False) 
metadata = MetaData() 
conn = engine.connect() 
columns = [] 

for i in xrange(100): 
    columns.append(Column('c%d' % i, VARCHAR(1), nullable = False, server_default = '0')) 
    columns.append(Column('d%d' % i, VARCHAR(2), nullable = False, server_default = '00')) 


user = Table('user', metadata, *columns) 
user.create(engine) 

user2 = Table('user2', metadata, Column('c0', VARCHAR(100), nullable = False, server_default = '0' * 100), \ 
           Column('d0', VARCHAR(200), nullable = False, server_default = '0' * 200)) 
user2.create(engine) 
""" 

many_columns = """ 
conn.execute(user.insert(), [{}] * 4000) 
""" 

two_columns = """ 
conn.execute(user2.insert(), [{}] * 4000) 
""" 

>>> timeit.Timer(two_columns, setup).timeit(number = 1) 
0.017949104309082031 
>>> timeit.Timer(many_columns, setup).timeit(number = 1) 
0.047809123992919922 

테스트.

import timeit 
setup = """ 
import sqlite3 
conn = sqlite3.connect(':memory:') 
c = conn.cursor() 

c.execute('CREATE TABLE user (%s)' %\ 
    ("".join(("c%i VARCHAR(1) DEFAULT '0' NOT NULL, d%i VARCHAR(2) DEFAULT '00' NOT NULL," % (index, index) for index in xrange(99))) +\ 
      "c99 VARCHAR(1) DEFAULT '0' NOT NULL, d99 VARCHAR(2) DEFAULT '0' NOT NULL")) 

c.execute("CREATE TABLE user2 (c0 VARCHAR(100) DEFAULT '%s' NOT NULL, d0 VARCHAR(200) DEFAULT '%s' NOT NULL)" % ('0'* 100, '0'*200)) 
conn.commit() 
""" 

many_columns = """ 
c.executemany('INSERT INTO user VALUES (%s)' % ('?,' * 199 + '?'), [('0', '00') * 100] * 4000) 
conn.commit() 
""" 

two_columns = """ 
c.executemany('INSERT INTO user2 VALUES (?,?)', [('0'*100, '0'*200)] * 4000) 
conn.commit() 
""" 

timeit.Timer(many_columns, setup).timeit(number = 1) 
timeit.Timer(two_columns, setup).timeit(number = 1) 

>>> timeit.Timer(many_columns, setup).timeit(number = 1) 
0.14044189453125 
>>> timeit.Timer(two_columns, setup).timeit(number = 1) 
0.014360189437866211 
>>>  
+0

훌륭한 의견을 보내 주셔서 감사합니다. 실제로 관련 질문 [int 및 binary 삽입 속도] (http://stackoverflow.com/questions/11388729/the-insert-speed-of-int-and-binary)를 물었습니다. 필자는 동일한 테이블에서 sql을 사용하여 삽입 속도를 테스트 한 결과 더 많은 열이있는 테이블에 대해 삽입 속도가 빠르다는 사실을 발견했습니다. 이상하지 않니? @ samy.vilar – heller

+0

재미있는 @heller, 간단한/빈 삽입을 사용하여 간단한 간단한 테스트를했는데 채워졌지만 VARCHAR를 사용하여 어디에서나 재미있는 전환 확인을 확인했는데 two_columns가있는 위치가 더 빠르다는 것을 확인했습니다. MySQL이 할 수있는 일이 무엇인지 모르겠다. 기사에서'InooDB가 훨씬 느리다 '고 말한 적이있다. –

+0

"[col for row in col]"와 같은 각 열의 각 열에 액세스하면 SQLAlchemy 예제는 약간의 오버 헤드가 있습니다. SQLite를 사용하면 열 유형의 데이터 프로세서 만 사용할 수 있습니다. 더구나, 당신은 여기에서 다만 핵심을 사용하게 기쁘다! ORM을 사용하면 전체 개체를로드하는 경우 오버 헤드가 훨씬 더 중요합니다. – zzzeek

1

Samy.vilar의 대답은 우수합니다. 그러나 기억해야 할 핵심 사항 중 하나는 열의 수가 데이터베이스 및 ORM의 성능에 영향을 미친다는 것입니다. 보유한 열의 수가 많을수록 디스크에서 더 많은 데이터에 액세스하고 전송할 수 있습니다.

또한 쿼리 및 테이블 구조에 따라 더 많은 열을 추가하면 쿼리가 인덱스로 덮여 기본 테이블에 대한 액세스가 강제로 변경되어 특정 데이터베이스 및 특정 상황에서 상당한 시간을 추가 할 수 있습니다.

저는 SQLAlchemy를 조금 사용해 봤지만 DBA로서 필자가 필요로하는 열을 쿼리하고 프로덕션 코드에서 "select *"사용을 피하는 개발자들에게 일반적으로 조언합니다. 필요한 열보다 많은 열을 포함 할 가능성이 있으며 잠재적 열이 테이블/뷰에 추가 될 때 코드가 더 부서지기 때문입니다.

+0

고마워. 그는 당신의 성명과 관련하여 같은 길이의 행을 확인했다. 더 많은 컬럼을 가지고 더 많은 데이터가 디스크에서 액세스되어 전송되었다. 그는 100 바이트 1 바이트와 2 바이트 컬럼 (300 바이트 행)은 1 100 바이트와 1 200 바이트 열 (다시 300 바이트 행)을 읽는 것이 훨씬 느릴 것이라고 그는 생각하지 않을 것입니다.이 문제는 대부분의 SQL 엔진에서 여러 번 처리 할 때 발생하는 오버 헤드라고 생각합니다. 일반적으로 열은 열의 수가 증가함에 따라 늘어납니다. 그러나 이것은 단지 직감입니다 ... –

+0

고마워, 나는 apparantly 그것에 충분히 깊게 읽지 않았다. 여러분이 말했듯이, 컬럼과 관련된 오버 헤드가 있습니다. 보완에 대한 답변은 – TimothyAWiseman

+0

+1입니다.) 질문에 대한 대답을 시도하기 위해, 나는 이것에 대해 조금 놀랐습니다. 구현에 따라 다름이라고 생각합니다. –

관련 문제