2009-06-07 9 views
19

PostgreSQL 8.3을 저장소 백엔드 (Python 및 psycopg2 사용)로 사용하는 특정 응용 프로그램이 있습니다. 중요한 테이블에 대해 수행하는 작업은 대부분 삽입 또는 업데이트 (드물게 삭제 또는 선택)입니다.PostgreSQL에서 업데이트/교체 작업의 속도를 높일 수 있습니까?

우리는 합리적으로 잘 작동하는 우리 자신의 Data Mapper과 같은 레이어를 만들었지 만 병목 현상이 많아 업데이트 성능이 떨어졌습니다. 물론, 나는 update/replace 시나리오가 '빈 테이블에 삽입'만큼 빠르다고 기대하지는 않지만 조금 더 가깝게하는 것이 좋을 것입니다. 이 시스템 내 시험에서 단어를 '대체'를 사용하는 용어에서 볼 수있는 우리는 항상 업데이 트에 각 행의 모든 ​​필드를 설정 동시 업데이트

에서 무료입니다

참고.

  1. 업데이트 할 행의 배열을 취하는 replace() 프로 시저를 작성 : 지금까지 우리의 업데이트 문제에 대한 두 가지 접근 방식을 시도했습니다

    CREATE OR REPLACE FUNCTION replace_item(data item[]) RETURNS VOID AS $$ 
    BEGIN 
        FOR i IN COALESCE(array_lower(data,1),0) .. COALESCE(array_upper(data,1),-1) LOOP 
         UPDATE item SET a0=data[i].a0,a1=data[i].a1,a2=data[i].a2 WHERE key=data[i].key; 
        END LOOP; 
    END; 
    $$ LANGUAGE plpgsql 
    
  2. insert_or_replace 규칙을 만듭니다 있도록 모든하지만, 가끔 삭제가된다 다중 행 삽입

    CREATE RULE "insert_or_replace" AS 
        ON INSERT TO "item" 
        WHERE EXISTS(SELECT 1 FROM item WHERE key=NEW.key) 
        DO INSTEAD 
         (UPDATE item SET a0=NEW.a0,a1=NEW.a1,a2=NEW.a2 WHERE key=NEW.key); 
    

목 모든 테스트는 데이터베이스와 동일한 컴퓨터에서 실행되는

  • : 테스트 실행에 대한

    Multi-row insert   : 50000 items inserted in 1.32 seconds averaging 37807.84 items/s 
    executemany() update  : 50000 items updated in 26.67 seconds averaging 1874.57 items/s 
    update_andres    : 50000 items updated in 3.84 seconds averaging 13028.51 items/s 
    update_merlin83 (i/d/i) : 50000 items updated in 1.29 seconds averaging 38780.46 items/s 
    update_merlin83 (i/u)  : 50000 items updated in 1.24 seconds averaging 40313.28 items/s 
    replace_item() procedure : 50000 items replaced in 3.10 seconds averaging 16151.42 items/s 
    Multi-row insert_or_replace: 50000 items inserted in 2.73 seconds averaging 18296.30 items/s 
    Multi-row insert_or_replace: 50000 items replaced in 2.02 seconds averaging 24729.94 items/s 
    

    랜덤 노트 : 후자의 속도가 느려 비트를 삽입하지만, 모두 업데이트 공정한 비트 속도 ESE 상주한다. localhost에 연결 중입니다.

  • 삽입 및 업데이트는 각각 500 건의 항목으로 데이터베이스에 적용되며 각 항목은 자체 트랜잭션 (업데이트)으로 전송됩니다.
  • 모든 업데이트/바꾸기 테스트는 이미 데이터베이스에있는 것과 동일한 값을 사용했습니다.
  • psycopg2 adapt() 기능을 사용하여 모든 데이터가 이스케이프되었습니다.
  • 모든 테이블을 절단하고 사용하기 전에 진공 청소기로 청소하다 (만 잘라내는 일이 이전 실행에서, 추가)
  • 표는 다음과 같습니다

    CREATE TABLE item (
        key MACADDR PRIMARY KEY, 
        a0 VARCHAR, 
        a1 VARCHAR, 
        a2 VARCHAR 
    ) 
    

