2012-08-17 4 views
4

sqlite 데이터베이스로 작업하는 웹 응용 프로그램이 있습니다. 내 버전의 sqlite는 공식 Windows 바이너리 배포판 - 3.7.13의 최신판입니다.sqlite는 WAL 모드에서 SQLITE_BUSY를 반환합니다.

문제는 데이터베이스에 과부하가 걸리면 sqlite API 함수 (예 : sqlite3_step)가 SQLITE_BUSY를 반환한다는 것입니다. 연결을 초기화 할 때

나는 다음 pragma를 전달합니다

journal_mode = WAL 
page_size = 4096 
synchronous = FULL 
foreign_keys = on 

databas 한 파일 데이터베이스입니다. Mono 2.10.8 및 Mono.Data.Sqlite 어셈블리를 사용하여 데이터베이스에 액세스합니다.

각 응용 프로그램에 50 개의 후속 http 요청을 보내는 50 개의 병렬 스레드로 테스트하고 있습니다. 모든 요청에 ​​대해 일부 읽기 및 쓰기가 데이터베이스에 수행됩니다. 모든 IO 작업 세트는 트랜잭션 내에서 실행됩니다.

400 번째 - 700 번째 요청까지 모든 것이 잘 진행됩니다. 이 (임의의) 순간에 API 함수는 SQLITE_BUSY를 영구적으로 반환하기 시작합니다 (정확히 말하면 다시 시도 한계에 도달 할 때까지).

내가 아는 한 WAL 모드는 병렬 읽기 및 쓰기를 투명하게 지원합니다. 체크 포인트 작업이 실행되는 동안 데이터베이스를 읽으려고 시도했기 때문일 수 있습니다. 그러나 자동 점검 점을 끈 후에도 상황은 동일하게 유지됩니다.

이 상황에서 잘못되었을 수있는 것은 무엇입니까? 대량의 병렬 데이터베이스 IO를 올바르게 제공하는 방법은 무엇입니까?

P.

요청 당 하나의 연결 만 가정합니다. WebSessionContext로 구성된 nhibernate를 사용합니다.

내가 이렇게 내 NHibernate에 세션을 초기화 :

ISession session = null; 

//factory variable is session factory 
if (CurrentSessionContext.HasBind(factory)) 
{ 
    session = factory.GetCurrentSession(); 
    if (session == null) 
    CurrentSessionContext.Unbind(factory); 
} 

if (session == null) 
{ 
    session = factory.OpenSession(); 
    CurrentSessionContext.Bind(session); 
} 

return session; 

그리고 HttpApplication.EndRequest을에

내가 이런 식으로 해제 :

//factory variable is session factory 
if (CurrentSessionContext.HasBind(factory)) 
{ 
    try 
    { 
    CurrentSessionContext.Unbind(factory) 
     .Dispose(); 
    } 
    catch (Exception ee) 
    { 
    Logr.Error("Error uninitializing session", ee); 
    } 
} 

를 지금까지 내가 아는 한 당 하나 개의 연결 만이 있어야한다 라이프 사이클을 요청하십시오. 요청을 처리하는 동안 코드는 순차적으로 실행됩니다 (ASP.NET MVC 3). 그래서 어떤 concurency도 여기에서 가능하지 않은 것처럼 보입니다. 이 경우 어떤 연결도 공유되지 않는다고 결론 내릴 수 있습니까?

+0

는 별개의 연결은 각 요청에 대해 열, 아니면 모든 요청이 동일한 연결을 공유 할 수 있습니까? 또한 관련 코드 비트, 특히 스레드가 데이터베이스와 상호 작용하는 부분을 추가 할 수 있습니까? –

+2

WAL 모드에 대한 가정이 잘못되었습니다. http://www.sqlite.org/wal.html에서 "WAL 파일이 하나뿐이므로 한 번에 하나의 작성자 만있을 수 있습니다." 쓰기로드가 많은 데이터베이스의 경우 PostgreSQL 또는 MySQL과 같은 것을 사용하십시오. –

답변

3

요청 스레드가 동일한 연결을 공유하는지 여부는 확실하지 않습니다. 그들이하지 않으면 당신은 이러한 문제가 발생해서는 안됩니다.

실제로 여러 스레드에서 연결 개체를 공유한다고 가정하면 SqliteConnection isn't thread-safe (이전 게시물이지만 System.Data.SQLite에서 진화 된 Mono의 일부로 유지 관리되는 SQLite 라이브러리로 잠금 메커니즘을 사용해야합니다. http://sqlite.phxsoftware.com).

그래서 SqliteConnection 개체를 사용하여 잠그지 않는다고 가정하면 시도해 볼 수 있습니까?이 작업을 수행하는 간단한 방법은 다음과 같을 수 있습니다 :

static readonly object _locker = new object(); 

public void ProcessRequest() 
{ 
    lock (_locker) { 
     using (IDbCommand dbcmd = conn.CreateCommand()) { 
      string sql = "INSERT INTO foo VALUES ('bar')"; 
      dbcmd.CommandText = sql; 
      dbcmd.ExecuteNonQuery(); 
     } 
    } 
} 

당신은 그러나 당신이 SQLite는 라이브러리와 더 이상 스레딩 문제가되지 않도록하기 위해 각 스레드와 별개의 연결을 열 수도 있습니다.

편집

당신이 게시 코드에 접속 후, 당신은 트랜잭션을 커밋 후 세션을 닫습니다합니까? 일부 IT 트랜잭션을 사용하지 않는 경우 세션을 플러시하고 닫으시겠습니까? 내가 코드에 표시되지 않기 때문에 부탁 해요, 그리고 그것이 https://stackoverflow.com/a/43567/610650

에서 언급 볼 나는 또한 http://nhibernate.info/doc/nh/en/index.html#session-configuration에 언급 참조 :

은 또한 당신이 NHibernateHelper.GetCurrentSession을 (호출 할 수 있습니다); 여러 번 원하는대로, 당신은 항상이 HTTP 요청 의 현재 세션을 얻을 것이다. 당신은 확실히 ISession이 응답이 전송되는 HTTP 전에 응용 프로그램 클래스의 Application_EndRequest 이벤트 핸들러 또는 HttpModule을에서 중, 당신의 단위 작업이 완료된 후에 닫혀 있는지 확인해야합니다.

+0

답변 해 주셔서 감사합니다. 귀하의 질문에 답변하기 위해 몇 가지 정보를 추가했습니다. 제발 좀 봐 – ILya

+0

@ILya 내 대답을 추가했습니다. –

+0

나는 정확히 nhforge에서 인용 한 것과 같습니다. 데이터베이스와의 모든 대화는 작업 단위 (UOW)로 포장됩니다. 즉, 모든 쿼리 블록이 트랜잭션에서 실행되며 트랜잭션없이 쿼리가 실행되지 않습니다. 나의 세션은 EndRequest에서 공개됩니다. CL로. SQLite 문서에 따르면 WAL 모드에서는 동시 쓰기가 불가능합니다. 그래서 나는 너에게 현상금을 줄 것이다. 그러나 독점 잠금이없는 SQLite로 동시 쓰기를 수행하는 방법을 알고 있다면이 지식을 확인하십시오. – ILya