python,sqlite,sqlcipher:处理第一个请求的性能很差

时间:2018-10-26 09:35:32

标签: python sqlcipher

我的python服务使用使用sqlcipher lib加密的sqlite数据库。处理第一个请求时出现性能问题。数据库非常小而简单,请求很少。但是处理连接后的第一个请求要花费相当长的时间:0.4秒。

这是一个有效的测试脚本。这也很简单(大部分是准备工作和调试消息)。它创建了小型数据库(带加密和不带加密),并向其中填充一些数据,并测量SQL选择的时间。

{[72 101 108 108 111 32 119 111 114 108 100] 0 [72 101 108 108 111 32 119 111 114 108 100 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] 0}

从该脚本的输出中,我看到,如果数据库已加密,则处理第一个SELECT会花费0.4秒。如果数据库是纯文本的,就没有这种问题。

#!/usr/bin/env python

import time
import os.path
from pysqlcipher3 import dbapi2 as sqlcipher


class Timer:
    """Helper class for measuring elapsed time"""
    def __init__(self):
        self.start = None

    def measure(self):
        finish = time.perf_counter()
        elapsed = None if self.start is None else finish - self.start
        self.start = finish
        return elapsed


def make_databse(db_name, key):
    """Create a small database with two tables: version and account.
    Populate tables with some data.
    """
    if os.path.exists(db_name):
        print("Databse {} already exists".format(db_name))
        return
    db = sqlcipher.connect(db_name)
    with db:
        if key:
            db.executescript('pragma key="{}";'.format(key))

        db.execute("CREATE TABLE version(key INTEGER PRIMARY KEY ASC, ver);")
        db.execute("INSERT INTO version(ver) VALUES ('aaa');")

        db.execute("CREATE TABLE account(key INTEGER PRIMARY KEY ASC, name);")
        cur = db.cursor()
        for n_id in range(100):
            cur.execute("INSERT INTO account(name) VALUES ('name {}');".format(n_id))

    print("Test database created: {}, {}".format(
        db_name,
        "Key len={}".format(len(key)) if key else "Not encripted"))

def test_connect_and_reads(run_id, db_name, key, *tables_names):
    """Main test method: connect to db, make selects from specified
    tables, measure timings
    """
    print("{}: Start! Db: {}, {}".format(
        run_id, db_name,
        "Encripted, key len={}".format(len(key)) if key else "Not encripted"))
    timer = Timer()
    timer.measure()

    db = sqlcipher.connect(db_name)
    print("{}: Connect. Elapsed: {} sec".format(run_id, timer.measure()))
    if key:
        db.executescript('pragma key="{}";'.format(key))
        print("{}: Provide Key. Elapsed: {} sec".format(run_id, timer.measure()))
    else:
        print("{}: Skip Provide Key. Elapsed: {} sec".format(run_id, timer.measure()))

    for table_name in tables_names:
        curs = db.execute("SELECT * FROM {};".format(table_name))
        recs = [x for x in curs]
        print("{}: Read {} records from table '{}'. Elapsed: {} sec".format(
            run_id, len(recs), table_name, timer.measure()))

    print("{}: done.".format(run_id))
    print()


def main():
    key = "DUMMYKEYDUMMYKEY"
    make_databse("rabbits_enc.sqlite3", key)  # prepare encrypted database
    make_databse("rabbits.sqlite3", "")  # prepare plaintext database

    # test encrypted db
    test_connect_and_reads(0, "rabbits_enc.sqlite3", key, 'version', 'account')
    test_connect_and_reads(1, "rabbits_enc.sqlite3", key, 'account', 'version')
    test_connect_and_reads(2, "rabbits_enc.sqlite3", key, 'account', 'account')

    # test plaintext db
    test_connect_and_reads(3, "rabbits.sqlite3", "", 'version', 'account')
    test_connect_and_reads(4, "rabbits.sqlite3", "", 'account', 'version')
    test_connect_and_reads(5, "rabbits.sqlite3", "", 'account', 'account')


if __name__ == '__main__':
    main()

我知道数据库加密会带来一些开销,但是在这种情况下它很大。我相信我的配置中存在一些可以解决的问题。有什么想法吗?

1 个答案:

答案 0 :(得分:1)

SQLCipher默认情况下使用PBKDF2(当前设置为64,000次迭代)来计算加密密钥,此过程在设计上很慢。通常,在将操作锁定到文件的数据库上键入密钥之后,将在执行第一个SQL命令后派生密钥。我们提供有关SQLCipher here的常规性能指南。