问题描述
我修改我的DatabaseHelper类使用SQLCipher库。
I've modified my DatabaseHelper class to use the SQLCipher library.
要做到这一点,我:
-
复制的资产注入我的资产文件夹和库(armeabi,X86,commons- codeC,番石榴-R09,sqlcipher)到我的libs文件夹。
Copied the assets into my assets folder and the libraries (armeabi, x86, commons-codec, guava-r09, sqlcipher) into my libs folder.
改变了进口在我的 DatabaseHelper
类,以便它们指向进口net.sqlcipher.database。*
代替。
Changed the imports in my DatabaseHelper
class so that they point to import net.sqlcipher.database.*
instead.
呼叫 SQLiteDatabase.loadLibs(getApplicationContext());
时,应用程序启动
修改,我叫行 getReadableDatabase()
和 getWriteableDatabase()
,以便他们包括密码作为参数;
Modified the lines where I call getReadableDatabase()
and getWriteableDatabase()
so that they include a passphrase as a parameter;
一切似乎都在将数据读/写正常地工作。我的问题是有关的性能,因为我的应用程序可能会以一定的频率执行数据库操作,使它变得缓慢(迁移到SQLCipher后)。
Everything seems to work fine as data is read/written properly. My issue is related to performance, as my app may execute DB operations with some frequency, causing it to become slow (after migrating to SQLCipher).
有关我DatabaseHelper方法,我相信我以下的标准方法,例如:
For my DatabaseHelper methods, I believe I'm following the standard approach, e.g.:
/*
* Getting all MyObjects
*/
public List<MyObject> getMyObjects() {
List<MyObject> objects = new ArrayList<MyObject>();
String selectQuery = "SELECT * FROM " + TABLE_NAME;
Log.v(LOG, selectQuery);
// Open
SQLiteDatabase db = this.getReadableDatabase("...the password...");
// I know this passphrase can be figured out by decompiling.
// Cursor with query
Cursor c = db.rawQuery(selectQuery, null);
// looping through all rows and adding to list
if (c.moveToFirst()) {
do {
MyObject object = createMyObjectFromCursor(c); // Method that builds MyObject from Cursor data
// adding to list
objects.add(object);
} while (c.moveToNext());
}
c.close();
db.close();
return objects;
}
我不完全熟悉SQLCipher的内部机制(比如它解密整个数据库文件时,我称之为 getReadableDatabase()
?),但是,在调试时,似乎开销是在 getReadableDatabase(密码)
和 getWritableDatabase(密码)
,如果我的上述推测这是有道理的是真的。
I'm not entirely familiar with the internal mechanics of SQLCipher (e.g. does it decrypt the whole DB file when I call getReadableDatabase()
?) but, while debugging, it seems that the overhead is in getReadableDatabase(password)
and getWritableDatabase(password)
, which makes sense if my supposition above is true.
应该移动到DatabaseHelper.open()和DatabaseHelper.close这些电话()方法,将由活动时,他们实例化一个DatabaseHelper被称为与其说他们每个人的方法,是一种不好的做法?请分享你对如何解决这一问题的知识。
Would moving those calls to a DatabaseHelper.open() and DatabaseHelper.close() method which would be called by the Activities whenever they instantiate a DatabaseHelper, instead of calling them on each individual method, be a bad practice? Please share your knowledge on how to address this issue.
编辑:
我用DDMS跟踪的方法之一,我可以看到的开销确实在 SQLiteOpenHelper.getReadableDatabase()
(取〜4秒。每个时间)。该查询似乎工作快,我不认为我需要担心他们。
I've used DDMS to trace one of the methods and I can see that the overhead is indeed at the SQLiteOpenHelper.getReadableDatabase()
(taking ~4 sec. each time). The queries seem to work fast and I don't think I need to worry about them.
如果我钻到电话,下面一个与每一次持续时间最长,我结束了:
If I drill down the calls, following the one with the longest duration every time, I end up with:
SQLiteDatabase.OpenOrCreateDatabase
- > SqLiteDatabase.openDatabase
- > SQLiteDatabase.openDatabase
- > SQLiteDatabase.setLocale
SQLiteDatabase.OpenOrCreateDatabase
--> SqLiteDatabase.openDatabase
--> SQLiteDatabase.openDatabase
--> SQLiteDatabase.setLocale
所以 SQLiteDatabase.setLocale(java.util.Locale中)
似乎是罪魁祸首,因为它正在〜4秒每次getReadableDatabase()被调用。我看了成源SQLiteDatabase,它只是锁定数据库,调用 native_setLocale(locale.toString(),mFlags)
(4秒开销发生在这里)和解锁DB。
So the SQLiteDatabase.setLocale(java.util.Locale)
seems to be the culprit, as it is taking ~4 seconds everytime getReadableDatabase() is called. I've looked into the source for SQLiteDatabase and it just locks the DB, calls native_setLocale(locale.toString(), mFlags)
(the 4 sec. overhead takes place here) and unlocks the DB.
这是为什么发生这种情况的任何想法?
Any idea on why this happens?
推荐答案
您所看到的性能问题是最有可能是由于SQLCipher密钥派生。 SQLCipher对打开数据库性能故意慢,使用PBKDF2执行密钥派生(即数千SHA1操作),以抵御暴力破解和字典攻击(可以在)。这项活动被推迟到第一次使用该数据库,这发生在的setLocale发生,这就是为什么你在分析的时候看到了性能问题那里。
The performance issue you are seeing is most likely due to SQLCipher key derivation. SQLCipher's performance for opening a database is deliberately slow, using PBKDF2 to perform key derivation (i.e. thousands of SHA1 operations) to defend against brute force and dictionary attacks (you can read more about this at http://sqlcipher.net/design). This activity is deferred until the first use of the database, which happens to occur in setLocale, which is why you are seeing the performance issue there when profiling.
最好的选择是缓存数据库连接,以便它可以被多次使用而不必数据库反复打开和密钥。如果这是可能的,在启动过程中一旦打开数据库是pferred行动的过程中,$ P $。在同一个数据库句柄的后续访问将不会触发密钥派生,所以性能会快很多。
The best option is to cache the database connection so that it can be used multiple times without having to open and key the database repeatedly. If this is possible, opening the database once during startup is the preferred course of action. Subsequent access on the same database handle will not trigger key derivation, so performance will be much faster.
如果这是不可能的另一种选择是禁用或削弱密钥推导。这将导致SQLCipher来派生密钥时使用较少的轮PBKDF2的。虽然这使数据库打开速度更快,这是从安全角度显著减弱。因此,不建议除了在特殊情况下。这就是说,这里是如何减少KDF迭代的信息:
If this is not possible the other option is to disable or weaken key derivation. This will cause SQLCipher to use fewer rounds of PBKDF2 when deriving the key. While this will make the database open faster, it is significantly weaker from a security perspective. Thus it is not recommended except in exceptional cases. That said, here is the information on how to reduce the KDF iterations:
这篇关于SQLCipher为Android getReadableDatabase()Overherad的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!