Python、sqlalchemy:如何提高加密sqlite数据库的性能?

Python, sqlalchemy: how to improve performance of encrypted sqlite database?

我有一个简单的服务应用程序:python、tornado 网络服务器、sqlite 数据库。数据库已加密。

问题是处理非常简单的 http 请求需要大约 300 毫秒。

从日志中我可以看出,大部分时间都花在了第一个 sql 请求的处理上,无论这个第一个请求多么简单。后续 sql 请求的处理速度要快得多。但是随后服务器开始处理下一个 http 请求,第一个 sql 请求又很慢。

如果我关闭数据库加密,问题就消失了:sql 请求的处理时间不取决于请求是否是第一个请求,我的服务器响应时间减少了 10 到 15。

不太明白是怎么回事。看起来 sqlalchemy 每次启动新会话时都会读取和解密数据库文件。有什么办法可以解决这个问题?

由于 pysqlite 或 sqlite3 模块如何工作 SQLAlchemy defaults to using a NullPool with file-based databases。这解释了为什么每个请求都会解密您的数据库:NullPool 在连接关闭时丢弃连接。这样做的原因是 pysqlite 的默认行为是不允许在多个线程中使用连接,并且在没有加密的情况下创建新连接非常快。

Pysqlite 确实有一个未记录的标志 check_same_thread 可用于禁用检查,但应谨慎处理线程之间的共享连接并且 SQLAlchemy 文档顺便提到 NullPool与 SQLite 的文件锁定配合使用效果很好。

根据您的 Web 服务器,您可以使用 SingletonThreadPool,这意味着线程中的所有连接都是相同的连接:

engine = create_engine('sqlite:///my.db',
                       poolclass=SingletonThreadPool)

如果您喜欢冒险并且您的 Web 服务器在使用时不在线程之间共享连接/会话(例如使用范围会话),那么您可以尝试使用与 check_same_thread=False 配对的不同池化策略:

engine = create_engine('sqlite:///my.db',
                       poolclass=QueuePool,
                       connect_args={'check_same_thread':False})

为了加密数据库,sqlcipher 根据我提供的密码创建了一个密钥。此操作在设计上会消耗资源。

但可以不使用密码,而是使用 256 位原始密钥。在这种情况下,sqlcipher 不必生成加密密钥。

最初我的代码是:

session.execute('PRAGMA KEY = "MY_PASSPHRASE";')

为了使用原始密钥,我将这一行更改为:

session.execute('''PRAGMA KEY = "x'<the key>'";''')

其中 <the key> 是 64 个字符长的十六进制字符串。

结果是小请求的速度提高了 20 倍以上。

仅供参考:要将数据库转换为使用新的加密密钥,应执行以下命令:

PRAGMA KEY = ""MY_PASSPHRASE";
PRAGMA REKEY = "x'<the key>'";

相关问题:

关于 sqlcipher 命令的一些信息以及密钥和原始密钥之间的区别:https://www.zetetic.net/sqlcipher/sqlcipher-api/