SQLite 동시 액세스


177

SQLite3는 동일한 DB에서 여러 프로세스를 읽고 쓰는 동시 액세스를 안전하게 처리합니까? 그것에 대한 플랫폼 예외가 있습니까?


3
현상금 목표 를 언급하는 것을 잊어 버렸습니다 . 대부분의 대답은 괜찮습니다 : "SQLite는 충분히 빠릅니다", "SQLite는 동시성을 잘 처리합니다". 정확히 같은 시간에 도착합니다 (이론적 매우 드문 경우). 1) 오류가 발생하여 프로그램이 중단됩니까? 또는 2) 두 번째 쓰기 작업이 첫 번째 작업이 완료 될 때까지 대기합니까? 또는 3) 쓰기 작업 중 하나가 삭제됩니까 (데이터 손실!)? 4) 다른 것? 동시 쓰기의 한계를 아는 것은 많은 상황에서 유용 할 수 있습니다.
Basj

7
@Basj 간단히 말하면, 2) 기다렸다가 여러 번 재 시도합니다 (구성 가능), 1) 오류 발생, SQLITE_BUSY.3) SQLITE_BUSY 오류를 처리하기 위해 콜백을 등록 할 수 있습니다.
obgnaw

답변:


112

이러한 동시 액세스의 대부분이 읽기 (예 : SELECT)이면 SQLite가이를 잘 처리 할 수 ​​있습니다. 그러나 동시에 쓰기를 시작하면 잠금 경합이 문제가 될 수 있습니다. SQLite 엔진 자체가 매우 빠르며 경합을 최소화하기 위해 많은 영리한 최적화가 있기 때문에 파일 시스템의 속도에 따라 많은 것이 좌우됩니다. 특히 SQLite 3.

대부분의 데스크톱 / 노트북 / 태블릿 / 전화 응용 프로그램의 경우 동시성이 충분하지 않으므로 SQLite가 빠릅니다. (Firefox는 책갈피, 기록 등에 SQLite를 광범위하게 사용합니다.)

서버 응용 프로그램의 경우, 어떤 사람은 하루 전에 100K 미만의 페이지를 일반적인 시나리오 (예 : 블로그, 포럼)에서 SQLite 데이터베이스로 완벽하게 처리 할 수 ​​있다고 말했지만 그 반대 증거는 아직 보지 못했습니다. 실제로 최신 디스크와 프로세서를 사용하면 웹 사이트와 웹 서비스의 95 %가 SQLite와 잘 작동합니다.

정말로 빠른 읽기 / 쓰기 액세스를 원한다면 메모리 내 SQLite 데이터베이스를 사용하십시오 . RAM은 디스크보다 몇 배 빠릅니다.


16
OP는 효율성과 속도가 아니라 동시 액세스에 대해 묻습니다. 웹 서버는 이와 관련이 없습니다. 메모리 데이터베이스에서 동일합니다.
Jarekczek

1
당신은 어느 정도 옳지 만 효율성 / 속도가 중요한 역할을합니다. 빠른 액세스는 잠금을 기다리는 데 소요되는 시간이 줄어 SQLite의 동시성 성능의 단점을 줄입니다. 특히, 쓰기가 적고 빠를 경우 DB는 사용자에게 동시성 문제가 전혀없는 것 같습니다.
Caboose September

1
인 메모리 sqlite 데이터베이스에 대한 동시 액세스를 어떻게 관리 하시겠습니까?
P-Gn

42

