Flask使用peewee操作SQLite在生产环境出现database is locked报错如何处理
问题成因
- SQLite本身是文件型数据库,采用全局文件级排他锁处理写操作,同一时间仅允许一个连接执行写入操作,并发场景下多个写请求争抢锁时就会触发
database is locked报错。 - 你的代码存在明显的不合理设计:
before_request钩子中每次请求都会执行db.create_tables操作,哪怕参数safe=True不会重复建表,也会触发SQLite的系统表查询甚至写操作,相当于每次请求都额外增加了一次锁争抢逻辑,大幅提升了锁冲突概率。 - 连接管理逻辑存在漏洞:你使用
after_request钩子关闭连接,该钩子仅在请求无异常抛出时才会执行,一旦请求处理过程中出现异常,数据库连接不会被正常关闭,持有的锁也无法释放,进一步加剧锁占用问题。 - 若生产环境使用多线程WSGI服务器,未配置合理的数据库连接池、锁超时时间过短,也会放大锁冲突的概率。
解决方法
1. 调整建表逻辑
将create_tables操作从请求钩子中移除,仅在应用启动时执行一次:
# 应用初始化阶段执行,仅执行一次 if __name__ == '__main__': db.connect() db.create_tables([Post, User], safe=True) db.close() app.run() # 生产环境用WSGI服务器启动的话,放在应用实例化后的初始化逻辑里即可
2. 修复连接管理逻辑
将释放连接的逻辑改为使用teardown_request钩子,该钩子无论请求是否抛出异常都会执行,避免连接泄漏:
@app.before_request def before_request(): db.connect() @app.teardown_request def teardown_request(exc): if not db.is_closed(): db.close()
3. 优化数据库配置
替换默认的SQLite数据库实例为peewee提供的SqliteQueueDatabase,自带连接队列管理,适配多线程并发场景,同时设置合理的锁超时时间、开启WAL模式提升并发性能:
from peewee import SqliteQueueDatabase db = SqliteQueueDatabase( 'your_database.db', timeout=30, # 锁等待超时时间,单位秒,可根据业务调整 pragmas={ 'journal_mode': 'wal', # 开启WAL模式,支持并发读+单写,大幅提升并发性能 'cache_size': -1024 * 64, # 设置页缓存大小 } )
4. 业务层优化
- 尽量缩短写事务的执行时长,不要在事务中执行HTTP请求、文件读写等耗时操作,减少锁的持有时间。
- 高频读场景可增加缓存层,减少直接访问数据库的请求量。
5. 长期演进方案
如果业务并发量持续上涨,建议替换为MySQL、PostgreSQL等支持行级锁的关系型数据库,SQLite本身的设计定位就是低并发、轻量的使用场景,不适合高并发写入的生产服务。
内容的提问来源于stack exchange,提问作者Jeeva
相关产品推荐
相关产品推荐