그래서, 진짜 질문은 : 업데이트/교체 작업 속도를 조금 더 높일 수 있습니까? (나는이 결과가 '충분히 좋을 것'이라고 생각하지만, SO 군중을 두드리지 않고 포기하고 싶지는 않다.)

더 우아한 replace_item() 또는 anything을위한 anyones 힌트가있다. 깨진 것이 가장 환영받을 것입니다.

테스트 스크립트는 재현하려는 경우 here을 사용할 수 있습니다. 그래도 그것을 확인하는 것을 잊지 마십시오 ... 그것 WorkForMe,하지만 ...

당신은 DB를 편집해야합니다.설정에 맞게 connect() 행을 사용하십시오. 내가 단일 쿼리 업데이트와 또 다른 시험이는 Freenode @ #postgresql에서 안드레스에

편집

감사합니다; 위의 update_andres로 나열된 다중 행 삽입과 매우 비슷합니다.

UPDATE item 
SET a0=i.a0, a1=i.a1, a2=i.a2 
FROM (VALUES ('00:00:00:00:00:01', 'v0', 'v1', 'v2'), 
      ('00:00:00:00:00:02', 'v3', 'v4', 'v5'), 
      ... 
    ) AS i(key, a0, a1, a2) 
WHERE item.key=i.key::macaddr 

편집 내가 삽입 - 투 - 온도와 또 다른 검사를 아래는 Freenode 및 용기/JWP @ #postgresql에서 merlin83에

감사합니다/삭제/삽입 방법 (update_merlin83 "로 표시 (I/d/i) ").

INSERT INTO temp_item (key, a0, a1, a2) 
    VALUES (
     ('00:00:00:00:00:01', 'v0', 'v1', 'v2'), 
     ('00:00:00:00:00:02', 'v3', 'v4', 'v5'), 
     ...); 

DELETE FROM item 
USING temp_item 
WHERE item.key=temp_item.key; 

INSERT INTO item (key, a0, a1, a2) 
    SELECT key, a0, a1, a2 
    FROM temp_item; 

내 직감이 시험은 실제 시나리오에서 성능에 매우 대표하지 않습니다,하지만 난 차이가 추가 조사를 위해 가장 유망한 접근법의 표시를 줄만큼 훌륭한 생각이다. perftest.py 스크립트에는 체크 아웃하려는 사용자를위한 모든 업데이트가 포함되어 있습니다. 너무 Freenode의 내가 삽입 - 투 - 온도/업데이트 변형으로 테스트해야한다는 지적 @ (고글 : #postgresql에서

편집

안드레스을 잊지으로 나열되지 않습니다,하지만 매우 추한 "update_merlin83 (i/u)"위).

INSERT INTO temp_item (key, a0, a1, a2) 
    VALUES (
     ('00:00:00:00:00:01', 'v0', 'v1', 'v2'), 
     ('00:00:00:00:00:02', 'v3', 'v4', 'v5'), 
     ...); 

UPDATE item 
SET a0=temp_item.a0, a1=temp_item.a1, a2=temp_item.a2 
FROM temp_item 
WHERE item.key=temp_item.key 

아마 최종 편집 편집 : 는 더 나은 우리의 부하 시나리오에 맞게 내 스크립트를 변경, 그리고 약간의 물건을 확장하고 약간의 임의성을 추가 할 때 번호도 보유 보인다. 누군가 다른 시나리오에서 매우 다른 숫자를 얻는다면 그것에 대해 알고 싶어합니다.

+0

도움이 될? 외국 열쇠? –

+0

테스트 스크립트에는 없습니다. 현실 세계에서, 하나. –

+0

'UPDATE'의'EXPLAIN ANALYZE'를 게시 할 수 있습니까? 나는 평가자가 일어날 것이라고 생각하는 것을 알고 싶다. – Sean