예, SQLite는 동시성을 잘 처리하지만 성능 측면에서 최고는 아닙니다. 내가 알 수있는 것에서 예외는 없습니다. 자세한 내용은 SQLite 사이트 ( https://www.sqlite.org/lockingv3.html)에 있습니다.

이 설명은 흥미 롭다 : "pager 모듈은 한 번에 모든 변경이 이루어 지도록하거나, 모든 변경이 발생하거나 전혀 수행되지 않도록하며, 둘 이상의 프로세스가 동시에 호환되지 않는 방식으로 데이터베이스에 액세스하려고하지 않도록합니다"


2
다음은 서로 다른 플랫폼 , 즉 NFS 파일 시스템 및 Windows의 문제에 대한 의견입니다 (이전 버전의 Windows에만 해당 될 수 있지만 ...)
Nate

1
PHP에서 모든 사용자가 사용할 수 있도록 SQLite3 데이터베이스를 RAM에로드 할 수 있습니까? 나는 그것이 절차적인 것이 아니라고 추측하고있다
Anon343224user

1
@foxyfennec .. 시작점이지만 SQLite는이 사용 사례에 최적의 db가 아닐 수도 있습니다. sqlite.org/inmemorydb.html
kingPuppy

37

그렇습니다. 이유를 알아 봅시다

SQLite는 트랜잭션입니다

SQLite의 단일 트랜잭션 내의 모든 변경 사항은 완전히 발생하거나 전혀 발생하지 않습니다.

동시 읽기 / 쓰기뿐만 아니라 이러한 ACID 지원은 두 가지 방식으로 제공됩니다. 이른바 저널링 ( " 이전 방식 "이라고 함) 또는 미리 쓰기 로깅 ( " 새로운 방식 "이라고 함)

저널링 (오래된 방식)

이 모드에서 SQLite는 DATABASE-LEVEL 잠금을 사용 합니다. 이것이 이해해야 할 중요한 포인트입니다.

즉, 무언가를 읽고 쓸 때마다 먼저 ENTIRE 데이터베이스 파일 에 대한 잠금을 획득 합니다. 여러 독자가 동시에 공존하고 무언가를 읽을 수 있음

쓰기 중에는 독점 잠금을 확보하고 다른 프로세스가 동시에 읽기 / 쓰기를 하지 않으므로 쓰기가 안전합니다.

이것은 왜 여기 가 SQLite는 구현 말을하는지 직렬화 거래

근심거리

매번 전체 데이터베이스를 잠글 필요가 있고 쓰기 동시성이 처리되는 프로세스를 기다리는 모든 사람이 대기하기 때문에 동시 쓰기 / 읽기의 성능이 상당히 낮습니다.

롤백 / 정지

데이터베이스 파일에 무언가를 쓰기 전에 SQLite는 먼저 임시 파일에서 변경되도록 청크를 저장합니다. 데이터베이스 파일에 쓰는 중에 충돌이 발생하면이 임시 파일을 가져 와서 변경 사항을 되돌립니다.

미리 쓰기 로깅 또는 WAL (새로운 방법)

이 경우 모든 쓰기는 임시 파일 ( write-ahead log )에 추가되고이 파일은 원래 데이터베이스와 주기적으로 병합됩니다. SQLite가 무언가를 검색 할 때 먼저이 임시 파일을 확인하고 아무것도 발견되지 않으면 주 데이터베이스 파일로 진행하십시오.

결과적으로 독자는 작가와 경쟁하지 않으며 올드 웨이에 비해 성능이 훨씬 좋습니다.

경고

SQlite는 기본 파일 시스템 잠금 기능에 크게 의존하므로주의해서 사용해야 합니다.

특히 저널링 모드에서 데이터베이스 잠금 오류가 발생 했을 가능성이 높 으므로이 오류를 염두에두고 앱을 설계해야합니다.


34

아무도 WAL (Write Ahead Log) 모드를 언급하지 않은 것 같습니다. 트랜잭션이 올바르게 구성되고 WAL 모드가 설정된 상태에서 업데이트가 진행되는 동안 사람들이 내용을 읽는 동안 데이터베이스를 잠글 필요는 없습니다.

유일한 문제는 어떤 시점에서 WAL을 주 데이터베이스에 다시 통합해야한다는 것입니다. 데이터베이스에 대한 마지막 연결이 닫힐 때이 작업이 수행됩니다. 사용량이 많은 사이트를 사용하면 모든 연결이 닫히는 데 몇 초가 걸리지 만 하루에 100K의 조회수는 문제가되지 않습니다.


흥미롭지 만 단일 네트워크에서만 작동하지만 네트워크에서 데이터베이스에 액세스하는 시나리오에서는 작동하지 않습니다.
Bobík

작가가 기다리는 기본 시간 제한은 5 초이며, 그 후 database is locked작가에 의해 오류가 발생합니다
mirhossein

16

2019 년에는 아직 출시되지 않았지만 별도의 지점에서 사용할 수있는 두 가지 새로운 동시 쓰기 옵션이 있습니다.

"PRAGMA journal_mode = wal2"

일반 "wal"모드에 비해이 저널 모드의 장점은 작성자가 한 wal 파일에 계속 쓰는 반면 다른 wal 파일은 체크 포인트됩니다.

시작 동시 -상세 문서 링크

BEGIN CONCURRENT 향상 기능은 시스템이 여전히 COMMIT 명령을 직렬화하더라도 데이터베이스가 "wal"또는 "wal2"모드 인 경우 여러 작성자가 쓰기 트랜잭션을 동시에 처리 할 수 ​​있도록합니다.

"BEGIN CONCURRENT"로 쓰기 트랜잭션을 열면 실제로 데이터베이스 잠금은 COMMIT가 실행될 때까지 지연됩니다. 이는 BEGIN CONCURRENT로 시작된 많은 수의 트랜잭션이 동시에 진행될 수 있음을 의미합니다. 시스템은 낙관적 인 페이지 레벨 잠금을 사용하여 충돌하는 동시 트랜잭션이 커미트되지 않도록합니다.

그것들은 begin-concurrent-wal2 또는 각각 별도의 브랜치에 있습니다.


1
이러한 기능이 언제 릴리스 버전에 적용되는지 알 수 있습니까? 그들은 정말 나를 위해 편리하게 올 수 있습니다.
피터 무어

2
몰라. 가지에서 쉽게 지을 수 있습니다. .NET의 경우 저수준 인터페이스 및 WAL2 + 동시 시작 + FTS5가있는 라이브러리가 있습니다 : github.com/Spreads/Spreads.SQLite
VB

오, 고마워 안정성에 대해 더 궁금합니다. SQLite는 릴리스와 관련하여 최고 수준이지만 프로덕션 코드에서 분기를 사용하는 것이 얼마나 위험한지 모르겠습니다.
피터 무어

2
이 스레드 github.com/Expensify/Bedrock/issues/65 및 Bedrock을 참조하십시오 . 그들은 생산에 그것을 사용하고 그 begin concurrent물건을 밀어 넣었 습니다.
VB

13

SQLite에는 데이터베이스 수준에 대한 독자-작성기 잠금이 있습니다. 여러 프로세스 (다른 프로세스가 소유하고있을 수 있음)는 동일한 데이터베이스에서 동시에 데이터를 읽을 수 있지만 하나만 데이터베이스에 쓸 수 있습니다.

SQLite는 무제한의 동시 판독기를 지원하지만 한 번에 한 명의 작성자 만 허용합니다. 많은 상황에서 이것은 문제가되지 않습니다. 라이터 큐업 각 응용 프로그램은 데이터베이스가 빠르게 작동하고 계속 진행하며 수십 밀리 초 이상 동안 잠금이 지속되지 않습니다. 그러나 더 많은 동시성이 필요한 일부 응용 프로그램이 있으며 해당 응용 프로그램은 다른 솔루션을 찾아야 할 수도 있습니다. - SQLite는 @ SQLite.org에 적합한 용도

독자-작성기 잠금은 독립적 인 트랜잭션 처리를 가능하게하며 데이터베이스 레벨에서 독점 및 공유 잠금을 사용하여 구현됩니다.

연결이 데이터베이스에서 쓰기 조작을 수행하기 전에 독점 잠금을 확보해야합니다. 단독 잠금이 확보되면 잠금이 다시 해제 될 때까지 다른 연결의 읽기 및 쓰기 작업이 모두 차단됩니다.

동시 쓰기의 경우 구현 세부 사항

SQLite에는 쓰기 작업 중에 가능한 한 늦게 데이터베이스를 잠그는 데 도움이되는 잠금 테이블이있어 최대의 동시성을 보장합니다.

초기 상태는 UNLOCKED이며이 상태에서는 연결이 데이터베이스에 아직 액세스하지 않았습니다. 프로세스가 데이터베이스에 연결되어 있고 트랜잭션이 BEGIN으로 시작된 경우에도 연결은 여전히 ​​잠금 해제 상태입니다.

UNLOCKED 상태 후 다음 상태는 SHARED 상태입니다. 데이터베이스에서 데이터를 읽거나 쓰지 않으려면 먼저 SHARED 잠금을 가져 와서 연결이 SHARED 상태가되어야합니다. 여러 연결이 동시에 SHARED 잠금을 확보하고 유지할 수 있으므로 여러 연결이 동일한 데이터베이스에서 동시에 데이터를 읽을 수 있습니다. 그러나 하나의 SHARED 잠금 만 해제되어 있으면 연결이 데이터베이스에 대한 쓰기를 완료 할 수 없습니다.

연결이 데이터베이스에 쓰려면 먼저 예약 된 잠금을 가져와야합니다.

한 번에 하나의 RESERVED 잠금 만 활성화 될 수 있지만 여러 개의 SHARED 잠금이 단일 RESERVED 잠금과 공존 할 수 있습니다. 예약 된 잠금이있는 동안 새 공유 잠금을 획득 할 수 있다는 점에서 예약 됨과 보류 중이 다릅니다. - 파일 잠금과 동시성에서 SQLite는 버전 3 @ SQLite.org

연결이 RESERVED 잠금을 확보하면 데이터베이스 수정 조작 처리를 시작할 수 있지만 이러한 수정은 실제로 디스크에 기록되지 않고 버퍼에서만 수행 될 수 있습니다. 판독 내용에 대한 수정은 메모리 버퍼에 저장됩니다. 연결이 수정 (또는 트랜잭션)을 제출하려는 경우 예약 된 잠금을 독점 잠금으로 업그레이드해야합니다. 자물쇠를 얻으려면 먼저 자물쇠를 PENDING 자물쇠로 들어야합니다.

PENDING 잠금은 잠금을 보유한 프로세스가 가능한 빨리 데이터베이스에 쓰려고하고 독점 잠금을 얻을 수 있도록 현재 공유 된 모든 잠금을 지울 때까지 대기하고 있음을 의미합니다. PENDING 잠금이 활성화 된 경우 기존 SHARED 잠금을 계속할 수 있지만 데이터베이스에 대해 새로운 SHARED 잠금이 허용되지 않습니다.

데이터베이스 파일에 쓰려면 독점 잠금이 필요합니다. 파일에는 하나의 EXCLUSIVE 잠금 만 허용되며 어떤 종류의 다른 잠금도 EXCLUSIVE 잠금과 공존 할 수 없습니다. 동시성을 최대화하기 위해 SQLite는 독점 잠금이 유지되는 시간을 최소화합니다. - 파일 잠금과 동시성에서 SQLite는 버전 3 @ SQLite.org

따라서 SQLite는 단순히 지원하지 않기 때문에 동일한 DB에 쓰는 여러 프로세스의 동시 액세스를 안전하게 처리한다고 말할 수 있습니다! 재시도 제한에 도달 하면 두 번째 작성자를 얻 SQLITE_BUSY거나 받게됩니다 SQLITE_LOCKED.


감사합니다. 2 명의 작성자가있는 코드의 예는 작동 방식을 이해하는 것이 좋습니다.
Basj

1
간단히 말해 @Basj, sqlite는 데이터베이스 파일에 대해 읽기-쓰기 잠금을 가지고 있으며, 파일을 동시에 쓰는 것과 같습니다. WAL을 사용하면 여전히 동시 쓰기가 불가능하지만 WAL은 쓰기 속도를 높이고 읽기와 쓰기를 동시에 수행 할 수 있으며 WAL은 WAL_READ_LOCK, WAL_WRITE_LOCK과 같은 새로운 잠금을 도입합니다.
obgnaw

2
큐를 사용하고 여러 스레드가 큐에 피드하고 큐의 SQL 문을 사용하여 하나의 스레드가 DB에 쓰도록 할 수 있습니까? 이와 같은
Gabriel

7

이 스레드는 오래되었지만 sqlite에서 수행 한 테스트 결과를 공유하는 것이 좋을 것이라고 생각합니다 .EXCLUSIVE 잠금 및 시간 초과가 설정된 트랜잭션 내에서 SELECT 및 UPDATE sql 명령을 실행하는 2 개의 Python 프로그램 인스턴스 (다른 프로세스가 동일한 프로그램)를 실행했습니다. 10 초 동안 잠금을 설정하면 결과가 실망 스러웠습니다. 모든 인스턴스는 10000 단계 루프에서 수행했습니다.

  • 독점 잠금으로 DB에 연결
  • 카운터를 읽으려면 한 행을 선택하십시오
  • 카운터가 1 씩 증가하는 것과 같은 새 값으로 행을 업데이트하십시오.
  • db와의 밀접한 연결

sqlite가 트랜잭션에 독점 잠금을 부여하더라도 실제로 실행 된 총주기 수는 20 000과 같지 않았지만 두 프로세스에 대해 계산 된 단일 카운터에 대한 총 반복 횟수입니다. 파이썬 프로그램은 거의 단일 예외를 던지지 않았습니다 (20 번 실행을 선택하는 동안 한 번만). 테스트 시점의 sqlite 개정은 3.6.20 및 python v3.3 CentOS 6.5입니다. 내 의견으로는 이러한 종류의 작업에 대해보다 안정적인 제품을 찾거나 sqlite에 대한 쓰기를 단일 고유 프로세스 / 스레드로 제한하는 것이 좋습니다.


5
: 여기에서 논의 된대로, 파이썬에서 잠금을 얻기 위해 마법의 말을해야 할 것 같습니다 stackoverflow.com/a/12848059/1048959 이를 파이썬 SQLite는 설명서를 리드 당신이이 믿을 수 있다는 사실에도 불구하고 with con충분하다.
Dan Stahlke
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.