multithreading应用程序中SQLite“数据库已锁定”错误

有一个multithreading应用程序,适用于大型DB文件(> 600 Mb)。 当我添加blob数据时,“数据库被锁定”问题就开始了,并且每个请求开始运行> 30 Kb的BLOB数据。 我认为问题与小硬盘速度有关。 它看起来像SQLite删除-journal文件,我的应用程序的一个线程失控(因为-journal文件被应用和删除),而其他我的线程想要与数据库做smth,但SQLite仍然更新DB文件…当然,每次数据库调用后我都可以做一分钟延迟,但这不是解决方案,因为我需要更快的速度。

现在我使用每个会话(每个线程)实现的会话。 因此每个应用程序对象和许多ISession对象都有一个ISessionFactory。

有我的助手类(你可以看到我使用IsolationLevel.Serializable和CurrentSessionContext = ThreadStaticSessionContext):

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()).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(); } } } } 

还有存储库帮助程序类。 正如您所看到的,每次调用DB都会出现锁定,因此对于不同的线程也不会出现并发DB调用,因为_locker对象是静态的。

 public abstract class BaseEntityRepository : IBaseEntityRepository where T : BaseEntity { 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(itemId); } catch { throw; } } return result; } public IList Find(Expression<Func> predicate) { IList result = new List(); lock (_locker) { try { result = nHibernateHelper.CurrentSession.Query().Where(predicate).ToList(); } catch { throw; } } return result; } } 

我使用像这样的前面的类(我每个线程调用一次nHibernateHelper.OpenConnection())。 存储库由单线实例化实例化:

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

我试图将IsolationLevel更改为ReadCommited,但这不会改变问题。 此外,我试图通过将SQLite日志模式从日志更改为WAL来解决此问题:

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

这有助于具有快速HDD的计算机,但在一些我得到相同的错误。 然后我尝试将“DB update file exists”检查添加到存储库,并在每次保存/更新/删除过程后延迟:

  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); } } 

相同的解决方案(检查数据库更新文件)不适用于-journal文件。 据报道,-journal文件已被删除,但我仍然有错误。 对于-wal文件它可以工作(我认为。我需要更多时间来测试它)。 但这个解决方案严重制动程序。

也许你可以帮助我?

回答自己。 问题与.IsolationLevel(IsolationLevel.Cerializable)有关。 当我将此行更改为.IsolationLevel(IsolationLevel.ReadCommitted)时,问题就消失了。

sqlite被设计成像这样“锁定”,因此名称中的精简版。 它仅适用于一个客户端连接。

但是,您可以为应用程序的不同区域使用多个数据库文件,这可能会阻止问题,直到您的用户库再次增长。

在插入行之前,您是否检查数据库是否已在另一个线程上使用?