2010-12-30 5 views
1

저는 MySQL을 처음 사용하기 때문에 메모리에 관해 질문이 있습니다.MySQL에 대한 메모리 문제 "SELECT *"

나는 200mb 테이블 (MyISAM, 2,000,000 개의 행)을 가지고 있으며,이 모든 것을 메모리에로드하려고합니다.

나는 python (실제로는 Python의 MySQLdb)을 sql과 함께 사용한다 : SELECT * FROM table.

하지만, 내 리눅스에서 "최고"나는이 파이썬 프로세스는 200 메가 테이블 나는 그것이 3기가바이트 메모리에 대한 사용 이유에 대해 궁금 해서요 (6기가바이트 총입니다) 내 기억의 50 %

를 사용했다 . 미리 감사드립니다.

+0

프로덕션 데이터베이스 바인딩은 요청 크기에 비례하여 메모리를 사용해야하지 않습니다. 주변에 결과를 보관하지 않는 한 더 많은 데이터를 수신하면 이전 데이터가 공개되어야합니다. 모든 데이터베이스 바인딩이이 작업을 제대로 수행하는 것은 아니지만 항상 버그입니다. 데이터베이스 백엔드에서 무한 범위를 선택하고 결과를 점진적으로 처리 할 수 ​​있어야하며 메모리 사용량은 제한없이 근거가되어서는 안됩니다. –

+0

'select *'를 사용하는 것은 거의 불가능합니다. 일부 DB 도구에도 불구하고. 일반적으로 원하는 열을 명시 적으로 나열해야합니다. – paxdiablo

답변

1

본인이하는 일에 본질적으로 잘못된 것은 없습니다. 쿼리의 크기에 따라 메모리 사용량이 증가하는 경우 다음 중 하나가 발생합니다.

  • 받는 결과에 대한 참조가 누출됩니다. 예를 들어 어딘가에 목록에 넣을 수 있습니다. 네가 그 일을하는지 알았 겠지.
  • 데이터베이스 바인딩 또는 기본 라이브러리가 쿼리에서 새 행을 읽는 동안 이전 행의 메모리를 해제하지 않습니다. 이것은 대개 버그입니다. 디버깅 기능이있는 경우 정상적으로 발생할 수 있지만 기본적으로 발생해서는 안됩니다.

기본 라이브러리는 일정량의 데이터를 캐시 할 수 있으며 결과적으로 상당한 메모리 사용량을 볼 수 있지만 구성에 심각한 오류가없는 한 3GB가되어서는 안됩니다.

여러분이하고있는 것을 재현하는 간단한 SQLite 코드가 있습니다. 실행하면 15 백만 행의 사소한 테이블을 만듭니다.이 테이블은 사용중인 버전의 디스크에 약 180MB입니다. 그런 다음이 모든 데이터를 선택하고 결과를 버리며 결과를 검사 할 수 있도록 잠자기합니다. 내 시스템에서 결과 프로세스는 15MB 만 사용합니다.

SQLite는이 처리 할 수 ​​있으며, MySQL과 PostgreSQL을 같은 프로덕션 서버 백업 데이터베이스는 할 수 있어야;

는 (데이터베이스를 만드는 것은 약간의 시간이 소요됩니다. 내가 별도의 호출로 create_dbread_db 패스를 실행합니다) , 너무. SELECT 결과는 데이터 스트림이며 데이터베이스는 제한없는 크기의 스트림을 쉽게 처리 할 수 ​​있어야합니다.결국 아마 있다고하지만 당신은 수동으로 청크 쿼리를 필요가 없습니다 -
import sqlite3 
def create_db(conn): 
    c = conn.cursor() 
    c.execute('create table test (i integer)') 
    conn.commit() 
    max_val = 15000000 
    chunk = 1000000 
    for start in xrange(0, max_val, chunk): 
     print "%i ..." % start 
     for i in xrange(start, start + chunk): 
      c = conn.cursor() 
      c.execute('insert into test (i) values (?)', (i,)) 
     conn.commit() 

def read_db(conn): 
    c = conn.cursor() 
    c.execute('select * from test') 
    for x in xrange(15000000): 
     c.fetchone() 

    print "Done" 

    # Sleep forever, to examine memory usage: 
    while True: 
     time.sleep(1) 

def go(): 
    conn = sqlite3.connect('test.db') 

    # Pick one: 
    create_db(conn) 
    # read_db(conn) 

if __name__ == "__main__": 
    go() 

은 귀하의 질문에 대답하지 않습니다,하지만 난 당신이 무슨 일을하는지 아무 문제가 없다는 것을 분명히 확인하고 싶었 해결 방법이 필요합니다.

+0

고마워요! 나는 이유를 찾으려고 노력할 것이다! 다시 한 번 감사드립니다! –

0

이것은 거의 틀린 설계입니다.

메모리의 모든 데이터를 한꺼번에 처리하고 있습니까?

한 명의 사용자를위한 것이라면 여러 사용자를 지원할 수 있도록 크기를 줄 이십시오.

중간 계층에서 계산을 수행하는 경우 모든 데이터를 메모리에 가져올 필요가 없도록 데이터베이스 서버로 작업을 전환 할 수 있습니까?

당신은 이것을 할 수 있다는 것을 알고 있습니다. 그러나 더 큰 질문은 (1) 왜입니까? 그리고 (2) 그 밖의 무엇을 할 수 있 었는가? 우리는 이에 답하기 위해 더 많은 컨텍스트가 필요합니다.

+0

답장을 보내 주셔서 감사합니다. 나는 내가 다음에해야 할 일을 알고 있다고 생각합니다. 사실 나는 old_table에서 일부 데이터를 처리하고 행별로 new_table에 결과를 삽입합니다. 그래서 데이터를 더 작은 세트로 분할하고 하나씩 처리해야합니다. 감사! –

+0

나는 저장 프로 시저를 사용하고 데이터베이스에서 작업하는 것이이 경우 더 합리적 일 수 있음을 제안한다. – duffymo

1

거의 모든 스크립팅 언어에서 변수는 실제 내용보다 더 많은 메모리를 차지합니다. INT는 32 비트 또는 64 비트 일 수 있으며 4 또는 8 바이트의 메모리가 필요하지만 16이나 32 바이트 (모자에서 번호를 가져옴)를 차지합니다. 언어 인터프리터는 다양한 메타 데이터를 방법.

데이터베이스에는 원시 저장 공간이 200MB 만 필요할 수 있지만 일단 메타 데이터를 고려하면 확실히 훨씬 많은 용량을 차지하게됩니다.

0

이것은 MySQLdb에서 Marc B의 답변과 버그 (버그가 아님)의 조합입니다. MySQLdb의 기본 커서는 클라이언트 측 커서입니다. 즉, 클라이언트 라이브러리는 클라이언트 프로세스 내의 메모리에있는 전체 결과 집합을 마샬링합니다. (기본적으로 서버 쪽 커서 클래스를 사용하여)이 문제에 대한 해결책을

How to get a row-by-row MySQL ResultSet in python

에 대한 답변을 참조하십시오. 선택한 열에서 반환 한 열의 수가 많을수록 각 열이 추가 인터프리터 메타 데이터를 작성하기 때문에 기대할 수있는 메모리 확장이 더 커집니다. 수십 개의 열이 없으면 3Gb가 큰 것처럼 보입니다.