博客
关于我
records库使用sqlite数据库Cannot operate on a closed database错误解决
阅读量:686 次
发布时间:2019-03-17

本文共 3796 字,大约阅读时间需要 12 分钟。

使用 records 库与 SQLite 数据库交互时错误分析及解决方案

作为开发人员,在使用 Python 的 records 库 进行数据库操作时,偶尔会遇到一些棘手的错误。最近在对 SQLite 数据库进行测试时,就出现了一个令人困惑的 ProgrammingError:“sqlalchemy.exc.ProgrammingError: (sqlite3.ProgrammingError) Cannot operate on a closed database.”

错误现象分析

让我们首先看看产生这个错误的代码示例:

import recordsdb = records.Database('sqlite:///testdb.db')rows = db.query('select * from users')print(rows.dataset)

只要上述代码运行,就会出现以下错误信息:

sqlalchemy.exc.ProgrammingError: (sqlite3.ProgrammingError) Cannot operate on a closed database.

分析原因

通过逐步排查,可以发现问题的根源在于 Connection 对象的资源管理机制。具体来说:

  • Database 类的初始化:在 __init__ 方法中,Database 对象会通过 create_engine 方法创建 SQLite 连接,并将 open 属性设置为 True。这意味着每次创建一个 Database 实例时,都会自动打开相应的数据库连接。

  • Query 操作的执行:当调用 db.query('select * from users') 时,Database 实例会调用 get_connection() 方法,返回一个 Connection 对象。接下来,Connection 对象执行实际的查询操作。

  • Connection 对象的生命周期:根据 SQLite 数据库的操作习�itude,Connection 对象需要在使用之后进行手动关闭,或者依赖上下文管理(with 语句)来自动关闭连接。Connection 类实现了 __enter____exit__ 方法,当使用 with 语句时,__exit__ 方法会自动关闭 connection,释放数据库资源。

  • 然而,问题出现在了当 Database 实例的 query() 方法中自动获取连接时。每次调用 query() 会创建一个新的 Connection 对象,而这些 Connection 对象会在执行完查询任务之后自动关闭。此时,任何对数据库的后续操作都会因为数据库已经关闭而失败。

    这种自动化的资源管理机制在本地数据库中是非常有用,但同时也带来了一个关键问题:每次查询都会导致数据库连接的打开和关闭。这种频繁的连接切换可能会对数据库性能产生负面影响,尤其是在处理大量数据或复杂查询的情况下。

    另外,如果在代码中使用了 with 语句来管理 Connection 对象,则会更加明显地看到这种自动化关闭的影响。例如:

    with db.get_connection() as conn:    rows = conn.query('select * from users')

    在上述代码中,__exit__ 方法会自动在 with 块的执行完毕后调用,导致 conn 被关闭。这意味着 Database 实例在后续操作中将无法再正常工作,因为数据库已经断开。

    错误的根本原因

    通过代码调试,可以切 waterproof 发现错误的具体根源:

    • db.query() 方法内部实际上是通过 conn.query() 进行操作的。
    • conn.query() 方法在执行完毕后,会自动调用 conn.close(),从而导致数据库关闭。
    • 这种行为意味着,每次完成一次查询操作,数据库都会被自动关闭,与后续的查询操作产生了竞争,导致无法正常执行。

    这种做法看起来有点奇怪,因为 records 库通常被设计为高级别抽象层,应该能够更好地管理数据库的连接。但在这个实现中,连接的生命周期管理显得不够完善。

    解决方法

    现在,我们来看如何修复这个错误,并确保代码能够正常运行。

    解决方法很简单:我们需要避免让 Connection 对象在查询之后自动关闭。为此,可以手动管理 Connection 对象,或者在不使用 with 语句的情况下,直接持有数据库连接并在需要时关闭它。

    修改后的代码示例

    import recordsdb = records.Database('sqlite:///testdb.db')conn = db.get_connection()rows = conn.query('select * from users')print(rows.dataset)

    在上述代码中,conn 是手动获取的 Connection 对象。程序的执行过程如下:

  • 创建 db 实例,并使用它来获取一个 conn 对象。
  • 使用 conn 对象执行查询操作。
  • 在查询执行完毕后,手动关闭 conn,或者在程序结束前根据需要进行其他操作。
  • 这种方式确保了 conn 一直保持打开状态,避免了每次查询后自动关闭带来的问题。

    扩展的优化建议

    为了进一步优化代码,可以考虑实现一些更高级的资源管理策略,例如:

  • 手动关闭连接:在每个查询完成之后,手动调用 conn.close()

    import recordsdb = records.Database('sqlite:///testdb.db')conn = db.get_connection()rows = conn.query('select * from users')conn.close()print(rows.dataset)
  • 使用 Batch 批处理:如果需要执行多个查询,可以将它们合并到一个批处理操作中。

    import recordsdb = records.Database('sqlite:///testdb.db')conn = db.get_connection()rows = conn.query('select * from users; select name from users')print(rows.dataset)
  • 重复使用数据库连接:为了减少数据库句柄的频繁打开和关闭,可以将连接持有到需要使用的地方。

    例如:

    import recordsdb = records.Database('sqlite:///testdb.db')conn = db.get_connection()rows = conn.query('select * from users')print(rows.dataset)
  • 使用多线程/多进程:如果需要并行处理多个数据库操作,可以通过线程池或进程池实现负载分担。

    例如:

    from concurrent.futures import ThreadPoolExecutorimport recordsdb = records.Database('sqlite:///testdb.db')with db.get_connection() as conn:    conn.execute('DELETE FROM users WHERE id = 1')
  • 设置数据库连接池:对于需要大量并发访问数据库的应用,可以配置一个连接池,来缓存和管理数据库连接。

  • 优化查询:检查和优化数据库查询,看看是否有冗余的查询或可以改进的地方。

    例如:

    • 避免不必要的大量数据获取。
    • 使用索引优化或分页技术来限制数据量。
    • 使用更高效的查询方式(如使用IN子句而不是多次查询)。
  • 这样一来,可以显著提升数据库操作的性能和稳定性。

    建议的最佳实践

    • 正确管理数据库连接:手动创建并使用数据库连接对象,而不是让记录库自动管理。
    • 确保连接在使用后被正确关闭:在每个上下文中都保证 conn.close() 被调用。
    • 合理利用连接池:通过连接池避免频繁创建和关闭数据库连接。
    • 优化查询:通过分析数据库查询,优化查询语句和执行方式,以提升性能。
    • 使用事务管理:在需要更高级别的数据一致性时,使用事务和锁来保证数据的完整性。
    • 监控数据库性能:使用数据库监控工具和 profiler 工具,跟踪数据库的性能指标,及时发现潜在的瓶颈和性能问题。

    通过遵循以上建议,可以有效地解决数据库连接问题,并优化数据库操作的整体表现。

    结论

    综上所述,本次错误的核心原因在于 Connection 对象在查询时自动关闭数据库连接,这在某些场景下会导致后续操作无法正常执行。为了避免类似问题,可以通过手动管理 Connection 对象,确保数据库连接的持有和关闭。通过正确的资源管理策略,可以显著提升数据库操作的稳定性和性能。

    此外,熟悉数据库的连接机制和 records 库 的实现细节,可以帮助开发人员更好地理解问题根源,并在遇到类似情况时快速找到解决方案。数据库开发是一个综合性的工作,需要不断学习和实践,才能真正做到写好、用好、维好。

    转载地址:http://pbthz.baihongyu.com/

    你可能感兴趣的文章
    Mura CMS processAsyncObject SQL注入漏洞复现(CVE-2024-32640)
    查看>>
    Mysql DBA 高级运维学习之路-DQL语句之select知识讲解
    查看>>
    mysql deadlock found when trying to get lock暴力解决
    查看>>
    MuseTalk如何生成高质量视频(使用技巧)
    查看>>
    mutiplemap 总结
    查看>>
    MySQL DELETE 表别名问题
    查看>>
    MySQL Error Handling in Stored Procedures---转载
    查看>>
    MVC 区域功能
    查看>>
    MySQL FEDERATED 提示
    查看>>
    mysql generic安装_MySQL 5.6 Generic Binary安装与配置_MySQL
    查看>>
    Mysql group by
    查看>>
    MySQL I 有福啦,窗口函数大大提高了取数的效率!
    查看>>
    mysql id自动增长 初始值 Mysql重置auto_increment初始值
    查看>>
    MySQL in 太多过慢的 3 种解决方案
    查看>>
    MySQL InnoDB 三大文件日志,看完秒懂
    查看>>
    Mysql InnoDB 数据更新导致锁表
    查看>>
    Mysql Innodb 锁机制
    查看>>
    MySQL InnoDB中意向锁的作用及原理探
    查看>>
    MySQL InnoDB事务隔离级别与锁机制深入解析
    查看>>
    Mysql InnoDB存储引擎 —— 数据页
    查看>>