2011-11-22 2 views
4

큰 DB 파일 (> 600 Mb)에서 작동하는 다중 스레드 응용 프로그램이 있습니다. BLOB 데이터를 추가하면 "데이터베이스 잠김"문제가 발생하고 요청 당 30KB 이상의 BLOB 데이터로 작업이 시작됩니다. 나는 작은 HDD 속도와 관련된 문제라고 생각한다. 그것은 SQLite 삭제 - 저널 파일, 내 응용 프로그램의 한 스레드가 잠겨있어 (저널 파일을 적용하고 삭제 된 때문에), 그리고 다른 내 스레드가 DB를 사용하여 smth 싶지 않지만 SQLite 여전히 DB 파일을 업데이 트 ... 물론 , 나는 각 DB 호출 후에 잠깐 지연을 할 수 있지만, 더 많은 속도가 필요하기 때문에 이것은 해결책이 아닙니다.다중 스레드 응용 프로그램에서 SQLite "데이터베이스 잠김"오류가 발생했습니다.

이제 대화 당 (세션 당) 세션을 사용합니다. 따라서 응용 프로그램 개체 당 하나의 ISessionFactory와 많은 ISession 개체가 있습니다.

public abstract class nHibernateHelper 
{ 
    private static FluentConfiguration _configuration; 
    private static IPersistenceContext _persistenceContext; 

    static nHibernateHelper() {} 

    private static FluentConfiguration ConfigurePersistenceLayer() 
    { 
     return Fluently.Configure().Database(FluentNHibernate.Cfg.Db.SQLiteConfiguration.Standard.ShowSql().UsingFile(_fileName).IsolationLevel(IsolationLevel.Serializable).MaxFetchDepth(2)). 
       Mappings(m => m.FluentMappings.AddFromAssemblyOf<Foo>()).CurrentSessionContext(typeof(ThreadStaticSessionContext).FullName); 
    } 

    public static ISession CurrentSession 
    { 
     get { return _persistenceContext.CurrentSession; } 
    } 

    public static IDisposable OpenConnection() 
    { 
     return new DbSession(_persistenceContext); 
    } 
} 

public class PersistenceContext : IPersistenceContext, IDisposable 
{ 
    private readonly FluentConfiguration _configuration; 
    private readonly ISessionFactory _sessionFactory; 

    public PersistenceContext(FluentConfiguration configuration) 
    { 
     _configuration = configuration; 
     _sessionFactory = _configuration.BuildSessionFactory(); 
    } 

    public FluentConfiguration Configuration { get { return _configuration; } } 
    public ISessionFactory SessionFactory { get { return _sessionFactory; } } 

    public ISession CurrentSession 
    { 
     get 
     { 
      if (!CurrentSessionContext.HasBind(SessionFactory)) 
      { 
       OnContextualSessionIsNotFound(); 
      } 
      var contextualSession = SessionFactory.GetCurrentSession(); 
      if (contextualSession == null) 
      { 
       OnContextualSessionIsNotFound(); 
      } 
      return contextualSession; 
     } 
    } 

    public void Dispose() 
    { 
     SessionFactory.Dispose(); 
    } 

    private static void OnContextualSessionIsNotFound() 
    { 
     throw new InvalidOperationException("Ambient instance of contextual session is not found. Open the db session before."); 
    } 

} 

public class DbSession : IDisposable 
{ 
    private readonly ISessionFactory _sessionFactory; 

    public DbSession(IPersistenceContext persistentContext) 
    { 
     _sessionFactory = persistentContext.SessionFactory; 
     CurrentSessionContext.Bind(_sessionFactory.OpenSession()); 
    } 

    public void Dispose() 
    { 
     var session = CurrentSessionContext.Unbind(_sessionFactory); 
     if (session != null && session.IsOpen) 
     { 
      try 
      { 
       if (session.Transaction != null && session.Transaction.IsActive) 
       { 
        session.Transaction.Rollback(); 
       } 
      } 
      finally 
      { 
       session.Dispose(); 
      } 
     } 
    } 
} 

그리고 저장소 헬퍼 클래스가있다 :

은 (당신이 볼 수 있듯이 나는 IsolationLevel.Serializable과의 CurrentSessionContext = ThreadStaticSessionContext 사용) 내 헬퍼 클래스가 있습니다. 볼 수 있듯이 모든 DB 호출마다 잠금이 있으므로 _locker 객체가 정적이므로 다른 스레드에 대해서도 동시성 DB 호출을 표시 할 수 없습니다.

public abstract class BaseEntityRepository<T, TId> : IBaseEntityRepository<T, TId> where T : BaseEntity<TId> 
{ 
    private ITransaction _transaction; 
    protected static readonly object _locker = new object(); 

    public bool Save(T item) 
    { 
     bool result = false; 

     if ((item != null) && (item.IsTransient())) 
     { 
      lock (_locker) 
      { 
       try 
       { 
        _transaction = session.BeginTransaction(); 
        nHibernateHelper.CurrentSession.Save(item); 
        nHibernateHelper.Flush(); 
        _transaction.Commit();   
        result = true; 
       } catch 
       { 
        _transaction.Rollback(); 
        throw; 
       } 
       //DelayAfterProcess(); 
      } 
     } 
     return result; 
    } 

    //same for delete and update 

    public T Get(TId itemId) 
    { 
     T result = default(T); 

     lock (_locker) 
     { 
      try 
      { 
       result = nHibernateHelper.CurrentSession.Get<T>(itemId); 
      } 
      catch 
      { 
       throw; 
      } 
     } 
     return result; 
    } 

