[Python] SQLite 성능 벤치 마크 - 이유 : 메모리 : 너무 느림 ... 디스크보다 1.5 배 빠름?


Answers

내 질문은 당신이 벤치마킹하려는 것은 무엇입니까?

이미 언급했듯이 SQLite의 : 메모리 : DB는 디스크 기반 디스크와 동일합니다 (예 : 페이지 된). 유일한 차이점은 페이지가 디스크에 기록되지 않는다는 것입니다. 두 가지의 유일한 차이점은 디스크 쓰기입니다. 메모리 : 할 필요가 없습니다 (디스크 페이지를 캐시에서 오프로드해야 할 때도 디스크 읽기를 수행 할 필요가 없습니다).

그러나 캐시에 대한 읽기 / 쓰기는 쿼리에 따라 쿼리 처리 시간의 일부만을 나타낼 수 있습니다. 쿼리에는 선택된 행이 구성원이어야하는 두 개의 대형 ID 집합이있는 where 절이 있으며 비용이 많이 듭니다.

Cary Millsap이 오라클 최적화에 대한 블로그 ( http://carymillsap.blogspot.com/2009/06/profiling-with-my-boy.html 대표 게시판)에서 보여준 것처럼, 쿼리의 어느 부분을 이해해야하는지 처리 시간이 걸립니다. 집합 구성원 테스트가 쿼리 시간의 90 %를 차지하고 디스크 기반 IO가 10 %라고 가정하면 메모리 : 10 % 만 저장합니다. 이것은 대표적인 사례가 아닌 극단적 인 예이지만 특정 쿼리가 결과를 비틀어 버리고 있음을 보여주기를 바랍니다. 더 간단한 쿼리를 사용하면 쿼리 처리의 IO 부분이 증가하므로 메모리 :의 이점이 있습니다.

마지막으로 SQLite의 가상 테이블을 실험했습니다. 실제 테이블을 담당하고 SQLite의 셀 값 저장 방식과 달리 입력 된 C ++ 컨테이너를 사용하면 처리 시간이 현저하게 줄어들 수 있습니다 over : memory :, 그러나 조금 화제가되고있다.) --DD

추신 : 나는이 스레드의 가장 인기있는 게시물에 댓글을 달 수있는 카르마가 충분하지 않아 최근 SQLite 버전이 기본적으로 Windows에서 1KB 페이지를 사용하지 않는다고 말합니다. http : // www. sqlite.org/changes.html#version_3_6_12

Question

왜 : 메모리 : sqlite에서 그렇게 느린가요?

나는 메모리 기반 sqlite 대 디스크 기반 sqlite를 사용하여 얻은 성능 향상이 있는지 확인하려고 노력해 왔습니다. 기본적으로 응용 프로그램을 진행하는 동안 디스크에 충돌하지 않는 매우 빠른 쿼리를 얻기 위해 시작 시간과 메모리를 교환하고 싶습니다.

그러나 다음 벤치 마크는 개선 된 속도에서 1.5X의 요소 만 제공합니다. 여기에 무작위 데이터를 1M 행 생성하고 동일한 테이블의 디스크 기반 버전과 메모리 기반 버전 모두에로드합니다. 그런 다음 크기가 약 300k 인 두 db에 무작위 쿼리를 실행합니다. 나는 메모리 기반 버전이 상당히 빨라질 것으로 기대했지만 언급 한대로 1.5X 스피드 업만을 얻었습니다.

다른 크기의 dbs와 쿼리 세트를 실험했습니다. 장점 : 메모리 : db의 행 수가 늘어남에 따라 올라간 것처럼 보입니다. 나는 몇 가지 가설을 가지고 있지만 이점이 왜 그렇게 작은 지 잘 모르겠습니다.

  • 사용 된 테이블이 (행으로) 충분히 크지는 않습니다. memory : 거대한 승자
  • 조인 / 테이블이 많을수록 : 메모리 : 이점이 더 뚜렷해집니다.
  • 연결이나 OS 레벨에서 어떤 종류의 캐싱이 발생하여 이전 결과에 어떻게 든 액세스 할 수 있으므로 벤치 마크가 손상 될 수 있습니다
  • 어떤 종류의 숨겨진 디스크 액세스가 계속 진행되고 있습니다 (아직 시도하지는 않았지만 저널링을 위해 PRAGMA를 해제했습니다)

내가 여기서 뭔가 잘못하고있는거야? 이유에 대한 생각 : 메모리 : 거의 즉석 조회를하지 못하고 있습니까? 다음은 벤치 마크입니다.

==> sqlite_memory_vs_disk_benchmark.py <==

#!/usr/bin/env python
"""Attempt to see whether :memory: offers significant performance benefits.

"""
import os
import time
import sqlite3
import numpy as np

def load_mat(conn,mat):
    c = conn.cursor()

    #Try to avoid hitting disk, trading safety for speed.
    #http://.com/questions/304393
    c.execute('PRAGMA temp_store=MEMORY;')
    c.execute('PRAGMA journal_mode=MEMORY;')

    # Make a demo table
    c.execute('create table if not exists demo (id1 int, id2 int, val real);')
    c.execute('create index id1_index on demo (id1);')
    c.execute('create index id2_index on demo (id2);')
    for row in mat:
        c.execute('insert into demo values(?,?,?);', (row[0],row[1],row[2]))
    conn.commit()

def querytime(conn,query):
    start = time.time()
    foo = conn.execute(query).fetchall()
    diff = time.time() - start
    return diff

