programing tip

SQLite 동시 액세스

itbloger 2020. 6. 4. 19:15
반응형

SQLite 동시 액세스


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


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

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

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

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


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

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


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

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


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

SQLite는 트랜잭션입니다

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

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

저널링 (오래된 방법)

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

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

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

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

근심거리

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

롤백 / 정지

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

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

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

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

경고

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

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


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

"PRAGMA journal_mode = wal2"

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

시작 동시 -세부 문서로 연결

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

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

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


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

DOC에서는 명확합니다.

트랜잭션 처리에서 SQLite는 데이터베이스 수준에서 독점 및 공유 잠금을 통해 독립적 인 트랜잭션 처리를 구현합니다. 이것이 여러 프로세스가 동일한 데이터베이스에서 동시에 데이터를 읽을 수 있지만 데이터베이스에는 하나만 쓸 수있는 이유입니다.

An exclusive lock must be obtained before a process or thread wants to perform a write operation on a database. After the exclusive lock is obtained, other read or write operations will not occur again.

Implementation details for the case of two writings :

SQLite has a lock table to help different database writing attempts to lock it, and that is made at the last moment to ensure maximum concurrency.

The initial state is "UNLOCKED", and in this state, the connection has not accessed the database yet. When a process is connected to a database and even a transaction has been started with BEGIN, the connection is still in "UNLOCKED" state.

The next state of the unlocked state is a SHARED state. In order to be able to read (not write) data from the database, the connection must first enter the SHARED state, that is to say, first to get a SHARED lock. Multiple connections can obtain and maintain SHARED locks at the same time, that is, multiple connections can read data from the same database at the same time. But even if no SHARED lock has been released, it does not allow any connection to write a database.

If a connection wants to write a database, it must first get a RESERVED lock.

Only a single RESERVED lock may be active at one time, though multiple SHARED locks can coexist with a single RESERVED lock. RESERVED differs from PENDING in that new SHARED locks can be acquired while there is a RESERVED lock.

Once a connection obtains a RESERVED lock, it can start processing database modification operations, though these modifications can only be done in the buffer, rather than actually written to disk. The modifications made to the readout content are saved in the memory buffer. When a connection wants to submit a modification (or transaction), it is necessary to upgrade the reserved lock to an exclusive lock. In order to get the lock, you must first lift the lock to a pending lock.

A PENDING lock means that the process holding the lock wants to write to the database as soon as possible and is just waiting on all current SHARED locks to clear so that it can get an EXCLUSIVE lock. No new SHARED locks are permitted against the database if a PENDING lock is active, though existing SHARED locks are allowed to continue.

An EXCLUSIVE lock is needed in order to write to the database file. Only one EXCLUSIVE lock is allowed on the file and no other locks of any kind are allowed to coexist with an EXCLUSIVE lock. In order to maximize concurrency, SQLite works to minimize the amount of time that EXCLUSIVE locks are held.

So you might say SQLite safely handles concurrent access by multiple processes writing to the same db simply because it doesn't support it ! You will get SQLITE_BUSYor SQLITE_LOCKED for the second writer when it hits the retry limitation.


This thread is old but i think it would be good to share result of my tests done on sqlite: i ran 2 instances of python program (different processes same program) executing statements SELECT and UPDATE sql commands within transaction with EXCLUSIVE lock and timeout set to 10 seconds to get a lock, and result were frustrating. Every instance did in 10000 step loop:

  • connect to db with exclusive lock
  • select on one row to read counter
  • update the row with new value equal to counter incremented by 1
  • close connection to db

Even if sqlite granted exclusive lock on transaction, the total number of really executed cycles were not equal to 20 000 but less (total number of iterations over single counter counted for both processes). Python program almost did not throw any single exception (only once during select for 20 executions). sqlite revision at moment of test was 3.6.20 and python v3.3 CentOS 6.5. In mine opinion it is better to find more reliable product for this kind of job or restrict writes to sqlite to single unique process/thread.

참고URL : https://stackoverflow.com/questions/4060772/sqlite-concurrent-access

반응형