답변

1

insert_or_replace. 이 시도 :

아마 아무것도 할 것이다
WHERE EXISTS(SELECT 1 FROM item WHERE key=NEW.key LIMIT 1) 

대신

의견에서 언급 한 바와 같이
WHERE EXISTS(SELECT 1 FROM item WHERE key=NEW.key) 

. 추가해야 할 것은 인덱스를 제거하여 INSERT/UPDATE 성능을 항상 빠르게 할 수 있다는 것입니다. 테이블이 오버 인덱싱 된 것을 발견하지 않으면이 작업을 수행하고 싶지 않을 것입니다.하지만 적어도 체크 아웃해야합니다.

+0

아마 불필요하다. - 문서 (http://www.postgresql.org/docs/current/static/functions)에서 발췌 한 것이다. -subquery.html # AEN15270) : "하위 쿼리는 일반적으로 완료 될 때까지가 아니라 적어도 하나의 행이 반환되는지 여부를 판단 할 수있을 정도로만 실행됩니다. –

+0

아, 고마워. 실재 현존하는 방법을 몰랐습니다. 이제 나야. :) – chaos

+0

키가 고유하므로 한 행만 반환합니다. 그럼에도 불구하고, 나는 노력했고, 성능면에서 눈에 띄는 변화가 없었다. 그래도 고마워! –

1

오라클에서는 테이블을 잠그면 분명 도움이 될 것입니다. PostgreSQL을 사용해 볼 수도 있습니다.

+0

모든 트랜잭션에서 잠긴 테이블로 모든 테스트를 실행 해 보았습니다. 변경 없음. –

2

몇 달 전에 비슷한 상황이 발생하여 튜닝 된 청크/트랜잭션 크기에서 최대 속도 향상을 얻었습니다. 테스트 중에 로그에 검사 점 경고가 있는지 확인하고 적절하게 조정할 수도 있습니다.

+0

체크 포인트 경고를 확실히 찾을 것입니다. 매우 적합합니다. 감사! –

2

UPS에서 WAL (Write Ahead Logging)을 사용하여 디스크 쓰기 사이에 업데이트를 캐시하는 것이 좋습니다.

wal_buffers 이 설정은 WAL (Write Write Log)에있을 수있는 버퍼 수를 결정합니다. 데이터베이스에 많은 쓰기 트랜잭션이있는 경우이 값을 기본값보다 약간 크게 설정하면 디스크 공간을보다 효율적으로 사용할 수 있습니다. 실험하고 결정하십시오.좋은 시작은 256-512K 메모리에 해당하는 32-64 정도입니다.

http://www.varlena.com/GeneralBits/Tidbits/perf.html

4

내가 페이지에서이 일을 일반적인 방법은 다음과 같습니다 임시 테이블 복사본을 사용 (아무 제약), 병합 (재미 부분), 이익에 목표 테이블을 일치하는 원시 데이터를로드합니다.

http://mbk.projects.postgresql.org/

워드 프로세서는 정말 친절하지,하지만 난 그것을 좋은 봐주는 게 좋을 것 :

나는 이러한 상황을 위해 특별히 merge_by_key 기능을 썼다.

+0

일반적인 프로세스의 요점은 다음과 같습니다 : 네트워크 왕복 비용을 피하기 위해로드 된 각 행에 대해 여러 커서 (포털) 생성을 피하기 위해 임시로로드하십시오 (예, executemany는 빠르지 만 COPY wtf-pwns-it 효율성에 wrt). 삽입 명령의 의미를 변경하는 규칙/트리거를 작성하지 않으려면 병합 기능/프로세스를 사용하십시오. 나는 그것을 두 가지 방법으로 모두 해왔으며 명시 적이기 때문에 항상 병합 프로세스를 선호했습니다. 병합 프로세스가 충분히 효율적이지 않은 경우 인덱스 일시 중지 (레크리에이션)/파티션 또는 http://pgfoundry.org/projects/pgbulkload/를 확인해야합니다. – jwp

관련 문제