#1) Build some fake data with 3 columns: int, int, float
nn   = 1000000 #numrows
cmax = 700    #num uniques in 1st col
gmax = 5000   #num uniques in 2nd col

mat = np.zeros((nn,3),dtype='object')
mat[:,0] = np.random.randint(0,cmax,nn)
mat[:,1] = np.random.randint(0,gmax,nn)
mat[:,2] = np.random.uniform(0,1,nn)

#2) Load it into both dbs & build indices
try: os.unlink('foo.sqlite')
except OSError: pass

conn_mem = sqlite3.connect(":memory:")
conn_disk = sqlite3.connect('foo.sqlite')
load_mat(conn_mem,mat)
load_mat(conn_disk,mat)
del mat

#3) Execute a series of random queries and see how long it takes each of these
numqs = 10
numqrows = 300000 #max number of ids of each kind
results = np.zeros((numqs,3))
for qq in range(numqs):
    qsize = np.random.randint(1,numqrows,1)
    id1a = np.sort(np.random.permutation(np.arange(cmax))[0:qsize]) #ensure uniqueness of ids queried
    id2a = np.sort(np.random.permutation(np.arange(gmax))[0:qsize])
    id1s = ','.join([str(xx) for xx in id1a])
    id2s = ','.join([str(xx) for xx in id2a])
    query = 'select * from demo where id1 in (%s) AND id2 in (%s);' % (id1s,id2s)

    results[qq,0] = round(querytime(conn_disk,query),4)
    results[qq,1] = round(querytime(conn_mem,query),4)
    results[qq,2] = int(qsize)

#4) Now look at the results
print "  disk | memory | qsize"
print "-----------------------"
for row in results:
    print "%.4f | %.4f | %d" % (row[0],row[1],row[2])

결과는 다음과 같습니다. 상당히 넓은 범위의 쿼리 크기에 대해 디스크의 메모리 사용 시간은 약 1.5 배입니다.

[ramanujan:~]$python -OO sqlite_memory_vs_disk_clean.py
  disk | memory | qsize
-----------------------
9.0332 | 6.8100 | 12630
9.0905 | 6.6953 | 5894
9.0078 | 6.8384 | 17798
9.1179 | 6.7673 | 60850
9.0629 | 6.8355 | 94854
8.9688 | 6.8093 | 17940
9.0785 | 6.6993 | 58003
9.0309 | 6.8257 | 85663
9.1423 | 6.7411 | 66047
9.1814 | 6.9794 | 11345

RAM이 디스크에 비해 거의 순간적이지 않아야합니까? 여기서 뭐가 잘못 됐어?

편집하다

여기에 좋은 제안.

내게있어 필자가 생각하는 주요 포인트는 ** 만들 방법이 없다는 것입니다 : 메모리 : 절대적으로 빠르지 만 디스크 액세스를 상대적으로 느리게 하는 방법이 있습니다 . **

즉, 벤치 마크는 메모리의 실제 성능을 적절하게 측정하지만 디스크의 실제 성능은 측정하지 않습니다 (예 : cache_size pragma가 너무 커서 또는 쓰기를하지 않기 때문에). 나는 그 매개 변수로 주위를 어지럽히고 기회가 생겼을 때 나의 발견을 게시 할 것이다.

즉, 메모리 내 db에서 속도를 좀 더 높일 수 있다고 생각하는 사람이 있다면 (내가 할 Cache_size 및 default_cache_size를 올려 놓는 것 이외에는), 나는 모두 귀를 기울이고 있습니다.




numpy 배열은 시퀀스에서 5 백만 개 이상의 객체를 처리 할 때까지 dict 및 tuple 및 다른 객체 시퀀스보다 느립니다. 대규모 반복 데이터를 반복하고 생성자를 사용하여 임시 대형 객체를 만들고 재 작성하지 않아도되므로 방대한 양의 데이터를 처리하는 속도를 크게 향상시킬 수 있습니다.

선형 성능을 제공하도록 설계되었으므로 numpy가 제한 요소가되었습니다. 작거나 큰 양의 데이터를 가진 별이 아닙니다. 그러나 numpy의 성능은 데이터 세트가 커짐에 따라 곡선으로 변하지 않습니다. 그것은 직선으로 유지됩니다.

게다가 SQLite는 매우 빠른 데이터베이스입니다. 대부분의 서버 데이터베이스보다 빠릅니다. SQL을 사용하는 경량의 초고속 폴트 톨러런스 데이터베이스가 브라우저에서부터 휴대폰에 이르기까지 수년 동안 테스트되어 왔을 때 왜 NOSQL 데이터베이스를 사용해야하는지에 대한 질문을 제기합니다.




sqlite3 실제로 캐시에서 디스크에 데이터를 쓰지 않을 수 있을까요? 왜 숫자가 유사한 지 설명 할 수 있습니다.

사용 가능한 메모리가 적어 OS가 페이징 할 수도 있습니다.




SQLite의 메모리 데이터베이스는 실제로 디스크에 닿지 않는 페이지 캐시입니다. 따라서 성능 조정을 위해 SQLite에서 메모리 db를 사용하는 것을 잊어야합니다.

저널을 끄고 동기화 모드를 끄고 대형 페이지 캐시를 설정할 수 있으며 대부분의 작업에서 거의 동일한 성능을 갖지만 내구성은 손실됩니다.

코드에서 테스트 수행의 90 % 이상을 수행 했으므로 명령 과 유일한 BIND 매개 변수를 다시 사용해야한다고 분명합니다.