    public IList<T> Find(Expression<Func<T, bool>> predicate) 
    { 
     IList<T> result = new List<T>(); 
     lock (_locker) 
     { 
      try 
      { 
       result = nHibernateHelper.CurrentSession.Query<T>().Where(predicate).ToList(); 
      } 
      catch 
      { 
       throw; 
      } 
     } 
     return result; 
    } 


} 

나는 (내가 nHibernateHelper.OpenConnection() 스레드 당 한 번 전화) 같은 이전의 클래스를 사용합니다.

using (nHibernateHelper.OpenConnection()) 
{ 
    Foo foo = new Foo(); 
    FooRepository.Instance.Save(foo); 
}  

내가 ReadCommited에 IsolationLevel을 변경했지만,이 문제가되지 않습니다 변경 : 저장소는 singletone에 의해 인스턴스화됩니다. 이 빠른 HDD와 컴퓨터에 도움이

using (nHibernateHelper.OpenConnection()) 
{ 
    using (IDbCommand command = nHibernateHelper.CurrentSession.Connection.CreateCommand()) 
    { 
     command.CommandText = "PRAGMA journal_mode=WAL"; 
     command.ExecuteNonQuery(); 
    } 
} 

,하지만 일부에 나는 같은 오류가 발생했습니다 : 또한 나는 WAL 저널에서 변경 SQLite는 저널 모드로이 문제를 해결하기 위해 노력했다. 그럼 난 추가하려고 "DB 업데이트 파일이 존재"저장소에 확인하고, 각/삭제/업데이트를 저장 시술 후 지연 :하지 -journal 파일 근무

protected static int _delayAfterInSeconds = 1; 
    protected void DelayAfterProcess() 
    { 
     bool dbUpdateInProcess = false; 
     do 
     { 
      string fileMask = "*-wal*"; 
      string[] files = Directory.GetFiles(Directory.GetCurrentDirectory(), fileMask); 
      if ((files != null) && (files.Length > 0)) 
      { 
       dbUpdateInProcess = true; 
       Thread.Sleep(1000); 
      } 
      else 
      { 
       dbUpdateInProcess = false; 
      } 
     } while (dbUpdateInProcess); 
     if (_delayAfterInSeconds > 0) 
     { 
      Thread.Sleep(_delayAfterInSeconds * 1000); 
     } 
    } 

같은 솔루션 (DB 업데이트 파일 확인). 그것은 저널 파일이 삭제되었다고보고했으나 여전히 오류가 있습니다. -wal 파일의 경우 작동합니다 (생각하기에 테스트 할 시간이 더 필요합니다). 그러나이 해결책은 심각하게 프로그램을 제동합니다.

나를 도와 줄 수 있습니까?

답변

3

나에게 응답. .IsolationLevel (IsolationLevel. Serializable)과 관련된 문제가있었습니다. 이 줄을 .IsolationLevel (IsolationLevel. ReadCommitted)으로 변경하면 문제가 사라집니다.

0

sqlite는이 "잠금"과 유사하도록 설계되어 있으므로 이름에 라이트가 사용됩니다. 그것은 하나의 클라이언트 연결을 위해 설계되었습니다.

하지만 응용 프로그램의 여러 영역에 대해 하나 이상의 데이터베이스 파일을 사용하면 사용자베이스가 다시 커질 때까지 문제를 해결할 수 있습니다.

+0

나는 SQLite가 어떤 것인지 알고 있습니다. 그래서'lock() {}'메커니즘을 사용하여 동시 연결을 피했습니다. 내가 이해할 수있는 한 ** 모든 것은 ** 다음과 같이 작동해야한다 :'my code {lock {SQLite code}}; 내 코드 {lock {SQLite code}}; ... 'SQLite 코드 작업은 잠금 블록 내에서 완료되어야합니다. 그러나 SQLite가보고 했으니, 끝났습니다 (다시 응용 프로그램으로 제어)!그래서 응용 프로그램은 잠금 블록을 남기고 다른 블록을 시작했습니다. 그러나 SQLite는 여전히 작업을 계속합니다. SQLite는 내 응용 프로그램의 상태를 속입니다. – user809808

+0

sqlite가 준비가되었다고 생각하고 파일 시스템이 끝났다고 생각할 때 약간의 불일치가있을 수 있습니다. 파일은 결국 일정 기간 후에 잠금 해제됩니까? 또는 자물쇠에 보관 해 두었던 추가 액세스 시도가 있습니까? –

+0

약간의 시간이 지나면 - 저널 파일이 사라지는 것을 의미하는 경우 - 아니요, 그렇지 않습니다. 어떤 기간이 지나면 여전히 저널 빈 파일 (0 바이트)이 보입니다. 그리고 예, 몇 시간이 지나면 Session.BeginTransaction 호출이 실패했지만 동일한 오류가 발생했습니다. _Begin은 SQL 예외로 인해 실패했습니다. 데이터베이스 파일이 잠겼습니다. \ r \ n 데이터베이스가 잠겼습니다. – user809808

0

행을 삽입하기 전에 다른 스레드에서 데이터베이스가 사용되었는지 확인합니까?

+0

lock 코드에서 lock {} 블록으로 검사했다. 그래서 나는 DB가 다른 스레드로부터 자유 롭다고 확신했다. 그리고 예, 스레드 X에서 문제가 발생하기 전에 DB가 스레드 Y에서 사용되었는지 확인했습니다. – user809808

관련 문제