草庐IT

c# - 多线程应用程序中的 SQLite "Database is locked"错误

coder 2023-07-21 原文

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

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

有我的帮助类(如您所见,我使用 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<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();
            }
        }
    }
}

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

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())。 Repository 由单例实例化:

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 的计算机上有所帮助,但在某些计算机上我遇到了同样的错误。然后我尝试将“数据库更新文件存在”检查添加到存储库,并在每次保存/更新/删除过程后延迟:

    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.Serializable) 有关。当我将此行更改为 .IsolationLevel(IsolationLevel.ReadCommitted) 时,问题就消失了。

关于c# - 多线程应用程序中的 SQLite "Database is locked"错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8227796/

有关c# - 多线程应用程序中的 SQLite "Database is locked"错误的更多相关文章

  1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  2. ruby - 其他文件中的 Rake 任务 - 2

    我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

  3. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  4. ruby-on-rails - rails : "missing partial" when calling 'render' in RSpec test - 2

    我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou

  5. ruby-on-rails - 由于 "wkhtmltopdf",PDFKIT 显然无法正常工作 - 2

    我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-

  6. ruby-on-rails - Rails 3 中的多个路由文件 - 2

    Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

  7. ruby - 将差异补丁应用于字符串/文件 - 2

    对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl

  8. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

    我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

  9. ruby - 检查 "command"的输出应该包含 NilClass 的意外崩溃 - 2

    为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar

  10. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

随机推荐